Security Review Prompt Templates
AI prompt templates for security code reviews. Identify vulnerabilities and implement secure coding practices.
Overview
Security vulnerabilities often hide in plain sight, injection flaws, authentication bypasses, and data exposure that seem obvious in retrospect. These prompts help you think like an attacker to find vulnerabilities before they're exploited. They're for defensive purposes: identifying weaknesses in your own code and implementing secure patterns.
Best Practices
Specify what the code handles (user input, payments, auth) so security review focuses on relevant threats
Include the full request flow, not just the handler, vulnerabilities often exist at boundaries
Mention your security requirements (compliance, data sensitivity) for appropriate depth
Describe your deployment environment (public internet vs. internal) to assess exposure
Ask for specific vulnerability classes (OWASP Top 10) rather than just 'review for security'
Prompt Templates
1. Security Vulnerability Assessment
Review this code for security vulnerabilities: ```[LANGUAGE] [PASTE CODE] ``` **This code handles:** - [WHAT IT PROCESSES - e.g. user registration, file uploads, payments] **Exposure:** - Internet-facing: [YES/NO] - Authenticated: [REQUIRED/OPTIONAL/NONE] - User input: [DESCRIBE WHAT INPUT IS ACCEPTED] **Check for:** - Injection vulnerabilities (SQL, command, XSS) - Authentication/authorization flaws - Sensitive data exposure - Security misconfigurations - Broken access control **Rate each finding:** Critical / High / Medium / Low **Provide:** Proof of concept (how to exploit) and secure fix
Language: Node.js/Express Code: User login endpoint with password check Handles: User authentication Exposure: Internet-facing, no prior auth required User input: Email and password from POST body
## Security Vulnerabilities Found
### 1. SQL Injection (Critical)
**Vulnerable code (Line 12):**
```javascript
const query = `SELECT * FROM users WHERE email = '${req.body.email}'`;
```
**Exploitation:**
```bash
curl -X POST /login -d "email=' OR '1'='1' --&password=anything"
# Returns first user in database, likely admin
```
**Secure fix:**
```javascript
const query = 'SELECT * FROM users WHERE email = ?';
const [rows] = await db.execute(query, [req.body.email]);
```
---
### 2. Timing Attack on Password Comparison (High)
**Vulnerable code (Line 18):**
```javascript
if (user.password === req.body.password) {
```
**Exploitation:** String comparison exits on first mismatch. Attacker can measure response times to guess password character by character.
**Secure fix:**
```javascript
const crypto = require('crypto');
// Constant-time comparison
const isMatch = crypto.timingSafeEqual(
Buffer.from(storedHash),
Buffer.from(computedHash)
);
```
---
### 3. No Password Hashing (Critical)
**Issue:** Passwords appear to be stored/compared as plaintext.
**Secure fix:**
```javascript
const bcrypt = require('bcrypt');
// On registration
const hashedPassword = await bcrypt.hash(password, 12);
// On login
const isValid = await bcrypt.compare(req.body.password, user.hashedPassword);
```
---
### 4. Missing Rate Limiting (Medium)
**Issue:** No protection against brute force attacks.
**Secure fix:**
```javascript
const rateLimit = require('express-rate-limit');
const loginLimiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 5, // 5 attempts
message: 'Too many login attempts, try again later'
});
app.post('/login', loginLimiter, loginHandler);
```
---
### 5. Verbose Error Messages (Low)
**Issue (Line 20):**
```javascript
return res.status(401).json({ error: 'User not found' });
// vs
return res.status(401).json({ error: 'Incorrect password' });
```
**Exploitation:** Tells attacker which emails are registered.
**Secure fix:** Same message for both cases:
```javascript
return res.status(401).json({ error: 'Invalid email or password' });
```Before deploying code that handles authentication, user input, sensitive data, or external exposure
- •Review authentication code with extra scrutiny, it's the highest-impact target
- •Assume all user input is malicious until validated and sanitized
2. Input Validation Design
Help me design input validation for this endpoint: **Endpoint:** [METHOD] [PATH] **Purpose:** [WHAT IT DOES] **Expected inputs:** ``` [DESCRIBE OR SHOW INPUT FORMAT] ``` **Business rules:** - [RULE 1 - e.g. email must be unique] - [RULE 2 - e.g. age must be 18+] **Threats to mitigate:** - [SPECIFIC CONCERNS - e.g. prevent script injection in bio field] **Framework:** [EXPRESS / FASTAPI / LARAVEL / etc.] **Validation library preference:** [ZOD / JOI / PYDANTIC / etc.] Provide: 1. Validation schema 2. Sanitization rules 3. Error response format 4. Edge cases to handle
Endpoint: POST /api/users (user registration)
Inputs: { email, password, name, bio, birthdate, website }
Business rules: Email unique, password 8+ chars with complexity, age 13+
Threats: XSS in bio/name, SSRF via website URL
Framework: Express.js
Library: Zod## Input Validation Schema
```typescript
import { z } from 'zod';
import xss from 'xss';
// Custom refinements
const sanitizeString = (val: string) => xss(val.trim());
const isNotDisposableEmail = (email: string) => {
const disposableDomains = ['tempmail.com', 'throwaway.com'];
const domain = email.split('@')[1];
return !disposableDomains.includes(domain);
};
const isSafeUrl = (url: string) => {
try {
const parsed = new URL(url);
// Prevent SSRF: only allow http(s), no internal IPs
if (!['http:', 'https:'].includes(parsed.protocol)) return false;
const ip = parsed.hostname;
if (ip.startsWith('127.') || ip.startsWith('10.') ||
ip.startsWith('192.168.') || ip === 'localhost') return false;
return true;
} catch {
return false;
}
};
// Registration schema
export const CreateUserSchema = z.object({
email: z
.string()
.email('Invalid email format')
.max(254, 'Email too long')
.toLowerCase()
.refine(isNotDisposableEmail, 'Disposable emails not allowed'),
password: z
.string()
.min(8, 'Password must be at least 8 characters')
.max(128, 'Password too long') // Prevent DoS via bcrypt
.regex(
/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)/,
'Password must contain uppercase, lowercase, and number'
),
name: z
.string()
.min(1, 'Name is required')
.max(100, 'Name too long')
.transform(sanitizeString), // XSS protection
bio: z
.string()
.max(500, 'Bio too long')
.optional()
.transform((val) => val ? sanitizeString(val) : val),
birthdate: z
.string()
.regex(/^\d{4}-\d{2}-\d{2}$/, 'Use YYYY-MM-DD format')
.refine((date) => {
const age = Math.floor(
(Date.now() - new Date(date).getTime()) / (365.25 * 24 * 60 * 60 * 1000)
);
return age >= 13;
}, 'Must be at least 13 years old'),
website: z
.string()
.url('Invalid URL')
.refine(isSafeUrl, 'Invalid website URL')
.optional(),
});
export type CreateUserInput = z.infer<typeof CreateUserSchema>;
```
## Middleware Usage
```typescript
import { Request, Response, NextFunction } from 'express';
export const validateCreateUser = (
req: Request,
res: Response,
next: NextFunction
) => {
const result = CreateUserSchema.safeParse(req.body);
if (!result.success) {
return res.status(400).json({
error: 'VALIDATION_ERROR',
details: result.error.errors.map((e) => ({
field: e.path.join('.'),
message: e.message,
})),
});
}
// Replace body with validated & sanitized data
req.body = result.data;
next();
};
```
## Edge Cases Handled
| Input | Handling |
|-------|----------|
| `<script>alert(1)</script>` in name | Sanitized to empty/escaped |
| `http://169.254.169.254/` (AWS metadata) | Rejected by SSRF check |
| Very long password | Capped at 128 to prevent bcrypt DoS |
| Unicode homoglyphs in email | Consider punycode normalization |
| Birthdate `9999-12-31` | Fails age check (negative age) |When designing input handling for any endpoint that accepts user data
- •Validate on input AND sanitize on output, defense in depth
- •Always have a max length, even for 'unlimited' text fields (prevent DoS)
3. Authentication Flow Review
Review my authentication implementation: **Auth method:** [SESSION / JWT / OAUTH / etc.] **Implementation:** ```[LANGUAGE] [PASTE AUTH CODE - login, token generation, verification] ``` **Token/session storage:** - Client-side: [COOKIE / LOCALSTORAGE / MEMORY] - Server-side: [DATABASE / REDIS / STATELESS] **Protected resources:** - [WHAT REQUIRES AUTH] **Check for:** - Token security (signing, expiration, storage) - Session management (fixation, hijacking) - Password handling (hashing, reset flow) - Account enumeration prevention - Privilege escalation vectors
Auth method: JWT with refresh tokens Code: Login endpoint, token generation, middleware verification Client storage: Access token in memory, refresh token in httpOnly cookie Server storage: Stateless JWT, refresh tokens in Redis Protected: All /api/* routes except /api/auth/*
## Authentication Security Review
### ✅ Good Practices Observed
1. **Refresh token in httpOnly cookie** - Prevents XSS from stealing long-lived token
2. **Access token in memory** - Not persisted, cleared on tab close
3. **Short access token expiry (15min)** - Limits window of compromise
4. **Refresh tokens in Redis** - Enables revocation
### ⚠️ Issues Found
#### 1. JWT Secret Weakness (High)
**Issue (Line 8):**
```javascript
const secret = process.env.JWT_SECRET || 'fallback-secret';
```
**Problem:** Fallback secret in code. If env var is missing, all tokens signed with known value.
**Fix:**
```javascript
const secret = process.env.JWT_SECRET;
if (!secret || secret.length < 32) {
throw new Error('JWT_SECRET must be set and at least 32 characters');
}
```
#### 2. Missing Token Type Validation (High)
**Issue:** Access token and refresh token use same structure.
**Attack:** Use refresh token as access token to bypass expiration.
**Fix:** Add token type claim:
```javascript
// Access token
const accessToken = jwt.sign(
{ userId, type: 'access' },
secret,
{ expiresIn: '15m' }
);
// Verification middleware
if (decoded.type !== 'access') {
throw new Error('Invalid token type');
}
```
#### 3. Refresh Token Rotation Missing (Medium)
**Issue:** Refresh token reused until expiry.
**Attack:** Stolen refresh token grants indefinite access.
**Fix:** Rotate on use:
```javascript
async function refreshAccessToken(refreshToken) {
const payload = jwt.verify(refreshToken, secret);
// Check if token is in Redis (not already used)
const exists = await redis.get(`refresh:${refreshToken}`);
if (!exists) throw new Error('Token revoked or already used');
// Delete old token
await redis.del(`refresh:${refreshToken}`);
// Issue new refresh token
const newRefreshToken = generateRefreshToken(payload.userId);
await redis.setex(`refresh:${newRefreshToken}`, 7 * 24 * 60 * 60, payload.userId);
return {
accessToken: generateAccessToken(payload.userId),
refreshToken: newRefreshToken
};
}
```
#### 4. No Refresh Token Family Tracking (Medium)
**Issue:** If attacker uses stolen refresh token before user, user's next refresh fails but attacker continues.
**Fix:** Track token families and revoke entire family on reuse detection.
### Security Checklist
| Control | Status |
|---------|--------|
| Passwords hashed (bcrypt) | ✅ |
| httpOnly cookie for refresh | ✅ |
| Secure cookie flag | ⚠️ Check production |
| SameSite cookie attribute | ❌ Add SameSite=Strict |
| Token expiration | ✅ |
| Token revocation | ✅ Via Redis |
| Rate limiting on login | ❌ Add |
| Account lockout | ❌ Add |When implementing or reviewing authentication systems, token handling, or session management
- •Authentication is too important to get wrong, consider using established libraries (Passport, NextAuth)
- •Always have a way to revoke tokens, stateless JWT alone can't handle compromise
4. Security Headers Configuration
Help me configure security headers for my application: **Application type:** [WEB APP / API / STATIC SITE] **Framework:** [EXPRESS / NGINX / CLOUDFLARE / etc.] **Content served:** - [HTML / JSON API / BOTH] - [USES IFRAMES? / LOADS EXTERNAL SCRIPTS?] **Current headers (if any):** ``` [PASTE CURRENT HEADERS] ``` **Requirements:** - [COMPLIANCE NEEDS - PCI, SOC2, etc.] - [SPECIFIC CONCERNS - XSS, clickjacking] Provide: 1. Recommended headers with values 2. Explanation of what each prevents 3. Implementation code/config 4. Testing instructions
Application: React SPA + REST API Framework: Express.js backend, Nginx reverse proxy Content: HTML app at /, JSON API at /api/* External content: Google Fonts, Stripe.js Compliance: SOC2
## Security Headers Configuration
### Express.js Middleware (using Helmet)
```javascript
const helmet = require('helmet');
app.use(helmet({
// Content Security Policy - prevents XSS
contentSecurityPolicy: {
directives: {
defaultSrc: ["'self'"],
scriptSrc: [
"'self'",
"https://js.stripe.com", // Stripe
// Avoid 'unsafe-inline' if possible
],
styleSrc: [
"'self'",
"https://fonts.googleapis.com",
"'unsafe-inline'", // Often needed for CSS-in-JS
],
fontSrc: [
"'self'",
"https://fonts.gstatic.com",
],
imgSrc: ["'self'", "data:", "https:"],
connectSrc: [
"'self'",
"https://api.stripe.com",
],
frameSrc: [
"https://js.stripe.com", // Stripe iframe
],
frameAncestors: ["'none'"], // Prevent clickjacking
objectSrc: ["'none'"],
upgradeInsecureRequests: [],
},
},
// Prevent MIME sniffing
noSniff: true,
// XSS filter (legacy browsers)
xssFilter: true,
// Prevent clickjacking
frameguard: { action: 'deny' },
// HSTS - force HTTPS
hsts: {
maxAge: 31536000, // 1 year
includeSubDomains: true,
preload: true,
},
// Hide Express
hidePoweredBy: true,
// Referrer policy
referrerPolicy: { policy: 'strict-origin-when-cross-origin' },
}));
// Additional headers for API routes
app.use('/api', (req, res, next) => {
res.setHeader('Content-Type', 'application/json');
res.setHeader('Cache-Control', 'no-store'); // Don't cache API responses
next();
});
```
### Nginx Configuration
```nginx
server {
#... SSL config...
# Security headers
add_header X-Frame-Options "DENY" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
# CSP (if not set by app)
add_header Content-Security-Policy "default-src 'self'; script-src 'self' https://js.stripe.com;..." always;
# Permissions Policy (formerly Feature-Policy)
add_header Permissions-Policy "geolocation=(), microphone=(), camera=()" always;
}
```
### Header Explanations
| Header | Value | Prevents |
|--------|-------|----------|
| Content-Security-Policy | Allowlist | XSS, data injection |
| X-Frame-Options | DENY | Clickjacking |
| X-Content-Type-Options | nosniff | MIME-type attacks |
| Strict-Transport-Security | max-age=... | Protocol downgrade |
| Referrer-Policy | strict-origin | URL leakage |
| Permissions-Policy | deny features | Unwanted API access |
### Testing
**Online tools:**
- https://securityheaders.com - Grade your headers
- https://observatory.mozilla.org - Mozilla's scanner
**Manual check:**
```bash
curl -I https://yoursite.com
```
**Expected grade:** A or A+ on securityheaders.com
### SOC2 Compliance Notes
✅ Required for SOC2:
- HSTS with 1-year max-age
- Content-Security-Policy
- X-Frame-Options or CSP frame-ancestors
⚠️ Document any CSP exceptions (unsafe-inline) with justificationWhen deploying web applications, hardening existing apps, or meeting compliance requirements
- •Start with a strict CSP and loosen only when needed, it's easier than tightening later
- •Use CSP report-uri to monitor violations before enforcing
Common Mistakes to Avoid
Focusing only on OWASP Top 10 while missing business logic flaws specific to your application
Assuming frameworks handle security automatically, most require explicit configuration
Testing only happy paths, attackers send malformed, oversized, and unexpected inputs
Frequently Asked Questions
Security vulnerabilities often hide in plain sight, injection flaws, authentication bypasses, and data exposure that seem obvious in retrospect. These prompts help you think like an attacker to find vulnerabilities before they're exploited. They're for defensive purposes: identifying weaknesses in your own code and implementing secure patterns.
Related Templates
Code Review Prompt Templates
AI prompt templates for thorough code reviews. Get comprehensive feedback on code quality, security, and best practices.
Debugging Prompt Templates
AI prompt templates for debugging code. Identify issues, understand errors, and find solutions faster.
Code Documentation Prompt Templates
AI prompt templates for writing code documentation. Create clear comments, READMEs, and API docs.
Have your own prompt to optimize?