The OWASP Top 10 isn’t just a checklist — it’s a snapshot of how real-world breaches happen. The 2021 revision reorganized the previous list around root causes rather than symptoms, which makes it a much better map for both developers and security engineers. This guide walks through each category with reproducible examples, fixes, and detections.

A01:2021 — Broken Access Control

Jumped to #1 from #5. Includes IDOR, missing function-level checks, and forced browsing.

Example (IDOR):

GET /api/invoices/4178 HTTP/1.1
Authorization: Bearer ey...

If the server returns the invoice without verifying ownership, any authenticated user can iterate the ID parameter and steal records.

Fix: Enforce object-level authorization on every endpoint. Use a deny-by-default policy and a single authorization layer — not scattered checks. CVSS 8.2 · HIGH

A02:2021 — Cryptographic Failures

Renamed from “Sensitive Data Exposure” to highlight the root cause. Common patterns:

  • TLS not enforced (HSTS missing)
  • Hard-coded keys in source
  • Weak algorithms (MD5, SHA-1 for passwords, ECB mode)
  • Hashing passwords without a slow KDF

Fix: Argon2id for passwords, AES-GCM for symmetric encryption, X25519 + ChaCha20-Poly1305 for modern transport.

A03:2021 — Injection

Now includes XSS as a subtype. SQL injection, OS command injection, LDAP injection, and template injection all sit here.

# Vulnerable — string concatenation
cursor.execute(f"SELECT * FROM users WHERE id = {request.args['id']}")

# Safe — parameterized query
cursor.execute("SELECT * FROM users WHERE id = %s", (request.args['id'],))
ORMs are not a guarantee ORM raw(), extra(), and string-built queries reintroduce injection. SQLAlchemy, Django ORM, and Sequelize have all shipped CVEs from misused unsafe APIs.

A04:2021 — Insecure Design

New category covering threat modeling failures and missing security requirements. The classic example: an account-recovery flow that never rate-limits, enabling enumeration and credential brute force.

Fix: Threat-model every feature before coding. STRIDE works; PASTA scales better for large organizations.

A05:2021 — Security Misconfiguration

Default credentials, verbose errors, unnecessary features enabled, missing security headers. The XML External Entities (XXE) category from 2017 was merged here.

# Headers every modern app should set
Strict-Transport-Security: max-age=31536000; includeSubDomains
Content-Security-Policy: default-src 'self'; script-src 'self'
X-Content-Type-Options: nosniff
Referrer-Policy: strict-origin-when-cross-origin
Permissions-Policy: geolocation=(), microphone=(), camera=()

A06:2021 — Vulnerable and Outdated Components

Equifax, Log4Shell, and every supply-chain breach lives here. Track inventory with an SBOM, scan with Dependabot/Renovate/Snyk, and patch within agreed SLAs (critical: 7 days, high: 30 days).

A07:2021 — Identification and Authentication Failures

Predictable session IDs, missing MFA, credential stuffing without rate limits, password reset tokens with no expiry.

Fix: Implement WebAuthn or TOTP for MFA. Use credential-stuffing prevention services. Pin session cookies to TLS via the __Secure- / __Host- prefix.

A08:2021 — Software and Data Integrity Failures

New category. Covers insecure deserialization, untrusted CI/CD pipelines, and auto-updates without signature verification.

# Never deserialize untrusted input
import pickle
pickle.loads(request.body)   # RCE waiting to happen

Use JSON or schema-validated formats. Sign and verify auto-update payloads with code signing.

A09:2021 — Security Logging and Monitoring Failures

Average dwell time for an undetected breach is still over 200 days. Log:

  • Authentication successes and failures
  • Access control failures
  • Server-side input validation failures
  • Privileged operations

Forward to a SIEM (Splunk, Elastic, Sentinel) and alert on anomalies. CVSS 3.7 · LOW

A10:2021 — Server-Side Request Forgery (SSRF)

Promoted to its own category after a wave of cloud-metadata SSRFs (Capital One 2019, SSRF chains in countless bug bounties). Any feature that fetches a URL is suspect.

Fix:

  1. Validate against an allow-list of domains/IPs.
  2. Block private RFC1918 ranges and link-local (169.254.169.254) at the egress firewall.
  3. Disable HTTP redirects in the fetch library or follow them safely.
  4. Disable IMDSv1 on AWS; require IMDSv2 with hop limit 1.

How to Use This List

Treat the Top 10 as the floor, not the ceiling. Real maturity means going beyond it — ASVS L2/L3 for AppSec teams, OWASP MASVS for mobile, and OWASP API Security Top 10 for API-heavy products.

References