MySQL Injection
SQL injection happens when untrusted input is concatenated directly into a query, letting an attacker change the query's meaning.
How Injection Happens
SQL injection occurs when application code builds a SQL statement by splicing raw user input into the query string. If the input contains SQL syntax such as quotes, semicolons, or comment markers, that syntax becomes part of the executed statement instead of staying inert data.
A vulnerable login check (do not use)
-- Application builds this string by concatenation:
-- "SELECT * FROM users WHERE username = '" + input + "' AND password = '...'"
-- If input is: admin' --
-- the executed query becomes:
SELECT * FROM users WHERE username = 'admin' -- ' AND password = '...';
-- Everything after -- is a comment, so the password check is skipped entirely.A Classic Bypass Payload
Another common payload is ' OR '1'='1, which turns a WHERE clause into something that is always true, potentially returning every row in a table instead of one matching user.
Tautology injection
-- Concatenated query: SELECT * FROM users WHERE username = '' OR '1'='1' AND password = '...';
-- '1'='1' is always true, so the WHERE clause matches all rows,
-- potentially returning every account regardless of credentials.The Fix: Parameterized Queries
Parameterized queries (also called prepared statements) send the SQL text and the user-supplied values to the database separately. The database compiles the query structure first and then substitutes the values as literal data — never as executable SQL — which makes injection through those parameters impossible.
Safe query with a Node.js MySQL driver
// Using mysql2 with placeholders — values are never concatenated into the SQL string
const [rows] = await connection.execute(
'SELECT * FROM users WHERE username = ? AND password_hash = ?',
[username, passwordHash]
);Safe query with PHP PDO
$stmt = $pdo->prepare('SELECT * FROM users WHERE username = :username');
$stmt->execute(['username' => $username]);
$user = $stmt->fetch();Defense in Depth
Parameterized queries are the primary defense, but a few extra layers reduce the blast radius if a mistake slips through.
- Never build SQL by string concatenation or template literals with user input.
- Use an ORM or query builder that parameterizes automatically, and audit any place it lets you drop to raw SQL.
- Apply the principle of least privilege: the application's database account should not have DROP or GRANT permissions.
- Validate and constrain input types (e.g., ensure an 'id' parameter is actually numeric) as a second layer, not a replacement for parameterization.
- Never trust input just because it came from a hidden form field, cookie, or API header.
Exercise: MySQL Injection
What core weakness enables a classic SQL injection attack?