SQL Injection in AI-Generated Code: How to Find and Fix It
AI coding tools can write queries that glue user input into SQL. Four patterns to search for, with before and after code for each, and a prompt to audit.
SQL injection happens when code builds a database query by gluing user input into the SQL text, so an attacker can change what the query means. AI coding tools can produce that pattern, especially in quick prototypes, so it is worth searching a generated codebase for it. The fix is almost always the same: send the SQL and the user's values separately, using parameterized queries.
This guide is a search-and-fix routine you can run on your own project. The advice follows OWASP's SQL Injection Prevention Cheat Sheet, and the code examples are written for Node and PostgreSQL, though the idea carries to any stack.
What the problem is
OWASP describes SQL injection as arising when applications use dynamic database queries built with string concatenation and user-supplied input. An attacker supplies input that contains SQL, the database cannot tell the code from the data, and it runs the attacker's version.
Here is the shape of it:
// Vulnerable: the customer name becomes part of the SQL text
const sql = `select * from orders where customer = '${req.query.name}'`;
const result = await pool.query(sql);If the name is an ordinary word, this works. If it contains a quote and extra SQL, the query changes. The code looks fine and passes every test with normal input, which is exactly why it survives reviews.
The fix OWASP recommends first
The primary defense is the prepared statement, also called a parameterized query. OWASP says to define all the SQL code first and pass in each parameter to the query later, so the database can always tell code from data. In node-postgres that looks like:
// Safe: SQL and values travel separately
const result = await pool.query(
'select * from orders where customer = $1',
[req.query.name]
);The placeholder $1 is filled by the driver as a value, never as SQL. Nothing the user types can change the structure of the statement.
Four patterns to search for
Open your project and search for each of these. They cover most of how string-built SQL shows up in generated code.
1. Template literals and concatenation inside a query
Search for query calls that contain ${ or + joined to a variable:
// Vulnerable
pool.query("select * from users where email = '" + email + "'");
pool.query(`delete from carts where id = ${cartId}`);
// Fixed
pool.query('select * from users where email = $1', [email]);
pool.query('delete from carts where id = $1', [cartId]);2. Raw query escape hatches in an ORM or query builder
Most ORMs are safe by default and unsafe when you drop to raw SQL. Search for raw, unsafe, queryRaw, or execute and read each call. Anything that interpolates user input into the raw string has the same problem as pattern 1. Use the tool's parameter or bindings feature for raw queries, and check its documentation for the exact syntax.
3. Dynamic table, column, or sort names
Placeholders cannot stand in for identifiers. A sort option from a URL is a classic case:
// Vulnerable: sort comes straight from the query string
pool.query(`select * from products order by ${req.query.sort}`);
// Fixed: allow-list the values you accept
const SORTABLE = { price: 'price', name: 'name', newest: 'created_at' };
const column = SORTABLE[req.query.sort] ?? 'created_at';
pool.query(`select * from products order by ${column}`);OWASP calls this out specifically: for elements like table names, column names, or sort order indicators that cannot use bind variables, use allow-list input validation. The final query still interpolates column, but the value can only be one of your own strings.
4. Search boxes and LIKE filters
Search features are written fast and tested lightly. Look for like '%${term}%'. The fix keeps the percent signs in the value:
// Vulnerable
pool.query(`select * from posts where title like '%${term}%'`);
// Fixed
pool.query('select * from posts where title like $1', [`%${term}%`]);What not to rely on
Escaping user input by hand is the tempting alternative. OWASP calls it fragile and says it cannot guarantee that this option will prevent all SQL injection in every situation, so keep it as a last resort rather than a plan. Input validation, such as checking that an id is a number, is a useful second layer, not a replacement for parameters.
Limiting what the database account can do also helps. OWASP lists least privilege as an additional defense: the account your app uses should not be able to drop tables or read data it never needs. If your backend talks to the database through an exposed API, the access rules are a separate layer, covered in how to check whether your app exposes user data.
A prompt to audit your codebase
Paste this into your AI coding assistant, then verify its findings by hand:
Search this project for every place that sends SQL to the database.
For each one, tell me:
1. The file and line.
2. Whether any part of the query text is built from a variable,
request value, or template literal.
3. Whether that variable can contain user input.
Do not change anything yet. List the risky ones first, then propose
parameterized replacements for me to approve one at a time.Asking for a report before edits keeps the change reviewable. The same habit applies to every generated change, as described in how to review AI-generated code before you ship it. Our overview of whether AI can find security vulnerabilities in your code explains where this kind of audit is strong and where it misses things, what Codex Security Cloud scans and what it will not shows what a hosted repository scanner covers, and signs your AI coding agent is about to ship a security bug lists warning signs to watch for during the build.
Test the fix
After switching to parameters, run the app with awkward input: a name containing an apostrophe, a very long string, an empty value. A parameterized query treats them all as plain text. If a normal user with an apostrophe in their name, such as O'Brien, breaks a screen, you probably still have string-built SQL somewhere.
FAQ
What is SQL injection in simple terms?
It is when user input is treated as part of the database command instead of as data, letting an attacker change what the query does.
Can AI-generated code have SQL injection vulnerabilities?
Yes. Any code that builds SQL from strings and user input can, regardless of who or what wrote it. Generated prototypes are worth checking because speed often wins over care.
Are ORMs immune to SQL injection?
They are safe by default when you use their normal query methods. Raw query features that interpolate user input bring the risk back.
What is the best way to prevent SQL injection?
OWASP's first recommendation is prepared statements with parameterized queries. Use allow-lists for identifiers such as sort columns, and give the database account only the permissions it needs.
How did this land?
About the author

Staff Engineer, Platform
Carlo works on the platform that turns prompts into running apps. He writes the engineering deep dives and the changelog notes worth reading.


