The canonical example

// VULNERABLE
const q = "SELECT * FROM users WHERE email = '" + email + "'";
db.query(q);

// Attacker submits: ' OR '1'='1
// Resulting query: SELECT * FROM users WHERE email = '' OR '1'='1'

The same pattern enables data exfiltration with UNION, destructive operations with ; DROP TABLE, and authentication bypasses by manipulating WHERE clauses.

Variants

The one true defense: parameterized queries

// SAFE — every driver in every language supports this
db.query("SELECT * FROM users WHERE email = ?", [email]);
// or
db.query("SELECT * FROM users WHERE email = $1", [email]);

Parameterized queries (also called prepared statements) send the query template and the parameters separately. The database never parses user input as SQL. This is not a defense-in-depth measure — it's the actual fix.

Things that are not defenses

!

Stored procedures aren't automatically safe. A stored procedure that builds dynamic SQL from input is just as vulnerable as inline code. The protection comes from parameterized invocation, not from the procedure boundary.