Why SQL Injection Still Works in 2026

I found SQL injection on a system that had been running for three years. Here's why the oldest web vulnerability is still the most common one.
The system had been running for three years. It had been through two development teams. It had a login form, a search field, and a contact page. When I ran a penetration test against it, the first input field I tested was vulnerable to SQL injection.
Not a subtle edge case. A login form that accepted ' OR '1'='1 and authenticated without credentials.
What SQL Injection Is, Specifically
SQL injection happens when user input is inserted directly into a SQL query without being treated as data. A login form might construct its query like this:
SELECT * FROM users WHERE username = 'alice' AND password = 'pass123';
If the application builds that query by concatenating strings — and the username field accepts ' OR '1'='1' -- — the query the database receives becomes:
SELECT * FROM users WHERE username = '' OR '1'='1' -- ' AND password = '';
The -- comments out the rest of the query. '1'='1' is always true. The WHERE clause returns every row in the users table. The application sees a result, assumes authentication succeeded, and logs you in as the first account — often the administrator.
What I Found
The login form wasn't the only vulnerable input. SQL injection was present across multiple points in the application: the search functionality, a user ID parameter in a GET request, and a form field in the contact page.
Using Burp Suite as an intercepting proxy, I could modify requests before they reached the server. On the search field, a union-based injection exposed the database schema:
' UNION SELECT table_name, column_name, null FROM information_schema.columns --
The application returned the full table list. Within those tables: usernames, hashed passwords, session tokens, and email addresses for every account in the system.
At no point did I need elevated access or prior knowledge of the database structure. The entry point was a text input. The payload was plaintext. The data the application returned was real.
Why It Still Exists
SQL injection was first documented in 1998. OWASP has included it in the Top 10 since the list began. Every major web framework ships with tools to prevent it. And it still appears consistently in penetration tests of systems that have been running in production for years.
The pattern is predictable: a developer builds a query using string concatenation because that's how the tutorial demonstrated it. The application ships. It works. It passes QA because QA tests with valid inputs. No one flags it during code review because the code does exactly what it's supposed to do — it returns the right data for the inputs it was designed for.
The vulnerability only manifests when someone provides unexpected input. That doesn't happen during normal development or testing. It happens during a penetration test, or when a real attacker finds it.
The gap isn't knowledge. Most developers have heard of SQL injection. The gap is the distance between "I've heard of it" and "I can recognise it in my own code."
Three years is a long time for a vulnerability to sit undetected. It is also a completely normal timeframe. Production systems don't get penetration tested unless someone specifically decides to test them.
The Fix
Parameterised queries — also called prepared statements — separate the query structure from the user data:
cursor.execute(
"SELECT * FROM users WHERE username = ? AND password = ?",
(username, password)
)
The database receives the query and the data as separate items. User input is treated as a value to be compared, not as SQL syntax. Even if the input contains ' OR '1'='1', the database compares it literally against the username column. It has no effect on the query structure because the structure was already compiled before the data was supplied.
This is why parameterised queries work where input validation alone does not. Validation can be bypassed — different encodings, edge cases in character sets, context-specific sequences that pass a filter but still exploit a parser. Parameterised queries remove the vulnerability at the architecture level. There is no point at which user input is interpreted as SQL.
The remediation in this assessment replaced string-concatenated queries with parameterised equivalents throughout the application. Verification confirmed that injection payloads which previously returned data now returned no result and caused no database error.
The Takeaway
A system that has been running for three years without being tested has not been running securely. It has been running without anyone checking.
SQL injection doesn't signal its presence. The application behaves normally for legitimate users. Logs show ordinary queries. The vulnerability is invisible until someone looks for it — and looking for it means attempting exploitation, not reading the code.
Penetration testing is not a launch formality. It is what you do when you need to know what you have actually built, not what you assume you have built.
This assessment was part of my MSc Cybersecurity at Robert Gordon University. Full project write-up: Web Application Penetration Testing & Hardening