Anatomy of an Injection

Chapter 2
Anatomy of an Injection
String vs numeric context, comment tricks, and subverting a login — defensively
Defensive framing
We dissect payloads so you can recognize them, test your own apps, and understand why the Chapter 7 fix is the only reliable answer. Practise only on systems you own or on training targets — PortSwigger Web Security Academy, OWASP Juice Shop, DVWA. Never run these against systems you don't have permission to test.

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:

ContextQuery looks likeTo break out you need…
StringWHERE name = 'INPUT'a quote ' to close the string first
NumericWHERE id = INPUTno quote — inject directly
// STRING context — input is inside quotes, so you need a ' to escape: WHERE name = '' OR 1=1' // NUMERIC context — input is NOT quoted, so you inject straight in: WHERE id = 5 OR 1=1 // no quote needed at all
"We quote our inputs" is not a defence — numeric context is injectable with no quote at all
A common false belief is that wrapping inputs in quotes (or stripping quotes from input) prevents SQLi. It doesn't. In numeric context the value isn't quoted, so an attacker needs no quote character to inject — 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=5 vs id=4+1: if both return the same record, the database evaluated 4+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:

CommentSyntaxDatabase
Double-dash-- (note the trailing space)most (ANSI standard)
Hash#MySQL
Inline/* … */most
// without a comment, the trailing ' breaks the syntax: WHERE username = 'admin'' AND password = '...' // syntax error // comment out everything after the injection — the password check vanishes: WHERE username = 'admin'-- ' AND password = '...' ← ignored

Putting It Together: Subverting a Login

Now the classic auth-bypass in full. The login query:

SELECT * FROM users WHERE username = '$user' AND password = '$pass'

An attacker who wants to log in as a specific known user (admin) without the password supplies the username:

username: admin'-- password: (anything) // resulting query — the password check is commented out: SELECT * FROM users WHERE username = 'admin'-- ' AND password = '...' // effectively: SELECT * FROM users WHERE username = 'admin' // → returns the admin row → logged in as admin, no password
Two flavours of login bypass — target a user vs match any user
There are two classic shapes. (1) 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:

  1. Break out — a ' (string context) or nothing (numeric) to escape the data slot into code position.
  2. Inject — your SQL: boolean logic (OR 1=1), a UNION (Chapter 4), a stacked statement (Chapter 6), etc.
  3. 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

Exercise 1

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.

📄 View solution
Exercise 2

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 -- .

📄 View solution
Exercise 3

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.

📄 View solution

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'='1 vs ' AND '1'='2 (logic change), 4+1 arithmetic (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