The SQLi Killer: Parameterized Queries
The fix isn't filtering. It isn't escaping. Here's why parameterized queries actually stop SQL injection.
You built a login form. An attacker types this into the username field:
' OR '1'='1 Your query becomes:
SELECT * FROM users WHERE username = '' OR '1'='1' AND password = '...' '1'='1' is always true. They’re in. No password needed.
The fix isn’t filtering. It isn’t escaping. It’s parameterized queries — and here’s why they actually work:
# Vulnerable — you're building SQL as a string
cursor.execute(f"SELECT * FROM users WHERE username = '{username}'")
# Safe — query structure is sent first, data arrives separately
cursor.execute("SELECT * FROM users WHERE username = %s", (username,)) In the safe version, the database receives the query template, compiles it, then receives the data as a bound value. The parser already finished its job before your input arrived. ' OR '1'='1 is just a string — the database never considers it as instructions.
The attack has nowhere to land.
One gotcha to know about: parameterized queries protect the point of execution. If you store malicious input safely, then later build a different query by concatenating that stored value — you’re vulnerable again. That’s called second-order injection, and it catches teams who think they’re fully covered.
DOCKER KIT — vulnerable and safe endpoints side by side. Run the attack on both:
docker run --rm -p 8080:8080 ayfr/lab-sqli-params