A05 / Injection — OWASP Top 10 00 / 09
query.sql
A05
OWASP Top 10 · 2025
Injection
When attacker input becomes part of the command your system executes.
// equifax — 2017 — what triggered detection?
Equifax 2017 — read the case before you answer
$700M settlement · 147M records · 78 days undetected

CVE-2017-5638 was an OGNL expression injection in Apache Struts' file upload handler. A patch existed two months before the breach. What made this an injection vulnerability rather than another type of flaw?

📨
Untrusted input reached an interpreter
The HTTP Content-Type header was parsed by Struts’ OGNL expression evaluator without sanitization.
🛡
Missing firewall rules
The Struts server was directly reachable from the internet with no WAF filtering.
⚙️
Server misconfiguration
Struts was running with excessive privileges, allowing the exploit to access sensitive data.
📋
Unpatched known vulnerability
The CVE had been public for 66 days with a fix available — Equifax hadn’t applied it.
// find the vulnerability

Django REST endpoint. One line lets an attacker read your entire database. Click it.

api/views/users.py
// attack walkthrough
step 1 of 4

Parameterised queries — always.

removed
added

Structure and data are compiled separately — the attacker's input can never change what the query does.

🎯 the one habit

Parameterize every query. Never concatenate user input into a command, query, or template — regardless of language, framework, or interpreter.

Python
Java
Node.js
parameterised.py
click the dot
why this works
// knowledge check
1 / 3

Which line introduces the SQL injection vulnerability?

views.py
A  — Line 1: the function accepts user_id as a parameter
B  — Line 2: user_id is concatenated directly into the SQL string
C  — Line 3: cursor.execute() sends the query to the database
// knowledge check
2 / 3

Which Node.js version is safe against SQL injection?

option-a.js
option-b.js
A  — Option A is safe: it validates uid before using it
B  — Option B is safe: $1 is a typed placeholder, uid bound after compilation
// knowledge check
3 / 3

What does this payload achieve?

Quick reference: Tautology = makes WHERE always true (e.g., OR 1=1). UNION-based = appends a second query to extract other tables. Blind/boolean = infers data character by character from true/false responses.

HTTP request
A  — Extracts all password hashes via UNION SELECT
B  — Makes the WHERE clause always true, returning all user records
C  — Infers data one character at a time using boolean responses
// self-check complete

// the mental model

carry this into your next code review

If a string you didn't write can change what an interpreter does rather than what it reads, you have an injection vulnerability.

A05 · Injection
Applies to
SQL · NoSQL · LDAP · XPath · OS cmds · XSS
All injection variants share one root: user-controlled data reaching an interpreter without structural separation. XSS is injection into an HTML/JS parser. LDAP, XPath, OS commands — same principle, different interpreter.
First fix
Parameterise every query — ORM > raw()
Django ORM .filter() parameterises automatically. Raw SQL only when the ORM can't express the query. Never concatenate or interpolate user input into a query string — no exceptions.
Defence in depth
Server-side validation · Least privilege DB user
Parameterisation prevents entry. Least privilege limits blast radius — the app DB user should only have SELECT on the tables it needs. No DROP, no ALTER, no cross-schema reads. Server-side validation is a second gate; client-side alone is not a control.