Parameterized Queries
Parameterized queries separate SQL structure from data by sending the statement and values in distinct protocol channels, so user input can never change the parsed statement.
Definition
A parameterized query transmits the SQL statement with placeholders and the parameter values as typed data in a separate binding step. The database parses the statement once with a fixed structure; values are never interpreted as SQL tokens. This is the structural defense against SQL injection, unlike escaping, which tries to sanitize a combined string.
Context and scope
Standard for every database call path: prepared statements in drivers, ORM query builders, and migration scripts. Pair with least-privilege database accounts for defense in depth.
Common misconceptions
- Escaping input is equally safe — escaping is dialect-specific and fragile; binding removes the interpretation problem entirely
- ORMs are automatically safe — string concatenation into ORM filters, raw fragments, and dynamic ORDER BY clauses reintroduce injection
- Parameters slow queries down — prepared plans are often faster on repeat; the parse-then-bind protocol is not the bottleneck
Anti-patterns
- Concatenating user input into ORDER BY or table names because placeholders cannot express identifiers
- Building query strings with template literals in logging-fallback code paths that bypass the data access layer
- Allowing client-supplied column names through a whitelist that is itself built by string interpolation
Related concepts
Related examples
Authoritative source
https://cheatsheetseries.owasp.org/cheatsheets/SQL_Injection_Prevention_Cheat_Sheet.html
What it is
Injection exists because code and data share one channel. Parameterization splits them: the statement arrives as a complete, parseable unit; each value arrives as data bound to a placeholder. No string formed from user input is ever parsed as SQL.
How it works
The driver sends Prepare followed by Execute with typed parameters, or the equivalent wire format. The planner sees a fixed tree and optimizes once; values flow into execution. Identifiers — table and column names — cannot be parameterized, so they must come from application-controlled whitelists, never interpolated.
When to use it
Everywhere user-influenced values touch SQL: filters, pagination offsets, search terms, multi-tenant scopes. ORMs parameterize by default; audit raw fragments and dynamic sorts where injection hides.
Pitfalls
The residual risks are structural: dynamic identifiers,二次 order injection from stored data, and driver-specific quirks like allowMultiQueries. Test with payloads in unit and integration suites, and give application accounts minimal grants so a hypothetical injection has minimal reach.