Quick reference for web application security. Use alongside the security-and-hardening skill.
- Threat Modeling (Start Here)
- Pre-Commit Checks
- Authentication
- Authorization
- Input Validation
- Security Headers
- CORS Configuration
- Data Protection
- Dependency Security
- AI / LLM Security
- Error Handling
- OWASP Top 10 Quick Reference
- OWASP Top 10 for LLMs Quick Reference
Before reaching for controls, spend five minutes thinking like an attacker:
- Trust boundaries mapped (requests, uploads, webhooks, third-party APIs, LLM output)
- Assets named (credentials, PII, payment data, admin actions, money movement)
- STRIDE run per boundary (Spoofing, Tampering, Repudiation, Info disclosure, DoS, Elevation)
- Abuse cases written next to use cases ("how would I misuse this?")
- No secrets in code (
git diff --cached | grep -i "password\|secret\|api_key\|token") -
.gitignorecovers:.env,.env.local,*.pem,*.key -
.env.exampleuses placeholder values (not real secrets)
- Passwords hashed with bcrypt (≥12 rounds), scrypt, or argon2
- Session cookies:
httpOnly,secure,sameSite: 'lax' - Session expiration configured (reasonable max-age)
- Rate limiting on login endpoint (≤10 attempts per 15 minutes)
- Password reset tokens: time-limited (≤1 hour), single-use
- Account lockout after repeated failures (optional, with notification)
- MFA supported for sensitive operations (optional but recommended)
- Every protected endpoint checks authentication
- Every resource access checks ownership/role (prevents IDOR)
- Admin endpoints require admin role verification
- API keys scoped to minimum necessary permissions
- JWT tokens validated (signature, expiration, issuer)
- All user input validated at system boundaries (API routes, form handlers)
- Validation uses allowlists (not denylists)
- String lengths constrained (min/max)
- Numeric ranges validated
- Email, URL, and date formats validated with proper libraries
- File uploads: type restricted, size limited, content verified
- SQL queries parameterized (no string concatenation)
- HTML output encoded (use framework auto-escaping)
- URLs validated before redirect (prevent open redirect)
- Server-side URL fetches allowlisted; private/reserved IPs blocked (prevent SSRF)
Content-Security-Policy: default-src 'self'; script-src 'self'
Strict-Transport-Security: max-age=31536000; includeSubDomains
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
X-XSS-Protection: 0 (disabled, rely on CSP)
Referrer-Policy: strict-origin-when-cross-origin
Permissions-Policy: camera=(), microphone=(), geolocation=()
// Restrictive (recommended)
cors({
origin: ['https://yourdomain.com', 'https://app.yourdomain.com'],
credentials: true,
methods: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE'],
allowedHeaders: ['Content-Type', 'Authorization'],
})
// NEVER use in production:
cors({ origin: '*' }) // Allows any origin- Sensitive fields excluded from API responses (
passwordHash,resetToken, etc.) - Sensitive data not logged (passwords, tokens, full CC numbers)
- PII encrypted at rest (if required by regulation)
- HTTPS for all external communication
- Database backups encrypted
# Audit dependencies
npm audit
# Fix automatically where possible
npm audit fix
# Check for critical vulnerabilities
npm audit --audit-level=critical
# Keep dependencies updated
npx npm-check-updatesSupply-chain hygiene (npm audit won't catch malicious packages):
- Lockfile committed; CI installs with
npm ci(notnpm install) - New dependencies reviewed (maintenance, downloads,
postinstallscripts) - No typosquats (
cross-envvscrossenv,react-domvsreactdom)
For any feature that calls an LLM (chatbots, summarizers, agents, RAG):
- Model output treated as untrusted — never into
eval/SQL/shell/innerHTML/file paths - Prompt injection assumed; permissions enforced in code, not in the system prompt
- Secrets, cross-tenant data, and full system prompts kept out of the context window
- Tool/agent permissions scoped; destructive or irreversible actions require confirmation
- Token, rate, and recursion/loop limits set (bound consumption)
// Production: generic error, no internals
res.status(500).json({
error: { code: 'INTERNAL_ERROR', message: 'Something went wrong' }
});
// NEVER in production:
res.status(500).json({
error: err.message,
stack: err.stack, // Exposes internals
query: err.sql, // Exposes database details
});| # | Vulnerability | Prevention |
|---|---|---|
| 1 | Broken Access Control | Auth checks on every endpoint, ownership verification |
| 2 | Cryptographic Failures | HTTPS, strong hashing, no secrets in code |
| 3 | Injection | Parameterized queries, input validation |
| 4 | Insecure Design | Threat modeling, spec-driven development |
| 5 | Security Misconfiguration | Security headers, minimal permissions, audit deps |
| 6 | Vulnerable Components | npm audit, keep deps updated, minimal deps |
| 7 | Auth Failures | Strong passwords, rate limiting, session management |
| 8 | Data Integrity Failures | Verify updates/dependencies, signed artifacts |
| 9 | Logging Failures | Log security events, don't log secrets |
| 10 | SSRF | Validate/allowlist URLs, restrict outbound requests |
For apps with LLM features. See the OWASP GenAI Security Project.
| ID | Risk | Prevention |
|---|---|---|
| LLM01 | Prompt Injection | Don't trust the system prompt as a boundary; enforce permissions in code |
| LLM02 | Sensitive Information Disclosure | Keep secrets/PII out of prompts; filter outputs |
| LLM03 | Supply Chain | Vet models, datasets, and plugins like any dependency |
| LLM04 | Data and Model Poisoning | Use trusted model sources, verify integrity; vet fine-tuning and RAG data |
| LLM05 | Improper Output Handling | Treat model output as untrusted; validate, parameterize, encode |
| LLM06 | Excessive Agency | Scope tool permissions; confirm destructive actions |
| LLM07 | System Prompt Leakage | Assume the system prompt can leak; put no secrets in it |
| LLM08 | Vector and Embedding Weaknesses | Partition RAG embeddings per tenant; validate documents before indexing |
| LLM09 | Misinformation | Ground answers with citations; validate critical claims; keep a human in the loop |
| LLM10 | Unbounded Consumption | Cap tokens, request rate, and loop/recursion depth |