SQL injection, from first principles
SQL injection has been on the OWASP Top 10 for two decades, and it keeps showing up for one simple reason: it happens whenever untrusted input gets concatenated into a query instead of being kept separate from it. The payload trivia is downstream of that one idea.
The core problem
A query built like this treats user input as part of the program rather than as data:
query = "SELECT * FROM users WHERE username = '" + input + "'"
If input can contain a quote character, it can close
the string literal early and inject its own SQL. Everything else โ
UNION-based extraction, boolean/time-based blind
techniques, second-order injection โ is just different ways of
exploiting that same trust boundary failure.
Why it's still common
- ORMs make it easy to fall back to raw string queries "just this once".
- Legacy code paths that predate an org's secure coding standards.
- Search/filter/sort parameters that feel like "just a string", not user-controlled SQL.
The fix, in order of preference
- Parameterized queries / prepared statements โ the interpreter keeps code and data separate; this closes the class of bug entirely.
- ORM query builders used as intended, without dropping to raw SQL string interpolation.
- Strict allow-listing for the rare cases where identifiers (table/column names) must be dynamic, since those can't be parameterized.
- Least-privilege DB accounts and input validation as defense in depth โ not a substitute for the above.
Test only systems you own or are explicitly authorized to test โ e.g. your own lab, or a program with written scope.← back to posts