🧠 How SQL Injection works

1. The normal flow

👤 User
alice / wonderland
🖥️ Web App
🗄️ Database
SELECT * FROM users
WHERE username = 'alice' AND password = 'wonderland';

One row matches → user is logged in. Everyone is happy.

2. The attack: input becomes code

The vulnerable server concatenates user input straight into SQL:

"SELECT * FROM users WHERE username = '" + input + "' AND password = '" + input + "'"

Attacker types this in the username field:

' OR '1'='1' --

The server happily builds:

SELECT * FROM users
WHERE username = '' OR '1'='1' --' AND password = 'anything';

The -- turns the rest of the line into a comment, so the password check is gone. '1'='1' is always true → the WHERE clause matches the first row → attacker is logged in, often as admin. 💥

😈 Attacker
' OR '1'='1' --
🖥️ Web App
concatenates string
🗄️ DB returns
admin row

3. It's not just login bypass — attackers read & tamper with data

Auth bypass is the easy demo. In the real world, attackers do far more once they can inject SQL:

📤 UNION-based exfiltration (read anything)

Append a second SELECT to the original query and stitch its rows in:

' UNION SELECT id, username, password, secret FROM users --

The "username" column in the response now contains every user's password. Same trick works to read information_schema, other tables, secrets — anything the DB account can see.

🔧 Stacked-query tampering (change anything)

If the driver allows multiple statements per call, ; ends your query and opens a new one:

'; UPDATE users SET password='pwn3d' WHERE username='admin'; --

No login required — and your application logs show only the original SELECT. Drivers like better-sqlite3 or libpq's simple-query interface reject this, but sql.js, MySQL's multi=true, and many ORM raw-query helpers don't. Never rely on the driver — fix it at the query.

⏱️ Blind & time-based

Even when the app returns no data, attackers can ask "is the first letter of admin's password a?" using a CASE + sleep() trick, and read the response time. No errors, no rows leaked — just patience.

4. Why it works — the mental model

❌ Concatenation

Data and code share the same string. The DB can't tell them apart.

"... WHERE username = '" + input + "'"

Input can break out of the quotes and inject new SQL.

✅ Parameterized query

Code is sent first, data is sent separately. Input is always treated as a value.

"... WHERE username = ? AND password = ?"
params: [input1, input2]

No matter what the user types, it can never become SQL.

5. Defense checklist

← Back to live demo