Cybersecurity

Is AI-Generated Code Secure? A Developer Security Checklist

Python JavaScript LLMs MCP AI Agents Cybersecurity Encryption Cryptography Authentication SQL Injection XSS CI/CD Git AWS Databases SQL Java Rust Vector Search Credentials Passwords Hashing HTTPS
1,989 words Includes Code

Is AI-Generated Code Secure? A Developer Security Checklist

🔑 Key Takeaway

AI-generated code is not automatically secure. LLMs produce syntactically correct code that often contains security vulnerabilities. Every AI-generated code snippet must be reviewed for injection flaws, authentication issues, insecure defaults, and other common vulnerabilities.

⚠️ All Examples Use Synthetic Code

This article demonstrates vulnerable and secure patterns using educational examples. Never deploy vulnerable code patterns in production.

Is AI-Generated Code Secure? Developer Security Checklist

Why AI-Generated Code Has Security Issues

LLMs learn from training data that includes both secure and insecure code patterns. The model may generate:

  • Syntactically correct but functionally insecure code
  • Code that works but lacks input validation
  • Patterns that are convenient but vulnerable
  • Outdated cryptographic practices
  • Hardcoded credentials (from training examples)

💡 The Challenge

AI doesn't "understand" security — it generates statistically likely code. Secure coding requires deliberate choices that go beyond what the model learned from training data.

Security Categories Overview

Security categories matrix for AI-generated code vulnerabilities

1. Injection Vulnerabilities

❌ Common AI Mistake: SQL Injection

# ❌ VULNERABLE: AI often generates this pattern
def get_user(user_id):
    query = f"SELECT * FROM users WHERE id = {user_id}"
    cursor.execute(query)
    return cursor.fetchone()

# Attack: user_id = "1 OR 1=1 --"
# Result: Returns ALL users

✓ Secure Pattern: Parameterized Queries

# ✓ SECURE: Use parameterized queries
def get_user(user_id: int):
    query = "SELECT * FROM users WHERE id = %s"
    cursor.execute(query, (user_id,))
    return cursor.fetchone()

# user_id is treated as data, not executable SQL

Review checklist for injection:

  • Search for string formatting in queries (f-strings, .format(), %)
  • Verify parameterized queries are used
  • Check for command injection (os.system, subprocess with shell=True)
  • Validate XSS protection (output encoding, CSP headers)

2. Authentication Issues

❌ Common AI Mistake: Weak Password Hashing

# ❌ VULNERABLE: AI may generate this
import hashlib

def hash_password(password):
    return hashlib.md5(password.encode()).hexdigest()

# Problems:
# - MD5 is cryptographically broken
# - No salt (rainbow table attacks)
# - Fast hashing (brute force)

✓ Secure Pattern: Proper Password Hashing

# ✓ SECURE: Use bcrypt or argon2
from bcrypt import hashpw, gensalt, checkpw

def hash_password(password: str) -> bytes:
    # gensalt() generates a random salt
    # hashpw is slow by design (prevents brute force)
    return hashpw(password.encode(), gensalt())

def verify_password(password: str, hashed: bytes) -> bool:
    return checkpw(password.encode(), hashed)

3. Authorization Flaws

❌ Common AI Mistake: Missing Authorization Check

# ❌ VULNERABLE: No authorization check
def get_user_profile(user_id):
    # Any user can view any other user's profile
    profile = db.query(Profile).filter_by(user_id=user_id).first()
    return profile

# Attack: Change user_id in URL to view other profiles

✓ Secure Pattern: Authorization Check

# ✓ SECURE: Verify authorization
from fastapi import HTTPException, Depends

def get_user_profile(
    user_id: int,
    current_user: User = Depends(get_current_user)
):
    # Check if user is authorized
    if current_user.id != user_id and not current_user.is_admin:
        raise HTTPException(
            status_code=403,
            detail="Not authorized"
        )
    
    profile = db.query(Profile).filter_by(user_id=user_id).first()
    return profile

4. Dependencies Security

📦 Dependency Risks in AI Code

AI models may suggest packages that are:

  • Vulnerable — Known CVEs in suggested versions
  • Abandoned — No longer maintained
  • Maintained by unknown parties — Supply chain risk
  • Unnecessary — Over-complicated solutions
# AI might suggest:
$ pip install requests==2.28.0  # May have CVEs

# Always verify:
$ pip-audit  # Check for known vulnerabilities
$ pip show requests  # Check maintenance status

5. Secrets in Code

❌ Common AI Mistake: Hardcoded Credentials

# ❌ VULNERABLE: AI may generate this for examples
API_KEY = "sk-1234567890abcdef"
DATABASE_URL = "postgresql://user:password@host/db"
SECRET_KEY = "super-secret-key-123"

# These are SYNTHETIC examples in this article
# But AI might generate similar patterns with real-looking keys

✓ Secure Pattern: Environment Variables

# ✓ SECURE: Use environment variables
import os

API_KEY = os.environ.get("API_KEY")
if not API_KEY:
    raise ValueError("API_KEY not set")

DATABASE_URL = os.environ.get("DATABASE_URL")

# Never log or expose secrets
logger.info("API connection established")  # ✓
# logger.info(f"Key: {API_KEY}")  # ❌ NEVER

6. Insecure Defaults

⚙️ Common AI-Generated Insecure Defaults

Setting Insecure Secure
Debug mode True False in production
CORS Allow all origins Specific origins
HTTPS Optional Required
Error messages Verbose stack traces Generic messages
Session timeout Never Appropriate timeout

7. Cryptography Mistakes

❌ Common AI Mistake: Weak Cryptography

# ❌ VULNERABLE: AI may generate these
import hashlib
import random

# Weak hashing
hash_value = hashlib.md5(data).hexdigest()  # ❌ Broken

# Weak random number generation
token = "".join(random.choices(string.ascii_letters, k=32))  # ❌ Not cryptographic

# Hardcoded IV (initialization vector)
iv = b'1234567890123456'  # ❌ Must be random

✓ Secure Pattern: Proper Cryptography

# ✓ SECURE: Use proper cryptographic libraries
import secrets
import hashlib

# Cryptographically secure random
token = secrets.token_urlsafe(32)  # ✓ CSPRNG

# Secure hashing (for non-password data)
hash_value = hashlib.sha256(data).hexdigest()  # ✓ Still consider bcrypt for passwords

# AES-GCM with random IV
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
key = AESGCM.generate_key(bit_length=256)
aesgcm = AESGCM(key)
nonce = secrets.token_bytes(12)  # ✓ Random nonce
ciphertext = aesgcm.encrypt(nonce, data, None)

8. Error Handling

❌ Common AI Mistake: Information Leakage

# ❌ VULNERABLE: Exposing internal details
except Exception as e:
    return {
        "error": str(e),
        "traceback": traceback.format_exc(),  # ❌ Leak
        "query": query,  # ❌ Leak
        "db_password": os.environ.get("DB_PASS")  # ❌ HUGE leak
    }

✓ Secure Pattern: Safe Error Handling

# ✓ SECURE: Log internally, return generic error
import logging

logger = logging.getLogger(__name__)

try:
    result = process_data(input)
except ValidationError as e:
    logger.warning(f"Validation error: {e}")
    return {"error": "Invalid input"}
except DatabaseError as e:
    logger.error(f"Database error: {e}")
    return {"error": "Service temporarily unavailable"}
except Exception as e:
    logger.critical(f"Unexpected error: {e}")
    return {"error": "Internal server error"}  # Generic

Complete Security Review Checklist

✅ 25-Point AI Code Security Review Checklist

💉 Injection

All queries use parameterized statements
No string concatenation in SQL/commands
Input validation for all user-provided data
Output encoding for XSS prevention

🔑 Authentication

Strong password hashing (bcrypt/argon2, not MD5/sha1)
No hardcoded credentials in source code
Secure session management (HttpOnly, Secure cookies)

🔐 Authorization

Authorization checks on all endpoints
No IDOR vulnerabilities (Insecure Direct Object References)
Principle of least privilege applied

📦 Dependencies

All dependencies scanned for CVEs
Versions pinned in requirements
No unnecessary or abandoned packages

🔐 Secrets

No hardcoded API keys or passwords
Environment variables used for configuration
.env files in .gitignore

⚙️ Insecure Defaults

Debug mode disabled in production
CORS restricted to allowed origins
HTTPS enforced

🔒 Cryptography

No MD5/SHA1 for security purposes
cryptographic.random for security tokens
Proper IV/nonce handling in encryption

⚠️ Error Handling

No stack traces exposed to users
Generic error messages for external users
Detailed logging for internal debugging

Automated Security Scanning

🔍 Tools for AI Code Security Review

Tool Type Best For
Bandit Python SAST Python security issues
ESLint Security JavaScript SAST JS/TS security issues
Semgrep Multi-language SAST Custom security rules
Snyk Code SAST/SCA Code + dependency scanning
CodeQL Semantic analysis Deep code analysis
# Example: Running Bandit on Python code
$ pip install bandit
$ bandit -r src/ -f json -o bandit-report.json

# Example: Running Semgrep
$ semgrep --config auto src/

# Integrate into CI/CD pipeline
# Fail builds on high-severity findings

Related BestWordz Resources

Conclusion

AI-generated code is a powerful productivity tool, but it is not a security tool.

Key principles:

  • Always review AI-generated code for security vulnerabilities
  • Use automated security scanning tools
  • Apply the principle of least privilege
  • Never trust AI to generate secure code by default
  • Security is a human responsibility, even with AI assistance

AI accelerates development. Human review ensures security.