Anatomy of an Injection
Every injection has the same three moves: break out of the data context, inject your SQL, and fix up the rest of the query so it still parses. This chapter looks at how each move works — and the first question an attacker (or tester) asks is: what context is my input in?
The First Question: String or Numeric Context?
Where the input lands in the query determines how you break out of it — exactly like the injection contexts from the XSS course, but for SQL. The two main cases:
| Context | Query looks like | To break out you need… |
|---|---|---|
| String | WHERE name = 'INPUT' | a quote ' to close the string first |
| Numeric | WHERE id = INPUT | no quote — inject directly |
id=5 OR 1=1 works directly. And in string context, quote-stripping/escaping is fragile (encodings, multi-byte tricks, and it breaks legitimate data like O'Brien). The point: you cannot reliably defend by manipulating quotes — only by parameterizing (Chapter 7), which removes the question of context entirely.
Probing: Does It Even Inject?
Before crafting an attack, a tester confirms a parameter is injectable. The classic probes:
- A lone quote
'— if it causes a database error or changed behaviour, the input is reaching the query unescaped (string context). - Always-true vs always-false —
' OR '1'='1(more results) vs' AND '1'='2(no results). A difference proves your input is altering the query's logic. - Arithmetic — in numeric context,
id=5vsid=4+1: if both return the same record, the database evaluated4+1— it's numeric and injectable.
Comments: Cutting Off the Rest of the Query
After injecting, the developer's original query continues — often with a trailing quote or an AND password='...' that would break your syntax. The fix-up move is a SQL comment, which tells the database to ignore everything after it:
| Comment | Syntax | Database |
|---|---|---|
| Double-dash | -- (note the trailing space) | most (ANSI standard) |
| Hash | # | MySQL |
| Inline | /* … */ | most |
Putting It Together: Subverting a Login
Now the classic auth-bypass in full. The login query:
An attacker who wants to log in as a specific known user (admin) without the password supplies the username:
admin'-- logs you in as a specific user (admin) by terminating the username and commenting away the password check. (2) ' OR '1'='1 (Chapter 1) makes the whole WHERE true and returns all users — you get logged in as whoever the app picks from the result set (often the first row). The first is precise; the second is a blunt "match everyone." Both exploit the same flaw, and both vanish under parameterized queries — and note this is also an authentication bypass (Auth course): account access with no credential, purely from a query-construction bug.
The Three Moves, Generalized
Every injection payload, however complex, is these three moves:
- Break out — a
'(string context) or nothing (numeric) to escape the data slot into code position. - Inject — your SQL: boolean logic (
OR 1=1), aUNION(Chapter 4), a stacked statement (Chapter 6), etc. - Fix up — a comment (
--) to discard the trailing query, or carefully balanced quotes so the whole thing still parses.
Recognizing these three moves lets you read any payload — and the next chapters expand the middle move (what you inject) for data extraction, blind inference, and beyond.
Hands-On Exercises
For a string-context query WHERE name = 'INPUT' and a numeric-context query WHERE id = INPUT, write an always-true injection for each. Explain why the numeric case needs no quote, and why "we strip quotes from input" fails to protect the numeric query.
Explain what role a SQL comment (-- ) plays in an injection. Given WHERE username='$u' AND password='$p', show the exact username payload that logs you in as admin with no password, and write out the resulting query. Note the trailing-space requirement of -- .
Break down the three moves (break out / inject / fix up) for the payload admin'-- and for ' OR '1'='1. Explain how the two login-bypass shapes differ (target a specific user vs match all), and confirm both are eliminated by parameterized queries.
Chapter 2 Quick Reference
- Every injection = break out (escape the data slot) → inject (your SQL) → fix up (comment/balance so it parses)
- String context (
name='INPUT') needs a'to break out; numeric context (id=INPUT) needs no quote - "We quote/strip quotes" is not a defence — numeric context injects with no quote; quote-handling is fragile and breaks real data
- Probe injectability: a lone
'(error),' OR '1'='1vs' AND '1'='2(logic change),4+1arithmetic (numeric) - Comments (
--with trailing space,#MySQL,/* */) cut off the rest of the developer's query - Login bypass:
admin'--(log in as a specific user) vs' OR '1'='1(match all) — both are auth bypass with no credential - All of these vanish under parameterized queries (Chapter 7) — context and breakout become irrelevant
- Next chapter: the types of SQLi — in-band (error/union), blind (boolean/time), and out-of-band