Skip to main content

SQL Injection

Keep untrusted values separate from SQL structure with parameterized queries, allowlisted identifiers, and narrow database authority.

Query structure and data

SQL injection occurs when attacker-controlled data changes the structure of a database statement. The injected syntax can alter a predicate, add a second statement, select other records, change data, call a database function, or reach an operating-system feature exposed by the database.

The defect usually appears when code builds SQL with string concatenation, interpolation, or an unsafe query builder. It can also appear in stored procedures that create dynamic SQL.

Parameterized queries

Send the SQL statement and values through an interface that binds parameters without reparsing values as SQL. Use the database driver's native parameter mechanism. Do not quote or escape a value and then append it to the query string.

Parameters represent values. They usually cannot represent identifiers, keywords, operators, or sort directions. Select those structural elements from a fixed server-side allowlist. If a feature requires arbitrary user-defined queries, isolate it as a separate high-risk capability with its own parser, authority, resource limits, and audit path.

Authority and data boundaries

Use a database principal with only the operations and objects required by the service. Separate migration authority from runtime authority. Avoid a shared owner account across services. Apply row, tenant, and object authorization in the application or an appropriate database policy. Parameterization prevents injection; it does not prove that the requested record is authorized.

Do not reveal raw database errors, schema names, query text with secrets, or connection details to the caller. Preserve enough internal evidence to investigate the failed operation without logging credentials or sensitive query values.

Failure and residual risk

An object-relational mapper can still expose raw-query functions. A parameterized value cannot protect a concatenated table or order field. A read-only database account can still disclose all readable data. Blind injection can succeed without a visible database error. A stored payload can become dangerous when a later job builds a new query from it.

Pomerium boundary

Pomerium can require an authenticated route and broad application permission before a caller reaches the service. It cannot parameterize the application's database statements or decide which rows the caller may read. The application must keep SQL structure separate, use narrow database credentials, and enforce object authorization.

Evaluation checklist

  • Do all database statements bind untrusted values through the native parameter API?
  • Are table, column, operator, and sort choices selected from server-side allowlists?
  • Can any stored procedure, report builder, migration tool, or administrative endpoint create dynamic SQL?
  • Does the runtime database principal have only the required operations and objects?
  • Do tests cover error-based, boolean, time, second-order, and alternate-encoding behavior?
  • Does the application enforce tenant, object, and action authorization separately?

Sources and further reading

Keep learning

Software and Application Security

Injection

Prevent attacker-controlled data from changing the structure or meaning of a command sent to an interpreter.

Learn this term

Get a Personalized Demo

Schedule a Call with a Pomerium Engineer

Get a Demo