Educational Content: This article is written for educational purposes to help developers and cybersecurity students understand software concepts. Always follow ethical guidelines and applicable laws.
I want to share something that happened to me recently that I think a lot of developers will recognize. I was building a small PHP API endpoint — moving fast, under a self-imposed deadline — and I asked an AI assistant to generate the database query logic. It gave me clean, readable, well-commented code. I reviewed it briefly, thought it looked professional, and merged it without running a security review. Three days later, during a more careful audit, I realized the query was vulnerable to SQL injection. The AI had built the query with string concatenation, not prepared statements.
The code looked exactly like something an experienced developer would write. Proper variable names. Helpful comments. Clean structure. And it had a textbook vulnerability sitting right in the middle of it. That experience changed how I think about AI-generated code in a security context, and it's increasingly the experience developers across the industry are having.
The Scale of the Problem
A 2026 analysis by Cycode examining over 100 large language models across 80 distinct coding tasks found that 62% of code generated by current AI models contained at least one exploitable vulnerability. Another data point from the same research: when producing frontend code that handles user-supplied data, AI models generate code vulnerable to cross-site scripting 86% of the time. A separate 2025 study from Stanford's Security Lab found that developers using AI coding assistants were significantly more likely to introduce security vulnerabilities than those writing code without assistance — not because the AI was reckless, but because the assistance created confidence that reduced manual review.
These aren't edge cases or exotic vulnerabilities. They're fundamental patterns — SQL injection, XSS, missing input validation, IDOR, hardcoded credentials — appearing in the majority of AI-generated code in security-sensitive contexts. And the insidious part: the code functions correctly. It passes tests. It gets deployed.
Why AI Coding Assistants Generate Vulnerable Code
Understanding the mechanism helps you develop better instincts about when to be skeptical. Large language models learn by processing enormous volumes of code — GitHub repositories, Stack Overflow answers, documentation, tutorials, blog posts. They learn to predict what code should look like based on patterns in that training data.
Here's the structural problem: a significant portion of publicly available code is insecure. Tutorials from five years ago use deprecated patterns. Stack Overflow answers optimize for getting something working quickly in a constrained code block, not for production security. Old repositories haven't been updated to reflect modern practices. The model doesn't distinguish between "this code works" and "this code is secure" — it learns what code looks like and produces statistically likely continuations of whatever you've started writing.
When you ask an AI to generate a login endpoint in PHP, it generates something that resembles the login endpoints in its training data. Many of those were written before prepared statements were universal practice, or by developers who didn't prioritize security, or as quick examples that were never intended for production. The AI reproduces the patterns because that's what it learned to do.
There's also a context problem: security often depends on information the AI doesn't have. Is this endpoint accessible to anonymous users or only authenticated ones? What trust level does input data from this source have? What's the broader authorization model? What other validation happens upstream? The AI works with what's in the prompt and generates something that looks reasonable given that narrow context — missing all the surrounding architecture that security decisions depend on.
The Most Common Vulnerabilities in AI-Generated Code
SQL Injection via String Concatenation
This is the pattern I encountered personally and the most consistently documented vulnerability in AI-generated database code. AI assistants frequently construct database queries via string concatenation rather than prepared statements — especially for queries with dynamic parameters, sorting, filtering, or complex conditions:
<?php
// What AI frequently generates — string concatenation
$username = $_POST['username'];
$query = "SELECT * FROM users WHERE username = '" . $username . "' AND active = 1";
$result = $db->query($query);
// What it should be — prepared statement
$stmt = $pdo->prepare("SELECT id, username, email FROM users WHERE username = ? AND active = 1");
$stmt->execute([$_POST['username']]);
$result = $stmt->fetch();
The concatenated version passes all functional tests because test data doesn't include SQL metacharacters. It fails immediately under any security testing. The pattern is particularly common when the AI generates query construction as part of a larger block — optimizing for the overall structure working rather than each component being secure.
Hardcoded Credentials and API Keys
Ask an AI to generate a complete working example of an API integration, a configuration file, or an environment setup, and there's a meaningful probability it will include what appear to be real credentials:
<?php
// AI-generated configuration — looks like placeholders but isn't always
define('DB_PASSWORD', 'admin123');
define('STRIPE_SECRET', 'sk_live_exampleKeyThatLooksReal');
define('JWT_SECRET', 'mysecretkey2026');
define('SMTP_PASSWORD', 'password@123');
Some of these are clearly fake. Others look uncomfortably like real credentials because they match patterns from the training data. And even obviously fake placeholders establish a bad habit: credentials in code, which developers then fill in with real values. The correct pattern is credentials in environment variables, loaded at runtime, never in version-controlled files.
Missing Input Validation (The Happy Path Problem)
AI-generated code strongly optimizes for the happy path — the case where inputs are well-formed and expected. Validation for unexpected, malformed, or adversarial inputs is consistently underrepresented:
<?php
// AI-generated — no validation, trusts all input
$user_id = $_GET['id'];
$page = $_GET['page'];
$limit = $_GET['limit'];
$stmt = $pdo->prepare(
"SELECT * FROM posts WHERE user_id = ? LIMIT ? OFFSET ?"
);
$stmt->execute([$user_id, $limit, ($page - 1) * $limit]);
// What it should include
$user_id = filter_input(INPUT_GET, 'id', FILTER_VALIDATE_INT);
$page = max(1, filter_input(INPUT_GET, 'page', FILTER_VALIDATE_INT) ?: 1);
$limit = min(100, max(1, filter_input(INPUT_GET, 'limit', FILTER_VALIDATE_INT) ?: 20));
if (!$user_id || $user_id <= 0) {
http_response_code(400);
echo json_encode(['error' => 'Invalid user ID']);
exit;
}
The first version uses a prepared statement (which prevents SQL injection) but still passes through negative page numbers, zero limits, non-integer values that PHP coerces in unexpected ways, and extremely large values that could cause performance issues. The validation layer isn't a substitute for prepared statements — it's an additional layer that prevents malformed input from reaching the database at all.
Broken Authentication Logic and Information Leakage
AI assistants frequently generate authentication code with subtle logic errors that reveal information to attackers:
<?php
// AI-generated — reveals whether email exists (information leakage)
$stmt = $pdo->prepare("SELECT * FROM users WHERE email = ?");
$stmt->execute([$_POST['email']]);
$user = $stmt->fetch();
if (!$user) {
echo json_encode(['error' => 'Email address not found']);
exit; // Tells attacker the email doesn't exist
}
if (!password_verify($_POST['password'], $user['password_hash'])) {
echo json_encode(['error' => 'Incorrect password']);
exit; // Tells attacker the email DOES exist
}
// Correct — same generic message prevents email enumeration
if (!$user || !password_verify($_POST['password'], $user['password_hash'])) {
echo json_encode(['error' => 'Invalid email or password']);
exit;
}
This distinction is subtle — many developers make it intentionally for UX reasons — but it has a real security implication. Differentiating the error messages lets attackers enumerate which email addresses are registered in your system, which they use for targeted phishing and credential stuffing campaigns.
Missing Ownership Checks (IDOR)
AI-generated API endpoints consistently check authentication (is the user logged in?) but miss authorization (is this user allowed to access this specific resource?):
<?php
// AI-generated — checks authentication, misses authorization
function getOrder(int $orderId, PDO $db): array {
$stmt = $db->prepare("SELECT * FROM orders WHERE id = ?");
$stmt->execute([$orderId]);
return $stmt->fetch() ?: [];
}
// Any authenticated user can fetch any order by changing the ID
// Correct — always tie resources to the authenticated user
function getOrder(int $orderId, int $authenticatedUserId, PDO $db): ?array {
$stmt = $db->prepare("SELECT * FROM orders WHERE id = ? AND user_id = ?");
$stmt->execute([$orderId, $authenticatedUserId]);
return $stmt->fetch() ?: null;
}
The AI generates the first version because it passes functional tests — when you test with your own order IDs, you always get valid results. The vulnerability only manifests when someone tries an order ID that belongs to a different user, which no standard test suite covers.
Building a Security Review Workflow for AI-Generated Code
The correct approach isn't to stop using AI coding assistants — they're genuinely useful and improve productivity. It's to treat everything they generate with the skepticism you'd apply to code from a junior developer: smart, capable of producing good code, but not yet having the security instincts that come from experiencing vulnerabilities in production.
For every significant block of AI-generated code that interacts with a database, user input, authentication, or external services, run through this checklist before merging:
Database queries: Is every user-controlled value going through a prepared statement parameter? Are there any dynamic identifiers (column names, table names) that need whitelist validation? Are query results filtered to only the columns needed, not SELECT *?
Input handling: Where does every piece of input data come from? What's its trust level? Is it validated before use (type, format, range)? Is it correctly encoded for wherever it ends up (HTML context, SQL parameter, URL, JSON)?
Authorization: Does every resource query include an ownership check? Is it checking not just "is this user authenticated" but "does this authenticated user have rights to this specific resource"?
Error handling: Do error messages reveal internal information (database names, table names, stack traces, user enumeration)? Are errors logged privately with full details while showing users only generic messages?
Credentials and secrets: Are there any hardcoded values that look like tokens, keys, passwords, or secrets? Even placeholders should be replaced with environment variable loading.
Rate limiting: On any endpoint handling authentication, OTP verification, password reset, or resource creation — is there rate limiting? AI almost never generates this spontaneously.
Writing Better Prompts for More Secure Output
The quality and security of AI-generated code improves significantly when you include security requirements explicitly in your prompt. The model doesn't know what your security requirements are unless you tell it. Explicit context produces better output:
Instead of: "Write a PHP login endpoint"
Try: "Write a PHP login endpoint that: uses PDO prepared statements for all database queries, uses password_verify() for password checking, calls session_regenerate_id(true) after successful login, returns the same generic error message whether the email doesn't exist or the password is wrong (to prevent email enumeration), and is preceded by a call to checkRateLimit($ip, $pdo) which I've already implemented"
The more security context you provide, the more the output reflects it. Also get into the habit of asking the AI to review its own output from a security perspective: "Now review this code for OWASP Top 10 vulnerabilities and list any issues you find." It won't catch everything, but it frequently surfaces the most obvious problems.
Automated Security Scanning as a Safety Net
Manual review catches logic errors and context-dependent vulnerabilities. Automated static analysis catches pattern-based issues at scale. Both are worth integrating into a development workflow that includes AI-generated code.
For PHP projects: Psalm (static analysis that catches type errors and some security issues), PHPStan (static analysis focused on code correctness), and taint analysis tools that trace user input through your application and flag places it reaches sensitive operations without sanitization. For JavaScript: ESLint with eslint-plugin-security flags common security anti-patterns. Semgrep with security rulesets provides pattern-based scanning configurable with custom rules.
These tools aren't substitutes for understanding — they're a safety net that catches what manual review misses in a large codebase.
The Bigger Picture
AI coding assistants have genuinely changed software development. The productivity improvement is real, the quality of generated boilerplate is good, and for well-defined problems they're a legitimate multiplier. None of that is going away.
But the security gap is real and structural. When 62% of AI-generated code contains vulnerabilities, that's not a footnote — it's a shift in where insecure code comes from. The source is changing from developers who haven't learned security fundamentals to developers who trust capable-seeming tools more than those tools have earned.
The adjustment is actually simple, just not automatic: stay skeptical, review security-critical paths carefully regardless of how clean the code looks, give the AI explicit security context in your prompts, run automated analysis, and never merge something just because it works in testing. Working code and secure code are different standards, and the AI optimizes for the former.
Building the habit of security-skeptical review for AI output is, at this point, a core professional skill. — Skand K.