The OWASP Top 10 gets referenced in every security conversation but rarely explained in terms that help developers actually write safer code. Most explanations are written for auditors — they describe what the vulnerability category is, not what it looks like in a pull request, what pattern produces it, or how to prevent it without restructuring your entire codebase. This guide takes the opposite approach. Each category gets a realistic code-level example and a concrete prevention pattern.
The 2021 edition is the current authoritative list. Some category positions shifted significantly from 2017, which matters if your team is still relying on older guidance.
A01: Broken Access Control
Broken access control moved to the top of the list in 2021, up from fifth place in 2017. It is now the most commonly found vulnerability in web applications, appearing in 94% of tested applications according to OWASP's dataset.
The pattern: an API endpoint that accepts a resource identifier from the request and returns the corresponding resource without verifying whether the authenticated user has permission to access it. Classic insecure direct object reference.
// Vulnerable
app.get('/api/orders/:orderId', authenticate, (req, res) => {
const order = db.getOrder(req.params.orderId); // no owner check
res.json(order);
});
// Fixed
app.get('/api/orders/:orderId', authenticate, (req, res) => {
const order = db.getOrderForUser(req.params.orderId, req.user.id);
if (!order) return res.status(403).json({ error: 'Forbidden' });
res.json(order);
});
Prevention: enforce authorization at the data access layer, not just at the route level. Every database query that returns user-owned data should include a user_id filter. No exceptions, no opt-out.
A02: Cryptographic Failures
Previously labeled "Sensitive Data Exposure," the rename to Cryptographic Failures more accurately describes the root cause. Developers expose sensitive data because they do not encrypt it, use weak encryption, or store secrets in places they should not be.
The three patterns we see most often in code review: MD5 or SHA1 used for password hashing (these are fast hashing algorithms, not password hashing functions — bcrypt, Argon2, or scrypt is the correct choice), symmetric encryption keys hardcoded in source files, and HTTP connections used for API calls that transmit tokens or PII.
A quick SAST scan on most production codebases will surface at least one instance of `md5()` applied to a password. In a Python codebase, that often looks like `hashlib.md5(password.encode()).hexdigest()`. The fix is always the same: `bcrypt.hashpw(password.encode(), bcrypt.gensalt())`. It is a one-line change with a one-sprint migration cost.
A03: Injection
SQL injection is the textbook example, but the category covers any injection that occurs when untrusted data is sent to an interpreter — LDAP injection, OS command injection, XPath injection, template injection. The 2021 list merged cross-site scripting (previously a separate entry) into this category.
The mechanics: an attacker provides input that the application passes to an interpreter without sanitization or parameterization. The interpreter executes the attacker-controlled portion as code.
Prevention is not complicated but requires discipline. Use parameterized queries everywhere, without exception. Use ORMs that parameterize by default. If you must build dynamic queries, whitelist the allowed values and validate against them. For shell commands, avoid `exec()`, `system()`, and similar calls entirely where possible; if unavoidable, use argument arrays rather than string concatenation.
Engineering note from Idris: The single most impactful thing a team can do to prevent injection vulnerabilities is establish a code standard that bans string interpolation in any call that touches a database or OS command. It takes one PR to add the linter rule; it prevents an entire class of vulnerabilities permanently.
A04: Insecure Design
New to the 2021 list. Insecure Design covers threat modeling failures — vulnerabilities that result from architectural decisions, not implementation bugs. No amount of code review will fix an insecure design; the architecture itself has to change.
Common examples: a password reset flow that relies on security questions (easily researched or guessed), a rate-limiting implementation that trusts client-supplied IP headers (easily spoofed), or a multi-tenant application that uses a single database schema without tenant isolation at the query level.
Addressing this at the team level means making threat modeling a required step in technical design reviews. It does not need to be a formal process — a 30-minute whiteboard session asking "how would an attacker abuse this feature?" before each significant feature ships catches the most common design flaws.
A05: Security Misconfiguration
Security misconfiguration is the category that DAST finds more reliably than SAST. Missing security headers, verbose error messages that expose stack traces, default credentials on third-party components, overly permissive CORS configurations — these are runtime properties, not code patterns.
A practical baseline: every production HTTP response should include `Content-Security-Policy`, `X-Frame-Options`, `X-Content-Type-Options`, and `Strict-Transport-Security` headers. Missing any of these is a common finding in security audits. Most web frameworks support middleware that adds these headers automatically — configuring it is a one-hour task that prevents several audit findings permanently.
A06: Vulnerable and Outdated Components
This is the dependency vulnerability category — using third-party libraries that contain known vulnerabilities. The Log4Shell vulnerability (CVE-2021-44228) in Log4j is the most visible recent example: a critical remote code execution vulnerability in a library used by hundreds of thousands of applications, including many that did not know they were using it transitively.
The solution is software composition analysis (SCA): automated scanning of your dependency tree against vulnerability databases (NVD, GitHub Advisory Database, OSV). Every major package manager ecosystem has tooling for this. The operational challenge is not detecting vulnerable dependencies — that is straightforward — but prioritizing which ones to update first when you have 40 flagged transitive dependencies and only one engineer-sprint available to address them.
A07: Identification and Authentication Failures
Previously "Broken Authentication." The category covers weak session management, missing multi-factor authentication, predictable credential recovery mechanisms, and credential stuffing vulnerabilities (accepting common or breached passwords).
The single highest-impact change for most web applications: implement account lockout and rate limiting on authentication endpoints. Brute force attacks against login endpoints are trivially easy to automate; a 5-attempt lockout with exponential backoff makes them impractical. If your authentication endpoint currently accepts unlimited login attempts, fixing that takes under two hours and prevents a common, low-sophistication attack class.
A08: Software and Data Integrity Failures
Another 2021 newcomer. This category covers deserializing untrusted data without verification, using CI/CD pipelines that accept unverified plugins or build artifacts, and auto-updating software from sources that are not integrity-verified.
The supply-chain security dimension has become significantly more relevant since the SolarWinds and 3CX incidents. Teams that auto-deploy from CI without verifying artifact signatures are trusting their entire CI/CD pipeline as a security boundary — an assumption that deserves explicit threat modeling.
A09: Security Logging and Monitoring Failures
Without logging, you cannot detect attacks in progress. Without monitoring, logs produce no alerts. Without alerts, a breach can run undetected for months.
A practical minimum: log all authentication events (successful and failed), all authorization failures, all changes to high-value records (user roles, payment methods), and all API errors at 4xx and 5xx. Route these logs to a centralized system with alerting rules on authentication failures (more than 10 per minute from a single IP) and authorization failures (more than 20 per hour per user). This does not require a SIEM — a Datadog or Grafana alert on these patterns provides meaningful detection capability.
A10: Server-Side Request Forgery (SSRF)
SSRF was added to the 2021 list based on industry data showing increasing prevalence, particularly in cloud-hosted applications where the internal metadata service (169.254.169.254 in AWS, Google Cloud, and Azure) is accessible from application servers and returns IAM credentials.
The pattern: an application accepts a URL from user input and makes an HTTP request to it (for example, a webhook URL validator, an image-fetching proxy, or a document import feature). An attacker provides an internal URL — the cloud metadata endpoint, an internal admin API, or a Redis instance without authentication — and the application fetches it and returns the response.
Prevention: never make server-side HTTP requests to URLs provided directly by user input without explicit allowlisting. If your use case requires fetching from user-provided URLs, resolve the URL to an IP address first and reject any IP in a private range (10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, 169.254.0.0/16, ::1).
Using the OWASP Top 10 as a Practical Checklist
The most effective way to use this list is not as a compliance checklist but as a review framework. Before merging any feature that involves user authentication, data access, external HTTP calls, file handling, or deserialization, walk through the relevant categories above. You will not catch every vulnerability this way, but you will catch the most common ones — and the most common ones are responsible for the vast majority of real-world breaches.
SAST tools can automate coverage for A01, A02, A03, A06, and part of A07. DAST tools cover A05, part of A07, and A10 more reliably at runtime. The categories that require human judgment — A04 (Insecure Design) and A09 (Logging and Monitoring) — are the ones where architectural review and operational discipline matter most. No scanner fully replaces a thoughtful engineer applying this list to the system they are building.