This SQL injection cheatsheet is a defensive reference: SQL injection happens when untrusted input is mixed into a database query as code rather than data, and the reliable fix across every language and framework is parameterized queries, which keep user input from ever being interpreted as SQL. The goal here is to help you recognize the vulnerability in your own code and remove it, not to attack systems you do not own. Every example below is framed around detection and prevention.
SQL injection remains one of the most damaging web vulnerabilities because a single injectable query can expose an entire database. It also happens to be one of the most completely solvable, which is why this cheatsheet focuses on the handful of patterns that actually matter.
The one thing that causes it
Every SQL injection bug reduces to the same mistake: building a query by concatenating user input into a string. Here is the anti-pattern, in Python, that you are hunting for:
# VULNERABLE — do not write queries like this
query = "SELECT * FROM users WHERE email = '" + user_input + "'"
cursor.execute(query)
Because user_input becomes part of the query text, input that contains SQL syntax changes what the query does instead of just supplying a value. The database has no way to tell the developer's intended query apart from the attacker's addition, because by the time it arrives they are the same string.
The same anti-pattern appears everywhere: string formatting, f-strings, template literals, and String.format are all just concatenation with nicer syntax. If user input reaches a query as text, you have the bug regardless of how the string was assembled.
The fix: parameterized queries
The definitive fix is to never build queries by concatenation. Use parameterized queries (also called prepared statements), where you write the query with placeholders and pass the values separately. The database then treats the values strictly as data:
# SAFE — parameterized query
cursor.execute(
"SELECT * FROM users WHERE email = %s",
(user_input,)
)
The equivalent in Java with JDBC:
PreparedStatement stmt = conn.prepareStatement(
"SELECT * FROM users WHERE email = ?");
stmt.setString(1, userInput);
ResultSet rs = stmt.executeQuery();
And in Node with a parameterized driver call:
db.query('SELECT * FROM users WHERE email = $1', [userInput]);
In all three the placeholder is the point. The value can contain any characters at all, including SQL keywords and quotes, and it will still be handled as a plain string value, never as executable SQL. This is not "escaping" the input; it is a structurally different mechanism where the query text and the data travel on separate channels.
What about ORMs and query builders?
Most ORMs (SQLAlchemy, Hibernate, Prisma, ActiveRecord) parameterize automatically when you use them as intended, which is a large part of why they reduce injection risk. The catch is the escape hatches. Nearly every ORM offers a raw-query method, and the moment you concatenate input into one of those, you are back to the vulnerable pattern with the ORM providing no protection:
# VULNERABLE even through an ORM
session.execute("SELECT * FROM users WHERE name = '" + name + "'")
So the cheatsheet rule for ORMs is: trust the parameterized path, treat every raw-query call as a place to audit, and pass bind parameters even in raw queries.
Defense in depth
Parameterized queries are the fix, but a few additional layers reduce impact if something slips through.
Apply least privilege to the database account your application uses. An app that only reads and writes its own tables should not connect as a superuser that can drop tables or read system catalogs. If an injection does occur, the account's limited permissions cap the damage.
Validate input against expected shape where you can. This is not a substitute for parameterization, and input validation alone does not stop SQL injection, but rejecting an email field that contains no @ or a numeric ID that contains letters removes obviously malformed input early.
Use allowlists for anything that legitimately cannot be parameterized, such as a column name in an ORDER BY clause. Parameters bind values, not identifiers, so a dynamic column or table name must be checked against a fixed list of permitted names rather than taken from input directly.
Keep detailed errors out of responses. Verbose database errors leak schema details that make an attacker's job easier. Return generic errors to users and log the detail server-side.
Finding SQL injection in an existing codebase
You do not have to hunt by eye. Static application security testing (SAST) traces user input to query sinks and flags concatenation into SQL, which is the fastest way to sweep a large codebase. Dynamic testing complements it by exercising the running application with crafted inputs and observing behavior; our DAST product is built for that kind of authorized, automated probing against your own environments.
Grep is a crude but useful first pass. Searching for query construction that uses string concatenation or formatting near an execute call surfaces the obvious cases quickly, and it costs nothing to run before a proper scan. For teams building broader appsec coverage, the security academy has hands-on labs that walk through detection and remediation on deliberately vulnerable apps.
FAQ
What is the fastest way to prevent SQL injection?
Use parameterized queries (prepared statements) everywhere user input reaches the database. Write the query with placeholders and pass values separately so the database always treats input as data, never as SQL. This is the definitive fix, not merely a mitigation.
Does input validation stop SQL injection?
Not by itself. Validation is a useful defense-in-depth layer that rejects obviously malformed input, but attackers can craft valid-looking input that still contains SQL. Parameterized queries are what actually close the vulnerability; treat validation as a complement, not a replacement.
Are ORMs safe from SQL injection?
Mostly, when used as intended, because they parameterize queries automatically. The risk is in raw-query escape hatches: concatenating user input into an ORM's raw SQL method reintroduces the vulnerability. Audit every raw-query call and pass bind parameters even there.
How do I find SQL injection in my own code?
Use static analysis (SAST) to trace input into query sinks, and dynamic testing (DAST) to probe the running app. A quick grep for string concatenation or formatting near execute calls is a useful first pass before running a full scan.