The Hidden Security Risks of Building Apps With AI — and How to Fix Them

calendar_today September 5, 2026
person info@softcrony.com
folder Security
Security vulnerabilities in AI generated code illustration showing risks from AI coding tools and how developers can fix them in 2026

🕮11 min read · 2,147 words

AI coding tools have genuinely changed how software gets built. GitHub Copilot, Cursor, Claude, ChatGPT — developers are using them to write boilerplate, generate functions, scaffold entire features, and move faster than was previously possible. The productivity gains are real and significant.

But there’s a problem that doesn’t get nearly enough attention: AI coding tools produce insecure code with alarming regularity. Not because the AI is malicious — because it’s optimising for something different than security. It’s optimising for code that looks correct, compiles, and solves the immediate problem. Security is a separate dimension that the AI doesn’t automatically prioritise unless you explicitly ask it to — and even then, it makes mistakes.

This guide is about what those security problems actually look like, why they happen, and what a development team needs to do to catch them before they reach production.

Why AI-Generated Code Has Security Problems

Understanding why the problem exists helps you know what to look for.

AI coding models are trained on vast amounts of publicly available code — open source repositories, Stack Overflow answers, tutorials, documentation examples. A significant portion of that code has security problems. Tutorials often skip security for brevity. Stack Overflow answers prioritise making something work over making it secure. Legacy open source code carries vulnerabilities that were never fixed. The AI learns patterns from all of this — including the insecure patterns.

When you ask an AI to write a function that handles user authentication, or processes a file upload, or queries a database, it generates code based on the patterns it learned during training. If the most common pattern in its training data for that task included a SQL injection vulnerability, the AI will reproduce that pattern confidently and without warning.

A second reason is that AI has no awareness of your specific application context. It doesn’t know your threat model, your data sensitivity, your regulatory requirements, or your existing security controls. It answers the question in front of it without knowing what surrounds it. A snippet of code that’s technically correct in isolation might be dangerously wrong in the context of your specific application.

A third reason is that AI tools are optimised for immediate feedback — making the code work. Security problems often don’t surface immediately. An SQL injection vulnerability won’t cause a test failure. An insecure file upload handler will pass all functional tests. The AI has no mechanism to discover these problems through the feedback loop it’s optimising for.

The Most Common Security Vulnerabilities in AI-Generated Code

SQL Injection remains one of the most common vulnerabilities in AI-generated code. When you ask an AI to write a database query, it frequently generates code that concatenates user input directly into query strings — the textbook SQL injection pattern. This is especially common in older-style PHP code, raw SQL queries, and examples that predate modern ORM usage.

A vulnerable pattern an AI might generate:

// VULNERABLE — never do this
$query = "SELECT * FROM users WHERE email = '" . $_POST['email'] . "'";
$result = mysqli_query($conn, $query);

The correct approach uses prepared statements or a query builder that handles parameterisation automatically:

// SECURE — parameterised query
$stmt = $pdo->prepare("SELECT * FROM users WHERE email = ?");
$stmt->execute([$_POST['email']]);

In Laravel, the Eloquent ORM handles this automatically — but if you ask an AI to write raw DB queries for performance, it may revert to unsafe patterns. Always use parameter binding when writing raw queries.

Insecure Direct Object References (IDOR) are extremely common in AI-generated API code. When an AI writes a route that retrieves a resource by ID — a user record, an order, a file — it frequently forgets to verify that the authenticated user actually has permission to access that specific resource. The code checks that the user is logged in, but not that the resource belongs to them.

// VULNERABLE — missing ownership check
Route::get('/orders/{id}', function ($id) {
    return Order::findOrFail($id); // Returns any order regardless of who owns it
});

// SECURE — verify ownership
Route::get('/orders/{id}', function ($id) {
    $order = Order::where('id', $id)
                  ->where('user_id', auth()->id())
                  ->firstOrFail();
    return $order;
});

This vulnerability allows any authenticated user to access any other user’s data simply by changing an ID in the URL. It’s trivially exploitable and AI generates this pattern frequently.

Hardcoded credentials and secrets appear regularly in AI-generated code — especially in configuration examples, database connection code, and API integration snippets. The AI generates working code with placeholder or example credentials that look real. Developers copy the pattern, replace the placeholder with their actual credentials, and commit it to version control without thinking.

// VULNERABLE — credentials in code
$client = new StripeClient('sk_live_actualrealkey123456789');

// SECURE — credentials in environment variables
$client = new StripeClient(env('STRIPE_SECRET_KEY'));

API keys, database passwords, JWT secrets, and third-party credentials should never appear in source code. They belong in environment variables, loaded from a secrets manager in production. AI tools frequently generate the insecure pattern because training data is full of it.

Missing input validation and sanitisation is a broad category that AI-generated code handles inconsistently. File uploads are a particularly dangerous example. AI-generated file upload handlers frequently skip MIME type validation, accept any file extension, store files in publicly accessible directories, and use the original filename without sanitisation — creating multiple overlapping vulnerabilities.

// VULNERABLE — missing validation
if ($_FILES['upload']['error'] === 0) {
    move_uploaded_file(
        $_FILES['upload']['tmp_name'],
        'uploads/' . $_FILES['upload']['name'] // original filename, any extension
    );
}

// SECURE — validate type, randomise name, restrict location
$allowedTypes = ['image/jpeg', 'image/png', 'image/webp'];
$file = $request->file('document');

if (!in_array($file->getMimeType(), $allowedTypes)) {
    abort(422, 'File type not allowed');
}

$filename = Str::uuid() . '.' . $file->extension();
$file->storeAs('private/uploads', $filename); // Not publicly accessible

Broken authentication patterns appear when developers ask AI to implement custom authentication rather than using established libraries. JWT handling is a common example — AI-generated JWT validation code frequently misses critical checks: verifying the algorithm, checking token expiration, validating the signature correctly. Using Laravel Sanctum, Passport, or a well-maintained auth library is almost always safer than AI-generated custom authentication.

Exposed sensitive data in API responses happens because AI-generated API endpoints frequently return entire database records rather than carefully scoped response objects. A user endpoint that returns the full User model will include password hashes, email addresses, internal IDs, and other sensitive fields that should never be exposed to the client.

// VULNERABLE — exposes all fields including password hash
return response()->json(User::find($id));

// SECURE — explicit field selection using API Resource
return new UserResource(User::find($id));
// UserResource only returns: id, name, avatar, created_at

Missing rate limiting on sensitive endpoints is consistently absent from AI-generated code. Login endpoints, password reset flows, OTP verification, and API endpoints that consume external services should all have rate limiting — both to prevent brute force attacks and to control costs. AI generates functional endpoint code without this protection unless explicitly asked.

Insecure CORS configuration is a problem in AI-generated backend code for SPAs and mobile apps. The quickest way to make a CORS error disappear — which is what the AI optimises for — is to allow all origins. This is appropriate for fully public APIs but catastrophic for APIs that handle authenticated user data.

// VULNERABLE — allows any origin
header("Access-Control-Allow-Origin: *");

// SECURE — explicit allowed origins
'allowed_origins' => [env('FRONTEND_URL', 'https://app.yourdomain.com')],

Why Code Reviews Don’t Always Catch These

A natural response to AI security risks is “we’ll catch them in code review.” This is partially true but overconfident in practice.

When code review volume increases — as it does when AI tools significantly accelerate code production — review quality degrades. Reviewers spend more time reading more code and have less cognitive bandwidth per line. Security-relevant issues that require careful thought about attack scenarios are the first things to get missed when reviewers are moving fast.

AI-generated code often looks clean and well-structured, which creates a false sense of safety. A reviewer who sees properly formatted, well-commented code with sensible variable names is less likely to scrutinise it carefully than messy, obviously rushed code. The security problem isn’t visible at a surface level — it requires understanding what the code does in adversarial conditions, not just whether it works in the happy path.

Reviewers who are themselves using AI tools for their own work are increasingly accustomed to patterns that AI produces — including insecure ones. If everyone on the team has normalised AI-generated query patterns, a SQL injection vulnerability in a code review might not register as wrong.

What to Actually Do About It

Add security requirements to your AI prompts explicitly. When using an AI tool to write code that handles user input, authentication, database queries, or file operations — specify your security requirements in the prompt. “Write a file upload handler for Laravel that validates MIME type, rejects executables, randomises the filename, and stores files outside the web root” produces significantly more secure output than “write a file upload handler.”

Ask the AI to review its own output for security issues as a second step: “Review this code for OWASP Top 10 vulnerabilities and suggest fixes.” This isn’t foolproof — the AI can miss things — but it catches obvious problems and builds security thinking into the workflow.

Use static analysis tools as part of your CI pipeline. Static analysis tools examine code for known vulnerability patterns automatically, without requiring human reviewers to catch everything manually. For PHP and Laravel projects, tools like PHPStan at a high rule level, Psalm, and Enlightn (specifically designed for Laravel security) catch many of the vulnerability patterns that AI generates. These should run on every pull request and block merging when they find critical issues.

For JavaScript and Node.js projects, npm audit catches known vulnerable dependencies. ESLint with security plugins catches common JavaScript security patterns. For Python projects, Bandit performs similar static security analysis.

Run dependency audits regularly. AI tools sometimes suggest outdated package versions that have known vulnerabilities, or generate package.json and composer.json entries that pull in vulnerable transitive dependencies. Automated dependency scanning — GitHub Dependabot, Snyk, or similar — should flag vulnerable dependencies before they reach production.

Conduct dedicated security-focused code reviews for high-risk code. Not every file needs the same level of security scrutiny, but some areas deserve focused attention: authentication and authorisation logic, anything that handles file uploads or user-supplied input, API endpoints that return sensitive data, payment processing flows, and any code that touches external systems. For these areas, a separate security-focused review pass — specifically looking for OWASP Top 10 vulnerabilities — is worth the time.

Use API Resources and Form Requests consistently in Laravel. These two patterns address multiple AI-generated security problems simultaneously. Form Requests enforce input validation at the controller boundary, making it much harder for unvalidated input to reach business logic. API Resources provide explicit control over what data is serialised into responses, preventing accidental data exposure. Both are patterns that AI tools often skip in favour of more direct approaches — make them mandatory in your project standards.

Environment variable hygiene needs to be a hard rule. No credentials, API keys, or secrets in source code — ever. This means a .env.example file with placeholder values, .env in .gitignore, a documented process for sharing secrets with new team members through a secrets manager or encrypted channel, and automated scanning for accidental credential commits using tools like GitGuardian or git-secrets in your pre-commit hooks.

Penetration testing before major releases. Static analysis and code review catch pattern-based vulnerabilities but miss logic flaws — vulnerabilities that arise from how components interact rather than how individual functions are written. A penetration test — even a focused, time-boxed one — on a significant new feature or before a major release catches these logic-level issues. For applications handling sensitive data or financial transactions, this is not optional.

Building Security Into Your AI-Assisted Development Workflow

The goal isn’t to stop using AI coding tools — the productivity benefits are too significant. The goal is to build a workflow where AI handles the speed and AI introduces security debt gets caught systematically before it ships.

This means treating AI-generated code with appropriate scepticism — not the unquestioning trust that comes from seeing clean, well-formatted output. It means adding security tooling to your pipeline that doesn’t depend on human reviewers catching everything. It means building team habits around explicit security prompting when using AI for sensitive code paths. And it means accepting that using AI tools increases the responsibility to review and validate, not decreases it.

The developers and teams that use AI tools safely aren’t the ones who avoid them. They’re the ones who understand the specific failure modes and have built systematic defences against each of them.

If you’re building an application using AI-assisted development and want a security review of your codebase — or want to discuss how to build security tooling into your development pipeline — our team at Softcrony is happy to help. We work with development teams across India and internationally to build applications that are fast to develop and secure to deploy.

Leave a comment