How LLMs Silently Introduce SQL Injection Into Your Codebase
LLMs like Copilot and Cursor regularly produce SQL injection vulnerabilities. Learn to spot and fix the most common AI-generated patterns.
The Invisible Injection Problem
SQL injection has been on the OWASP Top 10 for over two decades. Static analyzers catch it. Linters warn about it. Senior developers review for it. And yet, AI coding assistants are reintroducing it into modern codebases at a pace that manual review cannot keep up with.
The root cause is not that LLMs are careless. It is that they are pattern-completion engines trained on enormous corpora of code, including years of legacy tutorials, Stack Overflow answers, and open source projects that predate widespread adoption of parameterized queries. When you ask Copilot to "write a function that fetches a user by email," it reaches for the patterns it has seen most often, and those patterns are frequently unsafe.
What the Vulnerable Output Looks Like
Here is a representative example of the kind of code an LLM will produce if you do not specifically prompt for security:
# Unsafe: LLM-generated without security constraints
def get_user_by_email(email: str) -> dict | None:
conn = get_db_connection()
cursor = conn.cursor()
query = f"SELECT * FROM users WHERE email = '{email}'"
cursor.execute(query)
row = cursor.fetchone()
return dict(row) if row else None
This is a classic string interpolation injection. An attacker supplying ' OR '1'='1 as the email value can dump the entire users table. The same LLM, given slightly different context or a more security-aware prompt, will produce the correct version:
# Safe: parameterized query
def get_user_by_email(email: str) -> dict | None:
conn = get_db_connection()
cursor = conn.cursor()
cursor.execute("SELECT * FROM users WHERE email = ?", (email,))
row = cursor.fetchone()
return dict(row) if row else None
The difference is a single line, but the security implication is enormous.
Why Vibe Coding Makes This Worse
Vibe coding, shipping whatever the AI produces with minimal review, amplifies this problem in a specific way. When a developer accepts a suggestion in Cursor or Copilot without reading it carefully, the flawed pattern propagates. Then, when the next developer asks the AI to write something similar, the AI has even more context pointing it toward the unsafe pattern.
This is sometimes called context poisoning: the AI learns from what is already in the repository, and if the repository contains one injection vulnerability, the probability of the next suggestion being vulnerable increases.
Other LLM-Generated Injection Classes to Watch For
SQL injection is the most visible, but it is not the only class AI tools regularly introduce:
// Unsafe: LLM-generated shell command construction
const output = execSync(`git log --author=${username} --oneline`);
// Safe: argument array prevents shell injection
const output = execFileSync("git", ["log", `--author=${username}`, "--oneline"]);
# Unsafe: LLM-generated LDAP filter construction
filter_str = f"(uid={user_input})"
# Safe: escape special characters before building the filter
import ldap3
filter_str = f"(uid={ldap3.utils.conv.escape_filter_chars(user_input)})"
What Actually Fixes This
Prompt engineering helps at the margins. Adding "use parameterized queries" or "follow OWASP guidelines" to your system prompt reduces injection-prone output, but it does not eliminate it. LLMs do not have a reliable internal security policy.
What works reliably is automated scanning at the point where code enters the repository. A tool that understands AI-generated code patterns, rather than just matching known CVE signatures, can catch these classes before they reach production.
The other lever is code review culture. When your team treats AI output as a first draft rather than a final answer, injection vulnerabilities get caught in review. This requires explicit team norms, not just good intentions.
Key Takeaways
- LLMs default to unsafe string interpolation in database queries because unsafe patterns dominate their training data.
- Vibe coding workflows that skip careful review create a feedback loop that amplifies injection vulnerabilities across a codebase.
- Automated scanning tuned for AI-generated code patterns, combined with a review-first culture, is the most reliable defense.