SQL Injection Is Still #3. Here's Exactly How It Still Happens
It has been understood for 25 years, it is trivial to prevent, and it is still in the OWASP Top 10. Here is the exact code that causes it, the exact code that fixes it, and why it refuses to die.

In this article
Every year someone predicts that SQL injection is finally over. Every year it shows up again in a breach report. In the 2021 OWASP Top 10 it sits at position three, folded into the broader "Injection" category, and the 2025 data keeps it near the top. A vulnerability first written up in 1998 is still compromising production systems in 2026.
That is not because it is hard to prevent. It is because it is easy to introduce, and the fix requires a habit rather than a tool. Let us look at exactly what happens.
What injection really is
SQL injection happens when user input is treated as code instead of data. Your application builds a database query by gluing strings together, and if some of those strings came from a user, the user can smuggle in their own SQL. The database cannot tell the difference between the query you meant and the query the attacker rewrote — it just runs what it is given.
The vulnerable code, line by line
Here is a login check written the way it should never be written — and, still, the way it often is.
Vulnerable — do not ship this# Python, building SQL by string concatenation
username = request.form['username']
password = request.form['password']
query = (
"SELECT * FROM users "
"WHERE username = '" + username + "' "
"AND password = '" + password + "'"
)
db.execute(query)
Now watch what happens when the attacker types ' OR '1'='1 into the username box. The query the database receives becomes:
What the database actually runsSELECT * FROM users
WHERE username = '' OR '1'='1'
AND password = ''
'1'='1' is always true. The WHERE clause matches every row, the query returns the first user — often an administrator — and the attacker is logged in without a password. No exotic tooling. Just a quote and some boolean logic.
The same flaw lets an attacker read other tables (UNION SELECT), dump every password hash, or in the worst cases run commands on the host. Login bypass is just the friendly demo.
The one-line fix
The fix is to stop building queries out of strings and let the database driver separate code from data. This is called a parameterised query, or a prepared statement, and it is the whole solution.
Safe — parameterised query# The ? placeholders are data, never code
query = "SELECT * FROM users WHERE username = ? AND password = ?"
db.execute(query, (username, password))
# Now ' OR '1'='1 is treated as a literal username
# It matches no one. The attack simply fails.
That is it. The placeholder tells the driver "whatever arrives here is a value, never a command". The attacker's quote is stored and compared as literal text. Every mainstream language and framework supports this, and it is usually less code than the dangerous version.
Why it will not die
SQL injection endures because the dangerous pattern is the intuitive one. Gluing strings together to build a query is the first thing a new developer reaches for, plenty of old tutorials still teach it, and a codebase written that way in 2012 is still running today. The fix is cultural: parameterise every query, always, and review for the string-building pattern.
For anyone studying security, injection is the canonical example of why you never trust input. Understand this one flaw deeply and half of the OWASP Top 10 — XSS, command injection, LDAP injection — suddenly makes sense, because they are all the same mistake wearing different clothes.
Defence in depth: what to add after parameterisation
Parameterised queries stop the attack, and for most applications they are enough. But security professionals layer defences so that one mistake is not fatal, and injection is a good place to practise that habit. Run your database access under an account with the least privilege it needs — a web app that only reads a product catalogue should not connect as an administrator, so that even a successful injection is contained. Validate input at the boundary, rejecting obviously malformed data before it ever reaches a query. And add a web application firewall in front, which will catch the crude, automated injection attempts that make up most of the background noise on the internet.
None of these replace parameterised queries — a firewall that blocks ' OR 1=1 is trivially bypassed by an attacker who knows what they are doing — but together they mean a single slip in one query does not hand over the whole database. That layered mindset is the difference between a developer who has heard of security and one who practises it.
How attackers actually find it
Injection is rarely discovered by hand. Attackers point automated tools at every input on a site — every form field, URL parameter and header — firing thousands of probing payloads and watching for the tell-tale signs: a database error message, a change in response time, a page that returns different results for a true versus a false condition. This is why "we're a small site, nobody will target us" is no defence: the scanning is indiscriminate and constant. The same tools are exactly what a penetration tester uses, legitimately, to find these flaws before a criminal does — which is why understanding injection deeply is foundational for both defenders and offensive-security professionals.
The lesson beyond SQL
Injection is worth understanding deeply because it is the clearest example of the single most important idea in application security: never trust input, and never let data become code. Cross-site scripting is the same mistake with a browser instead of a database. Command injection is the same mistake with an operating system shell. Server-side template injection, LDAP injection, XML injection — all the same root cause wearing different costumes. Learn to see the pattern once and you can spot it everywhere: some value the user controls is being interpreted as an instruction rather than treated as inert text. Every fix is a version of the same discipline, keeping the user's data firmly on the data side of the line. Master SQL injection and you have not learned one vulnerability; you have learned the lens through which a large fraction of all web vulnerabilities becomes obvious.
If you take one action away from this article, make it a search of your own codebase for string-built queries — the concatenation pattern shown above. Every match is a potential injection point and a five-minute fix. Finding them before an attacker does is the entire job, and it is well within the reach of anyone who now knows exactly what the dangerous pattern looks like.
All 8 CISSP domains on one page — $9.99
Pin it above your desk and revise in minutes instead of evenings. Printable PDF, $9.99, free re-downloads for life.
Grab it for $9.99