What is SQL injection?
SQL injection is a vulnerability where input from a user is pasted into a database query as code instead of being passed as data, so the input can change what the query does. Depending on the query, an attacker may read other users' records, bypass a login or modify data.
Why it matters
The bug class is old and well understood, yet it is still part of A05, injection, in the OWASP Top 10:2025, and MITRE tracks it as CWE-89. It survives because building a query by joining strings is the shortest code to write, and because modern stacks still contain raw queries: a reporting endpoint, a search filter, a sort parameter that an ORM does not cover.
The defence is also well understood. The OWASP SQL Injection Prevention Cheat Sheet puts prepared statements with parameterized queries first: the query's structure is fixed in code, and user input can only ever fill a value slot.
Example: the same query, vulnerable and fixed
A product search on your own app (https://your-app.example) might be written like the first line below. The input becomes part of the SQL text, so a quote in it ends the string early and whatever follows is read as SQL. The second version sends the query and the value separately, so the database never parses the input as code:
// Vulnerable: input concatenated into the SQL text
db.query("SELECT * FROM products WHERE name LIKE '%" + q + "%'");
// Fixed: a parameterized query; q is only ever a value
db.query("SELECT * FROM products WHERE name LIKE ?", ["%" + q + "%"]);
To test your own app, send a single quote in each input that reaches a query and watch for a database error, a 500, or a result that changes in a way the input should not explain. That tells you the input reaches SQL unescaped; confirm in the code and fix it there rather than going further. CyberGym, a deliberately vulnerable shop published for practice, includes SQL injection challenges, so you can study the whole bug class end to end on a target that permits it.
Common mistakes
- Escaping quotes by hand. Hand-rolled escaping misses encodings and numeric contexts. Use parameterized queries.
- Assuming an ORM makes you safe. Raw query helpers and string-built filters in an ORM are injectable too. Search the code for them.
- Parameterizing values but not identifiers. Table names, column names and ORDER BY directions cannot be bound as parameters. Map them through an allow-list.
- Hiding errors and calling it fixed. A generic error page makes the bug harder to see, not harder to exploit.
Related terms
Go deeper
Sources
Test only sites you own or have written permission to test. To practise without one, use CyberGym, a legal, deliberately vulnerable target. Practise on CyberGym.
