Security

Anchored on vulnerability classes, not a numbered list from any particular year. Classes are stable; the list ordering shifts every few cycles.

Canonical web vulnerability classes

Injection (SQL, command, LDAP, NoSQL)

How it works: User input is concatenated into a query string or shell command, so the input can terminate the intended syntax and inject attacker-controlled statements.

Fix pattern: Parameterized queries / prepared statements. Never string-concatenate user input into SQL. For shells, pass arguments as an array, not via a shell string.

Broken authentication

How it works: Guessable passwords, missing MFA, session fixation, long-lived tokens without revocation, credential stuffing.

Fix pattern: Memory-hard KDF for passwords, MFA, short access-token TTLs with refresh-token rotation, rate limiting on login, device fingerprinting for high-risk flows.

Sensitive data exposure

How it works: Secrets in source control, unencrypted PII at rest, overly verbose errors, tokens in URLs (which log everywhere).

Fix pattern: Secrets in a vault / KMS, encryption at rest + TLS in transit, strip PII from logs at emit time, never put tokens in query strings.

XML / deserialization attacks

How it works: Unsafe deserializers (pickle, Java ObjectInputStream, certain YAML loaders) execute code from untrusted input. XXE lets external entities exfiltrate files.

Fix pattern: Never deserialize untrusted data with unsafe loaders. Use safe loaders (yaml.safe_load, JSON). Disable DTDs / external entities in XML parsers.

Broken access control

How it works: Users access resources they should not — horizontal (other tenants' data) or vertical (admin endpoints from a regular user).

Fix pattern: Authorize at the boundary of every resource, not just at the login page. Never trust client-sent role claims; derive authorization from server-side identity.

Security misconfiguration

How it works: Default credentials left in place, debug endpoints exposed, open CORS, unnecessary services enabled, missing security headers.

Fix pattern: Harden defaults, minimize surface area, enable security headers (CSP, HSTS, X-Frame-Options), review third-party integration configs.

XSS — Cross-Site Scripting

How it works: User-controlled content rendered to other users without sanitization — attacker scripts run in the victim's browser with the victim's cookies.

Fix pattern: Context-aware output encoding (different rules for HTML body, attribute, JS, URL). Content Security Policy as defense-in-depth. Template engines that escape by default.

CSRF — Cross-Site Request Forgery

How it works: A malicious site tricks a victim's browser into sending an authenticated request to your site using the victim's existing session cookie.

Fix pattern: SameSite=Lax or Strict cookies; anti-CSRF tokens for state-changing requests; require a custom header (browsers do not send custom headers cross-origin without preflight).

SSRF — Server-Side Request Forgery

How it works: User-controlled URL causes your server to make a request — to internal metadata services, private networks, or sensitive endpoints behind your firewall.

Fix pattern: Allow-list of domains; explicit block of private IP ranges (127.0.0.0/8, 169.254.0.0/16, 10/8, 172.16/12, 192.168/16); DNS rebinding protection; no redirects to internal IPs.

Vulnerable / outdated components

How it works: A library with a known CVE in your dependency tree — direct or transitive.

Fix pattern: Automated dependency scanning; monitor security advisories for your stack; pin versions and renew them on a schedule. Know your transitive deps — many vulns live four layers deep.

Insufficient logging & monitoring

How it works: Attacks go undetected because auth failures, access-control denials, and suspicious patterns are not logged or alerted on.

Fix pattern: Log every auth decision (success AND failure) with context; alert on bursts of failures; separate audit logs from application logs; retain for the window your incident response needs.

Authentication vs Authorization

Authentication (authn)

"Who are you?" Proves identity — password, token, cert, MFA. Successful authn produces a verified identity (user id + maybe a set of claims).

Authorization (authz)

"What are you allowed to do?" Decides whether this identity can perform this action on this resource. Runs on every protected request, not just at login.

Common mistake

Treating authn = authz. The user logged in; that does not mean they can read tenant 42's data. Horizontal access-control bugs almost always start here.

Password storage

Kind Verdict Reason
Plaintext Never A database leak becomes a user-identity leak for every site where people reused that password
Unsalted hash (MD5, SHA-1, SHA-256) Never Rainbow tables crack the entire DB in minutes. Fast hashes were designed for speed, not password storage
Salted fast hash Discouraged Per-user salt stops rainbow tables, but a GPU can still try billions per second. Passwords too weak to survive that
bcrypt Acceptable Slow, salted, with a tunable cost factor. Limited to 72-byte inputs (truncates silently)
scrypt Good Memory-hard — forces GPUs and ASICs to spend expensive RAM per guess
argon2 (argon2id) Best Winner of the Password Hashing Competition. Memory-hard, parallelism-tunable. Modern default

Why 'just hash it with SHA-256' is wrong

SHA-256 was designed to be fast — billions of hashes per second on a GPU. A password KDF must be slow and memory-hard so an attacker cannot brute-force the distribution of plausible passwords faster than real users can log in. Slow is the feature.

JWT trade-offs

Good for

  • Short-lived access tokens (minutes) in stateless services
  • Propagating identity across microservice boundaries
  • Scenarios where the load of hitting a central auth server on every request is unacceptable

Bad for

  • Long-lived sessions — revocation is hard when the token is self-validating
  • Storing arbitrary user state — anything in the token grows it and is visible to anyone who holds it
  • Situations requiring immediate invalidation — token stays valid until it expires unless you maintain a revocation list (which removes the stateless benefit)

Mitigations

Secrets management patterns

Pattern Good for Weakness
Environment variables Small / early projects; container-native deployments with secret injection Visible to anything in the process (including /proc/<pid>/environ); rotating means redeploying; easy to leak via logs
Central secrets service Multi-team orgs with dynamic secrets, leases, rotation, audit Another service to operate; apps need a bootstrap credential to fetch their secrets
Cloud KMS-backed secrets store Cloud-native deployments where IAM already defines who can access what Couples you to the provider; cost-per-API-call can surprise at high volumes
External secrets operator Kubernetes workloads that want Secret objects materialized from a central store Still stores the secret in etcd (needs encryption-at-rest); pulls-in another control loop

Canonical fixes — side by side

SQL injection

Don't

cur.execute(f"SELECT * FROM users WHERE email = '{email}'")

Do

cur.execute("SELECT * FROM users WHERE email = %s", (email,))

Why: The driver handles escaping; the SQL is fixed syntactically

XSS

Don't

element.innerHTML = userInput

Do

element.textContent = userInput

Why: textContent treats input as text — no tags are parsed. innerHTML parses as HTML and will execute scripts inside

CSRF

Don't

Cookie: session=abc123

Do

Set-Cookie: session=abc123; SameSite=Lax; Secure; HttpOnly

Why: SameSite=Lax blocks cross-site POSTs from including the cookie; HttpOnly denies JS access; Secure forces TLS