Structured analysis of who could attack, how, and what to protect.
STRIDE
Microsoft's 6-category threat taxonomy. Walk every component, asset, and trust boundary through each category.
Letter
Threat
Defense property
S
Spoofing identity
Authentication
T
Tampering with data
Integrity
R
Repudiation
Non-repudiation (signed audit log)
I
Information disclosure
Confidentiality
D
Denial of service
Availability
E
Elevation of privilege
Authorization
STRIDE × component matrix
Data Flow Diagrams & Trust Boundaries
Draw external entities, processes, data stores, data flows. Trust boundaries cross where credentials change (browser→server, service→DB, public→private VPC). Every boundary crossing gets a STRIDE pass.
Simple web-app threat model (data flow + trust boundaries)
Attack Trees
Root = attacker's goal. Children = necessary conditions (AND/OR). Leaves = concrete attacks. Use to identify the cheapest path for the attacker and harden that first.
Attack tree: steal admin credentials
MODULE 2
Cryptography Essentials
What to use, when. Never roll your own.
Symmetric Encryption
Same key encrypts and decrypts. Use for data at rest and any large payload. Modern pick: AES-256-GCM or ChaCha20-Poly1305 — both are AEAD (authenticated encryption with associated data), so they encrypt AND integrity-check.
# Python: correct AES-GCM with random nonce
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
import os
key = AESGCM.generate_key(bit_length=256)
aes = AESGCM(key)
nonce = os.urandom(12) # 96-bit nonce — NEVER reuse with same key
ct = aes.encrypt(nonce, b"secret", associated_data=b"context-v1")
pt = aes.decrypt(nonce, ct, b"context-v1")
Asymmetric (Public-Key)
Different keys for encrypt/decrypt or sign/verify. Slow → use for key exchange and signatures, not bulk data.
RSA-2048+ — widely supported, slow. Avoid for new code.
ECDSA P-256 — fast, standard in TLS / JWT / code signing.
Ed25519 — fastest, simpler impl, deterministic signatures, preferred for new code.
X25519 — key agreement (Diffie-Hellman) counterpart to Ed25519.
Hash Functions
One-way, fixed-length digest. Use cases:
Integrity: SHA-256 / SHA-3-256 / BLAKE3 — any modern cryptographic hash.
Password storage: use a KDF, not a plain hash. See below.
Content addressing: BLAKE3 for speed; SHA-256 for ecosystem compat.
Key Derivation & Password Hashing
Passwords are low-entropy — a plain SHA-256 falls to GPU brute-force. Use a slow, memory-hard KDF.
Argon2id — modern winner of PHC. Default for new systems. Parameters: m=64 MB, t=3, p=1 is a safe baseline.
scrypt — memory-hard, well supported.
bcrypt — cost 12+. Capped at 72-char password (truncates longer).
PBKDF2-HMAC-SHA-256 — fallback if FIPS required. iter ≥ 600k (2023 OWASP).
# Python: Argon2id
from argon2 import PasswordHasher
ph = PasswordHasher(time_cost=3, memory_cost=64*1024, parallelism=1)
hashed = ph.hash("user-password") # store this
ph.verify(hashed, "user-password") # raises on mismatch
if ph.check_needs_rehash(hashed): # after param bump
hashed = ph.hash("user-password")
Slow-on-purpose: attacker with GPU farm sees O(1) hashes/sec; legitimate login path is fine.
Password verification flow (Argon2id)
HMAC & Signatures
HMAC = keyed-hash MAC. Use for integrity + authenticity when both sides share a symmetric secret (webhooks, cookies, JWT HS256). Never use plain hash(secret + data) — length-extension attacks.
Digital signatures = asymmetric HMAC. Signer holds private key, verifiers hold public. Ed25519 / ECDSA for new code.
alg: "none" — library MUST reject. Historical CVE.
Algorithm confusion — server expects RS256 but attacker sends HS256 signed with the server's public key treated as secret. Pin algorithm; don't trust header alg.
Weak HS256 secret — brute-forceable. Use 256-bit random or switch to RS256/Ed25519.
Long TTL — hours/days without refresh is bad. Access ≤ 15m.
Storing in localStorage — accessible to any XSS. Prefer HttpOnly cookie with SameSite=Lax.
No audience / issuer check — token from service A accepted by service B.
OAuth delegates authorization. Auth-code + PKCE is the right flow for all public clients (SPA, native, mobile). Client credentials for machine-to-machine.
OAuth 2.0 Authorization Code + PKCE
OIDC & SAML
OIDC = OAuth + id_token (JWT with user claims). Modern federation default.
SAML 2.0 = XML-based SSO, still dominant in enterprise. More complex; beware XML signature wrapping attacks. Prefer OIDC for new deployments.
MODULE 4
Authorization
What is the caller allowed to do?
RBAC
Users → Roles → Permissions. Simple, widely supported, doesn't express resource-level rules well ("Alice can edit only her docs").
ABAC
Attribute-based. Policy = function of (subject attrs, resource attrs, action, environment). Expressive; needs a policy engine. Standards: XACML (complex), Rego (OPA), Cedar (AWS).
Relationship-based — who has what relation to what. Scales to billions of tuples. Pattern: tuples (user, relation, object) with indirection via usersets ("user:alice is in group:eng"). Used by Google Drive, GitHub, Figma.
Rotate service credentials; prefer workload identity (AWS IRSA, GCP WIF) over long-lived keys.
Separate read vs write keys/roles.
MODULE 5
Web Vulnerabilities — OWASP Top 10 Defense
Concepts + correct defenses. Defense-only (no exploit recipes).
A03 Injection — SQL / NoSQL / Command
Cause: concatenating untrusted input into a query. Defense: parameterized statements. Never build a query via string formatting.
# Python — parameterized (safe)
cur.execute("SELECT * FROM users WHERE email = %s AND active = %s", (email, True))
# NEVER this:
# cur.execute(f"SELECT * FROM users WHERE email = '{email}'")
For NoSQL (MongoDB), reject operator objects in user input: validate shape with a schema (pydantic, joi). For shell commands, use subprocess.run([...]) with a list, never shell=True.
A03 XSS — Cross-Site Scripting
Reflected — input echoed in response. Defense: context-aware output encoding.
Stored — input saved then rendered for others. Same defense, at render time.
DOM-based — client JS writes untrusted data to sink (innerHTML, document.write).
Defense stack (all layers):
HTML-encode on output. Use framework auto-escape (React's JSX, Jinja2 autoescape, Handlebars).
Never inject untrusted data into a script, style, onclick, or href=javascript:. If needed, use Trusted Types (MDN).
CSP (Content-Security-Policy) header with default-src 'self'; script-src 'nonce-xxx' 'strict-dynamic'. Blocks inline <script> and eval.
HttpOnly + Secure + SameSite=Lax on session cookies → stolen DOM can't reach them.
A01 CSRF — Cross-Site Request Forgery
Attacker's site triggers a state-changing request to yours using the victim's cookies. Defense:
SameSite=Lax cookies (default in modern browsers) block most cross-site POSTs.
CSRF token: synchronizer-token pattern — server issues a random token, client echoes in a custom header; server compares constant-time.
Check Origin / Referer header as a second layer.
For pure-JSON APIs using Bearer tokens (no cookies), CSRF is not an issue.
A10 SSRF — Server-Side Request Forgery
User input becomes a URL the server fetches. Attacker targets 169.254.169.254 (cloud metadata) or internal services. Defense:
Allow-list of host/port/scheme.
Resolve DNS yourself, reject private-range IPs (RFC1918 + link-local + loopback). Beware TOCTOU — re-resolve and pin.
Disable redirects OR re-check each hop.
Prefer a dedicated egress proxy with metadata-service blocked.
On AWS: enforce IMDSv2 (session-token required) so a blind GET can't read creds.
A01 IDOR — Insecure Direct Object Reference
Endpoint trusts an object ID from the request without authz check. Defense: every object fetch must check the caller owns/has-access to the object. Don't rely on "UUID is random enough" — that's obscurity, not security.
# FastAPI: correct ownership check
@app.get("/doc/{doc_id}")
def get_doc(doc_id: str, user = Depends(current_user)):
doc = db.get(doc_id)
if doc is None or not can_read(user, doc):
raise HTTPException(404) # don't leak existence
return doc
A08 Insecure Deserialization
pickle.loads, yaml.load, Java ObjectInputStream on untrusted input = arbitrary code exec. Use JSON with a strict schema (pydantic, jsonschema). If you must use pickle, sign payloads with HMAC and verify before loading.
Other OWASP Hits
A02 Crypto Failures — weak TLS config, hard-coded keys. Use Mozilla SSL Config Generator.
A04 Insecure Design — threat-model at design time (mod1).
A05 Misconfig — defaults (default creds, open S3 bucket, open CORS). Scan with checkov / tfsec.
Tells browser: only HTTPS for this host for 1 year. Submit to hstspreload.org to bake into browser source — no first-request window.
Cert Pinning & CT
Pinning = app hard-codes accepted cert or public-key hash. Kills MITM via rogue CA but brittle on rotation. Mobile apps common, web is deprecated (HPKP dead).
Certificate Transparency = public append-only log of issued certs. Monitor for unauthorized certs on your domain (crt.sh, Cert Spotter).
TLS certificate chain
MODULE 7
Secrets Management
Keep secrets out of code, config, and logs.
Never Commit Secrets
Pre-commit hook: gitleaks / detect-secrets.
Rotate anything ever committed — even if rolled back, it's in history.
.env files: gitignored, but still unencrypted on disk. OK for dev, NOT prod.
Kubernetes: external-secrets operator syncs from a manager into K8s Secret.
Envelope Encryption
Encrypt data with a DEK (data encryption key). Encrypt the DEK with a KEK held by KMS. Store {ciphertext, wrapped_DEK}. Rotation: re-wrap DEK by KEK rotation — don't re-encrypt data.
Envelope encryption
Rotation
All credentials have a max age. Short-lived tokens via OIDC federation (AWS IRSA, GCP WIF) > long-lived access keys.
Design for overlap: new secret valid before old retired.
Alert on un-rotated secrets past threshold.
MODULE 8
Secure Coding Patterns
Defaults that prevent whole classes of bugs.
Validate Input, Encode Output
Validation is allow-list (positive pattern). Encoding is context-aware: HTML body ≠ HTML attr ≠ URL ≠ JSON ≠ script. Use the framework's escaper for the target context.
Rate Limiting
Token bucket per (IP, account, endpoint). Prevents brute-force + abuse. See system-design mod9 for token-bucket viz.
Dependency Scanning
SCA (Software Composition Analysis) — match lockfiles against CVE DB. Snyk, GitHub Dependabot, npm audit, pip-audit, OSV-scanner.
SAST — static analysis on your source. Semgrep, CodeQL.
DAST — probe the running app. ZAP, Burp.
SBOM — Software Bill of Materials in SPDX/CycloneDX format.
Safe Deserialization
JSON only, with a schema. Never pickle.loads or yaml.load on untrusted data. YAML: use yaml.safe_load.
Logging Hygiene
Never log: passwords, tokens, full PAN, full SSN, keys, session cookies.
Mask or hash sensitive fields. Include request ID for trace correlation.
Structured JSON logs; centralize in SIEM.
MODULE 9
Network & Zero Trust
Don't trust the network — authenticate every request.
Beyond Perimeter
Legacy model: hard shell (firewall) around soft interior. Breaks once attacker is inside. Google's BeyondCorp model: every request authenticated + authorized regardless of origin network.
Zero Trust Primitives
Identity-aware proxy (IAP) between client and app. Terminates TLS, authenticates user via OIDC, forwards identity headers to backend.
Service mesh mTLS (Istio, Linkerd) — every intra-cluster call authenticated + encrypted by the data-plane proxy.
Workload identity — SPIFFE SVIDs for services; no shared secrets.
Egress control — explicit allow-list of external domains a workload may reach.
BeyondCorp-style request flow
Network Segmentation
Even in zero-trust, minimize blast radius: separate VPCs per environment (prod/stage/dev), private subnets for data stores, PrivateLink for cross-account. Public subnet only for LB/NAT.
MODULE 10
Interview Cheat Sheet
Questions you WILL be asked.
"How does HTTPS work?"
Client hello → server hello + cert chain → client verifies chain against trust store → ECDHE key agreement → derive session keys → AEAD cipher (TLS 1.3 = 1-RTT). See networking mod6.
"Design an auth system."
Identify users, workloads, and federation. Argon2id for passwords + WebAuthn for MFA. Short-lived access JWT (RS256, 15 min) + opaque refresh token stored server-side. OAuth PKCE for public clients. Session revocation via refresh-token blacklist or JWT + short TTL.
"How to store passwords?"
Argon2id(m=64MB, t=3, p=1) — library stores salt inside hash. Never plain hash. Rate-limit login. Check against HIBP on set.