Teams often assume prepared statements cover every part of a query, but they only protect data literals. When the query includes dynamic identifiers, such as an ORDER BY field or other syntax elements, those values still need strict allowlisting. Without that second control, attackers can reshape query behaviour or chain additional database operations.
Where the allowlist boundary actually sits
The common mistake is treating “parameterized query” as if it protects the whole statement. It protects values, not structure. In Lua database code, the risky part is usually the SQL syntax that is assembled from user input, such as sort columns, sort direction, selected table names, or fragment choices. Those pieces must be constrained to a known safe set before the query is built.
That distinction matters because SQL parsers do not treat identifiers and literals the same way. If a team allowlists only the obvious value fields but leaves structural tokens unconstrained, an attacker can still redirect the query plan, change which rows are returned, or create injection-like behavior through a “safe” field name. The control has to cover the syntax boundary that remains outside the prepared statement.
- Allowlist column names, direction tokens, table aliases, and optional clauses separately.
- Map each allowed input to a hard-coded SQL fragment rather than concatenating raw user text.
- Treat any value that changes query shape as code, not data.
Why Lua makes this easy to misjudge
Lua often encourages compact string-building patterns, and that makes the unsafe pattern look deceptively clean. A team may test with ordinary inputs, see that values are escaped, and conclude the query is safe. The failure appears later when a user-controlled field is used to select a sort order, dynamic identifier, or conditional branch that was never passed through the same validation path.
The operational issue is not just classic SQL injection. Even when the database driver supports parameter binding, some query elements cannot be bound as parameters at all. That means the team must split the problem into two controls: bind data literals wherever the driver allows it, and enforce strict allowlisting for every non-literal fragment. If those two controls are mixed together, the protection model becomes inconsistent and brittle.
For practical verification, review the code path that constructs the final SQL string, not just the call that executes it. If user input can alter the final token sequence, the query is still externally influenced. In broader identity and secrets terms, the same pattern is why teams should keep query-construction privileges narrow and avoid letting application logic improvise SQL syntax from uncontrolled input; NHI guidance on dynamic versus static secrets also reinforces that long-lived, reusable control points become dangerous when they are too permissive, and the underlying lifecycle issues are visible in Ultimate Guide to NHIs, Static vs Dynamic Secrets.
Standards & Framework Alignment
This section maps relevant standards and security frameworks to the operational risks and controls described in this guidance.
OWASP Agentic AI Top 10 address the attack and risk surface, while CIS Controls v8 and NIST CSF 2.0 set the governance and control requirements practitioners need to meet.
| Framework | Control / Reference | Relevance |
|---|---|---|
| CIS Controls v8 | CIS 16 — Application Software Security | Lua query construction is application logic that needs secure input handling. |
| Recommendation — Validate and constrain all user-controlled SQL fragments before assembling queries. | ||
| NIST CSF 2.0 | PR.DS — Data Security | Dynamic SQL input control protects data handling from being reshaped by untrusted input. |
| PR.AC — Identity Management, Authentication and Access Control | Query-shaping input can become an access path to broader data exposure or action. | |
| Recommendation — Protect data flows so user input cannot alter query behavior. Restrict query-construction privileges and enforce least privilege in the application. | ||
| OWASP Agentic AI Top 10 | A1 — Prompt Injection | User-controlled syntax that changes execution behavior is structurally similar to injection abuse. |
| A2 — Tool Misuse | Dynamic SQL fragments can redirect what the application is authorized to do. | |
| Recommendation — Treat externally supplied control tokens as untrusted and gate them through allowlists. Bound any input that changes execution paths to predefined safe actions. | ||
Practitioner Guidance
What to verify: Verify that every user-influenced SQL fragment is reduced to a known allowlisted token before string concatenation. If the fragment is not a literal value, assume it needs a separate validation rule and a separate test case.
Common mistake: Do not rely on escaping or prepared statements as a blanket answer. If the query can still change shape through ORDER BY, LIMIT, table choice, or clause selection, the dangerous part has not been controlled yet.
What good looks like: The application exposes a small, explicit set of allowed query shapes, with each dynamic branch mapped to predefined SQL text. Unsafe inputs fail closed rather than being “normalized” into a nearby acceptable value.
Practitioner takeaway: The control objective is not “can we bind parameters?” but “can untrusted input still influence SQL structure?” If yes, the allowlist is incomplete.
Further reading: The general pattern of separating data handling from syntax handling is consistent with the OWASP Cheat Sheet Series and the query-shape discipline described in the OWASP API Security Top 10.
Related resources from NHI Mgmt Group
Deepen Your Knowledge
Reviewed and updated by the NHIMG editorial team on September 17, 2026.
NHI Mgmt Group — the #1 independent authority on Non-Human Identity, IAM, and Agentic AI security. nhimg.org