OWASP Top 10, Authentication, Encryption, Network Security, Web Security, Common Vulnerabilities, Security Headers, Best Practices — security mastery.
OWASP Top 10Web SecurityOWASP 2021
⏳
Loading cheatsheet...
🛡️
OWASP Top 10 (2021)
ESSENTIAL
OWASP Top 10 — 2021
#
Vulnerability
Risk
A01
Broken Access Control
CRITICAL
A02
Cryptographic Failures
HIGH
A03
Injection
CRITICAL
A04
Insecure Design
HIGH
A05
Security Misconfiguration
HIGH
A06
Vulnerable Components
HIGH
A07
Auth Failures
CRITICAL
A08
Software & Data Integrity
MEDIUM
A09
Logging & Monitoring Failures
MEDIUM
A10
Server-Side Request Forgery
HIGH
Quick Prevention Summary
Vulnerability
Prevention
Broken Access Control
RBAC, deny by default, check ownership
Crypto Failures
Strong algorithms, proper key mgmt
Injection
Parameterized queries, input validation
Insecure Design
Threat modeling, secure patterns
Misconfiguration
Automated hardening, minimal install
Vulnerable Components
Dependency scanning, updates
Auth Failures
MFA, strong password policy, lockout
SSRF
Allowlist, disable URL redirects
security/access-control.js
// ── A01: Broken Access Control — Prevention ──
// BAD: No authorization check
app.get('/api/users/:id', async (req, res) => {
const user = await User.findById(req.params.id);
res.json(user); // Anyone can access any user!
});
// GOOD: Authorization check + ownership
app.get('/api/users/:id', authenticate, async (req, res) => {
const user = await User.findById(req.params.id);
if (!user) return res.status(404).json({ error: 'Not found' });
// Only admins or the user themselves can access
if (req.user.role !== 'admin' && req.user.id !== user.id) {
return res.status(403).json({ error: 'Forbidden' });
}
res.json(user);
});
// Prevent IDOR (Insecure Direct Object Reference)
// Use UUIDs instead of sequential IDs
// Implement row-level security in database
// Always verify resource ownership
🚫
Broken Access Control is the #1 vulnerability (2021). Always enforce server-side authorization checks. Never trust client-side role checks. Use deny-by-default: if a role is not explicitly granted access, deny.
💉
Injection Attacks
CRITICAL
security/sql-injection.js
// ── SQL Injection (SQLi) ──
// BAD: String concatenation — VULNERABLE!
const query = `SELECT * FROM users WHERE email = '${req.body.email}'`;
// Attack: email = "' OR '1'='1' --" → returns ALL users
// GOOD: Parameterized queries (prevents SQLi)
const user = await db.query(
'SELECT * FROM users WHERE email = $1',
[req.body.email]
);
// GOOD: ORM with parameterized queries
const user = await prisma.user.findUnique({
where: { email: req.body.email }
});
// ── NoSQL Injection ──
// BAD: Passing user input directly to query
const user = await User.find({ email: req.body.email, password: req.body.password });
// Attack: password = { "$gt": "" } → bypasses password check!
// GOOD: Type checking + schema validation
const user = await User.findOne({
email: String(req.body.email),
password: hash, // compare server-side, never in query
});
// ── Command Injection ──
// BAD: Executing user input
const { exec } = require('child_process');
exec(`ping ${req.body.host}`);
// Attack: host = "google.com; rm -rf /"
// GOOD: Input validation + whitelisting
const allowedHosts = /^[a-zA-Z0-9.-]+$/;
if (!allowedHosts.test(req.body.host)) {
return res.status(400).json({ error: 'Invalid host' });
}
exec('ping', ['-c', '4', req.body.host]);
// ── XSS (Cross-Site Scripting) ──
// BAD: Rendering user input without escaping
<div dangerouslySetInnerHTML={'{'}__html: userComment{'}'} />
// Attack: <script>stealCookies()</script>
// GOOD: Auto-escaping (React does this by default)
<div>{'{'}userComment{'}'}</div> // Safe — React escapes
// GOOD: Sanitize if HTML is needed
import DOMPurify from 'dompurify';
const clean = DOMPurify.sanitize(userComment);
security/xss-types.md
# ── XSS Types ──
STORED XSS:
- Malicious input stored in database
- Rendered to other users without escaping
- Most dangerous — persistent
- Prevention: Output encoding, CSP headers
REFLECTED XSS:
- Malicious input in URL/request reflected in response
- Victim clicks crafted link
- Prevention: Output encoding, input validation
DOM-BASED XSS:
- JavaScript modifies DOM with unsanitized data
- Happens entirely client-side
- Prevention: Avoid innerHTML, use textContent
# ── Prevention Checklist
- [ ] Use parameterized queries (no string concatenation)
- [ ] Validate ALL inputs (whitelist approach)
- [ ] Escape outputs (context-aware encoding)
- [ ] Set Content-Security-Policy headers
- [ ] Use HttpOnly cookies (prevent JS access)
- [ ] Sanitize HTML if needed (DOMPurify)
CSRF Prevention
Method
Description
CSRF Token
Hidden form token validated server-side
SameSite Cookie
SameSite=Strict or Lax
Origin Header
Verify Origin matches expected host
Double Submit Cookie
Token in cookie + header, compare
Custom Header
Require X-Requested-With header (AJAX)
Input Validation Principles
Rule
Description
Whitelist
Allow known-good, reject everything else
Type check
Verify expected data type
Length limit
Set maximum length on all fields
Sanitize
Remove/encode dangerous characters
Server-side
NEVER rely on client-side validation only
Schema validation
Use Zod, Joi, Yup for structured data
🚫
SQL injection is still the #1 attack vector. ALWAYS use parameterized queries — never concatenate user input into SQL. Modern ORMs (Prisma, Sequelize, TypeORM) use parameterized queries by default, but raw queries are still vulnerable.
🔑
Authentication Security
AUTH
security/password-hashing.js
// ── Password Hashing (NEVER store plaintext!) ──
const bcrypt = require('bcrypt');
const SALT_ROUNDS = 12;
// Hash password on registration
async function register(email, password) {
// 1. Validate password strength
if (password.length < 8) throw new Error('Password too short');
// 2. Hash with salt (bcrypt generates salt automatically)
const hash = await bcrypt.hash(password, SALT_ROUNDS);
// 3. Store hash (NOT the password!)
await db.query(
'INSERT INTO users (email, password_hash) VALUES ($1, $2)',
[email, hash]
);
}
// Verify password on login
async function login(email, password) {
const user = await db.query(
'SELECT * FROM users WHERE email = $1', [email]
);
if (!user) {
// Don't reveal if email exists
await bcrypt.hash('dummy', 10); // prevent timing attacks
throw new Error('Invalid credentials');
}
// bcrypt.compare is timing-safe
const valid = await bcrypt.compare(password, user.password_hash);
if (!valid) throw new Error('Invalid credentials');
return user;
}
// ── Argon2 (even more secure, OWASP recommended) ──
const argon2 = require('argon2');
async function hashPassword(password) {
return argon2.hash(password, {
type: argon2.argon2id,
memoryCost: 65536, // 64 MB
timeCost: 3, // 3 iterations
parallelism: 4, // 4 threads
});
}
// ── Common Password Mistakes ──
// ❌ MD5, SHA1, SHA256 (too fast, use bcrypt/argon2)
// ❌ No salt (rainbow table attacks)
// ❌ Same salt for all users
// ❌ Storing passwords in logs or error messages
// ❌ Comparing passwords in SQL query (timing attack)
security/jwt-security.js
// ── JWT Security ──
const jwt = require('jsonwebtoken');
// BAD: Weak secret, long expiry, no validation
const token = jwt.sign({ userId }, 'secret', { expiresIn: '365d' });
// GOOD: Strong secrets, short expiry, proper claims
const token = jwt.sign(
{
sub: userId, // subject (who)
role: user.role, // authorization
type: 'access', // token type
iat: Math.floor(Date.now() / 1000), // issued at
},
process.env.JWT_SECRET, // strong random secret (256+ bits)
{
expiresIn: '15m', // short expiry
issuer: 'api.example.com',
audience: 'app.example.com',
algorithm: 'RS256', // asymmetric (preferred) or HS256
}
);
// ── JWT Security Checklist ──
// - Use RS256 (asymmetric) in production
// - Short expiry for access tokens (15 min)
// - Separate refresh tokens (7 days, httpOnly cookie)
// - Validate ALL claims (sub, exp, iss, aud, type)
// - Rotate refresh tokens on each use
// - Store refresh tokens in database (revoke on logout)
// - Blacklist compromised tokens
// - Don't store sensitive data in payload (visible if decoded)
MFA (Multi-Factor Authentication)
Factor
Example
Security
Knowledge
Password, PIN
Low (phishable)
Possession
Phone, hardware key
Medium
Inherence
Fingerprint, face
High
TOTP
Google Authenticator
Medium-High
FIDO2/WebAuthn
YubiKey, Touch ID
Highest
Session Management
Practice
Description
httpOnly cookies
Prevent JS access to session ID
Secure flag
Only send over HTTPS
SameSite
Strict or Lax (prevent CSRF)
Short expiry
Auto-logout after inactivity
Rotate SID
Regenerate after login
Invalidate
Clear on logout/password change
🚫
Never store passwords in plain text.Use bcrypt (min 12 rounds) or argon2id. The password hash should be unique per user (different salt). On login failure, always return the same generic error — don't reveal if the email exists.
# ── HTTPS / TLS ──
TLS Handshake (simplified):
1. Client → Server: ClientHello (supported cipher suites)
2. Server → Client: ServerHello + Certificate + Key Exchange
3. Client → Server: Key Exchange (pre-master secret, encrypted)
4. Both: Derive session keys
5. Both: Encrypted communication begins
TLS Versions:
- TLS 1.2 (minimum acceptable)
- TLS 1.3 (recommended — faster, more secure)
HTTPS Configuration:
- Use TLS 1.3 (fallback to 1.2)
- Strong cipher suites (AES-256-GCM, ChaCha20-Poly1305)
- HSTS header: Strict-Transport-Security: max-age=31536000; includeSubDomains
- OCSP stapling for fast certificate verification
- Certificate from trusted CA (Let's Encrypt is free)
💡
Use AES-256-GCM for encryption at rest.It provides both confidentiality (encryption) and integrity (authentication tag). Never use AES-CBC without HMAC — it's vulnerable to padding oracle attacks. Always use authenticated encryption.
🌐
Network Security
NETWORK
security/cors.js
// ── CORS (Cross-Origin Resource Sharing) ──
// BAD: Allow ALL origins (dangerous in production)
app.use(cors({ origin: '*' }));
// GOOD: Restrict to specific origins
const cors = require('cors');
app.use(cors({
origin: ['https://app.example.com', 'https://admin.example.com'],
methods: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE'],
allowedHeaders: ['Content-Type', 'Authorization'],
credentials: true, // allow cookies
maxAge: 86400, // preflight cache (24h)
}));
// ── Preflight Request (OPTIONS) ──
// Browser sends OPTIONS before actual request for cross-origin
// Server responds with allowed methods/headers
// Actual request only sent if preflight succeeds
Use helmet() as a baseline for security headers, then customize CSP for your app. A strict CSP that disallows inline scripts is the best XSS defense. Always set HSTS to enforce HTTPS.
🐛
Common Vulnerabilities
ATTACKS
security/ssrf.js
// ── SSRF (Server-Side Request Forgery) ──
// BAD: User-controlled URL → server makes request
app.get('/proxy', async (req, res) => {
const data = await fetch(req.query.url); // Attacker controls URL!
// Can access: localhost, internal APIs, cloud metadata
// http://169.254.169.254/latest/meta-data/ (AWS secrets!)
res.send(await data.text());
});
// GOOD: URL allowlist + validation
const ALLOWED_DOMAINS = ['api.example.com', 'cdn.example.com'];
app.get('/proxy', async (req, res) => {
const url = new URL(req.query.url);
// Allowlist domains
if (!ALLOWED_DOMAINS.includes(url.hostname)) {
return res.status(403).json({ error: 'Domain not allowed' });
}
// Block private IP ranges
const ip = await dns.resolve(url.hostname);
if (isPrivateIP(ip)) {
return res.status(403).json({ error: 'Private IPs not allowed' });
}
// Only allow HTTPS
if (url.protocol !== 'https:') {
return res.status(403).json({ error: 'Only HTTPS allowed' });
}
const data = await fetch(url.toString());
res.send(await data.text());
});
Security is a process, not a product. Implement defense in depth: multiple layers of security (input validation, parameterized queries, CSP, rate limiting, monitoring). No single measure is sufficient alone.
🎯
Interview Q&A
PREP
Q: What is SQL injection and how to prevent it?SQL injection occurs when user input is concatenated into SQL queries, allowing attackers to manipulate the query logic. Prevention: (1) Parameterized queries/prepared statements, (2) ORM with parameterized queries, (3) Input validation (whitelist), (4) Least privilege DB user. Never use string concatenation for SQL.
Q: Explain XSS types and prevention.Stored XSS: malicious input stored in DB and rendered to other users. Reflected XSS: input reflected in immediate response (URL params). DOM-based XSS: JS modifies DOM with unsanitized data. Prevention: output encoding, CSP headers, HttpOnly cookies, input validation, React's auto-escaping.
Q: How does CSRF work and how to prevent it?CSRF tricks a user's browser into making authenticated requests to a different site. Prevention: CSRF tokens (unique per form, validated server-side), SameSite=Strict/Lax cookies, verifying Origin header, requiring custom headers (X-Requested-With).
Q: JWT vs session-based auth?JWT: stateless, self-contained, scalable (no server storage), but harder to revoke and token size grows. Sessions: server-side storage, easy to revoke, but requires sticky sessions or shared store. JWT is better for distributed systems; sessions are better for fine-grained security control.
Q: What is CORS and why is it needed?CORS (Cross-Origin Resource Sharing) is a browser security mechanism that restricts cross-origin HTTP requests. Without CORS, a malicious site could make requests to your API using the victim's cookies. Configure allowed origins, methods, headers. Never use wildcard (*) with credentials.
Q: HTTPS vs HTTP — why HTTPS is mandatory?HTTPS encrypts all communication (prevents MITM attacks), provides data integrity (no tampering), and authentication (server identity via certificate). TLS 1.3 is recommended. HSTS header ensures browsers always use HTTPS. Free certificates from Let's Encrypt.
Q: What is Content Security Policy?CSP is an HTTP response header that restricts what resources (scripts, styles, images, fonts) a page can load. It prevents XSS by blocking inline scripts and restricting script sources. Example: script-src 'self' 'nonce-abc' prevents all scripts except from same origin and those with the nonce.
Q: How to store passwords securely?Never store plaintext. Use bcrypt (12+ rounds) or argon2id. These algorithms are intentionally slow (prevent brute force) and include a unique salt per user (prevent rainbow tables). On failed login, always return generic error, use constant-time comparison.
💡
Top security interview topics: OWASP Top 10, SQL injection, XSS (3 types + prevention), CSRF, JWT/session auth, HTTPS/TLS, CORS, CSP headers, password hashing (bcrypt vs argon2), encryption (AES-256-GCM), rate limiting, and secure coding practices.