SQL Injection (SQLi) & Second-Order SQLi: Prepared statements, ORM vulnerabilities, and blind SQLi.
Mechanics of SQL Injection
SQL Injection occurs when untrusted user input is directly concatenated into a dynamic database query string, allowing an attacker to manipulate the query's abstract syntax tree (AST).
Vulnerable Code:
const query = "SELECT * FROM users WHERE email = '" + req.body.email + "'";
Attacker Payload:
admin@example.com' OR '1'='1' --
Executed Query:
SELECT * FROM users WHERE email = 'admin@example.com' OR '1'='1' --'
Result: Bypasses authentication; returns all user records!
1. The Definitive Defense: Parameterized Prepared Statements
Parameterized queries separate code from data. The SQL query structure is pre-compiled by the database engine before the parameters are bound. Input parameters are treated strictly as literal values, rendering SQL injection mathematically impossible.
// SECURE: Parameterized Query
const query = 'SELECT id, email, role FROM users WHERE email = $1 AND active = $2';
const result = await db.query(query, [req.body.email, true]);
2. Second-Order SQL Injection
Second-Order SQLi occurs when malicious input is safely stored in the database without immediate execution (e.g. registration name set to admin'--), but is later concatenated into a raw query by an asynchronous reporting job or background admin worker.
Rule: Always use parameterized queries even when reading from your own internal database tables!