Course outline

Secure Software Development / Intermediate: implementation boundaries and verification / Lesson 8

Keep data separate from interpreter instructions

Use structured interfaces and fixed mappings instead of constructing executable syntax from values.

About 4 min readContent reviewed
On this page

Lesson goals

  • Distinguish value parameters from structural identifiers.
  • Review argument semantics even when a shell is absent.

Name the interpreter at the destination

Data becomes dangerous when a later component interprets it as instructions in an unintended context. SQL, shell commands, templates, HTML, and expression languages have different grammars. A single generic escaping function cannot establish safety for all of them. Start by asking whether an interpreter is necessary at all. A library operation or structured API often avoids constructing syntax. Then identify which parts are fixed program structure and which parts are external values. This design question is more reliable than searching for a universal list of suspicious characters.

Bind values and map structural choices

Parameterized database APIs separate values from SQL structure when used correctly. They do not generally make arbitrary table names or sort directions into safe value parameters. Map supported structural choices to fixed application-owned fragments, then bind ordinary values with the actual driverโ€™s supported interface. The example constructs a query plan for inspection only and never connects to a database. Its question-mark placeholders reflect a fictional selected adapter style; other drivers use different syntax. A passing string assertion cannot prove that a real driver binds values or enforces least privilege.

python Inspect a nonexecuting structured query plan
orders = {"title": "title ASC, id ASC", "newest": "created_at DESC, id DESC"}
def query_plan(category, sort_key):
    if sort_key not in orders:
        return "UNSUPPORTED_SORT"
    return ("SELECT id, title FROM workshops WHERE category = ? ORDER BY " + orders[sort_key], (category,))

statement, values = query_plan("art", "title")
assert values == ("art",)
assert statement.endswith("ORDER BY title ASC, id ASC")
assert query_plan("art", "other") == "UNSUPPORTED_SORT"
print("query structure and values separated")
Expected output
query structure and values separated

The complete local program creates strings and tuples only. It executes no SQL and uses no driver or database. Actual parameter binding, identifier quoting, permissions, and query behavior remain unverified integration boundaries.

Remember argument semantics beyond shell parsing

A structured argument list avoids shell parsing when used with an appropriate process API, but the invoked program still interprets its own options and file names. An untrusted value beginning with an option prefix can have meaning to that program. Prefer avoiding process creation entirely; otherwise review the exact executable, allowed options, working directory, environment, and value semantics. The second example maps a requested report format to a library-level renderer identifier. It does not run a command. This demonstrates reducing interpretation authority rather than claiming that an argument list alone solves every command-related risk.

python Map a format to a narrow library capability
renderers = {"json":"library-json", "csv":"library-csv"}
def renderer_for(requested):
    return renderers.get(requested, "UNSUPPORTED_FORMAT")

assert renderer_for("json") == "library-json"
assert renderer_for("csv") == "library-csv"
assert renderer_for("--help") == "UNSUPPORTED_FORMAT"
print("report capability mapping checked")
Expected output
report capability mapping checked

The complete program checks three local mapping decisions. The option-like string is inert classroom data. No shell, subprocess, renderer, file, or external tool is invoked, and CSV spreadsheet interpretation is not addressed by this mapping.

Review delayed interpretation too

A value can enter safely through a parameterized write and later become unsafe when another path concatenates it into syntax. Trace stored workshop titles and report fields through exports, templates, logs, and background processing. Validation at entry does not remove the obligation to use a safe destination interface. Keep context-specific output encoding close to the point of interpretation, and avoid storing pre-escaped text as if one encoding suits every use. Review helpers that accept raw fragments because they can bypass the structured interface and reintroduce instruction construction.

Verify the interface actually used

Code review should inspect the final call that crosses the interpreter boundary, not only the helper that prepares a value. In an approved isolated component test, exercise the real adapter with benign punctuation and exact expected data behavior. Do not infer driver behavior from the local plan example. Check legitimate international text and supported sort choices as well as rejection cases. A repair should preserve useful functionality while eliminating the unintended instruction path. Report which engine and driver assumptions were verified and which remain illustrative; this course executes neither commands nor databases.

Common errors and fixes

A sort direction is passed as an ordinary SQL value

Why it happens
Structural syntax is confused with data parameters.
How to fix it
Map supported choices to fixed reviewed fragments.
Prevent it next time
Review identifiers and values separately in query builders.

No shell is treated as no argument risk

Why it happens
The target program still interprets options.
How to fix it
Prefer a library or constrain the exact program interface.
Prevent it next time
Review executable and argument semantics together.

Entry validation is assumed to protect every later use

Why it happens
Stored data crosses another interpreter boundary.
How to fix it
Use a safe destination interface at each interpretation point.
Prevent it next time
Trace delayed exports and background paths.

Check your understanding

1. Can a parameterized query safely accept any client-provided ORDER BY fragment?

Show worked solution

No. Value parameters do not authorize arbitrary SQL structure. Select a supported sort name and map it to an application-owned fragment, then bind values through the actual driver. Verify that adapter separately; the offline query-plan example does not execute SQL.

2. A report format is selected from json and csv. What additional CSV question remains?

Show worked solution

The mapping limits the renderer capability, but a spreadsheet may interpret cell content under its own rules. Review the intended consumer and safe CSV export policy separately. Choosing a structured library does not automatically establish safe behavior in every downstream application that opens the result.

Key takeaways

  • Separate fixed syntax from external values.
  • Structured interfaces still require semantic review.
  • Delayed interpretation creates new trust boundaries.

Official references