system-design

The OWASP Top 10 for System Designers

The ten most common ways web applications get breached, framed for design decisions rather than code review. Knowing these turns “is it secure?” from a shrug into a checklist.

Prerequisites: AuthN vs AuthZ, Sessions/JWT/OAuth Time to read: ~22 minutes


Why this list

The OWASP Top 10 is the industry-standard list of the most critical web application security risks, updated every few years from real breach data. 🚨 You don’t need to memorize all ten verbatim, but knowing the top few — and being able to say how your design defends against them — is a genuine interview and job skill. Security questions in design interviews almost always map to this list.

This chapter frames each as a design concern. (Ordered by the OWASP 2021 ranking, still current.)


1. Broken Access Control 🚨

#1, and the most common. A user does something they shouldn’t be allowed to.

The classic — IDOR: an authenticated user accesses another user’s data by changing an ID.

# ❌ Logged in, but no ownership check — change the ID, read anyone's order
GET /orders/124    returns order 124 regardless of who owns it

Design defences:


2. Cryptographic Failures

Sensitive data exposed through weak or missing encryption. (Formerly “Sensitive Data Exposure.”)

Failures: transmitting sensitive data over HTTP, storing passwords with fast/no hashing, weak ciphers, hardcoded keys, unencrypted PII at rest.

Design defences:


3. Injection 🚨

Untrusted input interpreted as a command. SQL injection is the archetype.

# ❌ SQL injection: input becomes part of the query
query = f"SELECT * FROM users WHERE name = '{user_input}'"
# user_input = "'; DROP TABLE users; --"  → catastrophe

Design defences:


4. Insecure Design

🚨 A category added to emphasize that security must be designed in, not bolted on. The others are implementation bugs; this is architectural — missing rate limiting, no defence against business-logic abuse, no threat modeling.

Design defences:

This is the category most relevant to system design interviews — it’s about designing for abuse.


5. Security Misconfiguration

Insecure defaults, unnecessary features, verbose errors, missing hardening.

Failures: default passwords, publicly-accessible admin panels, unnecessary open ports, verbose error messages leaking internals, permissive CORS, cloud storage buckets left public.

🚨 Misconfigured S3 buckets have exposed medical records, voter data, and credentials repeatedly — it’s one of the most common real-world data breaches, and it’s pure misconfiguration.

Design defences:


6. Vulnerable and Outdated Components

Using dependencies with known vulnerabilities. Your code may be perfect; a library with a public CVE is the way in.

🚨 Log4Shell (2021) — a critical flaw in the ubiquitous Log4j library — let attackers run arbitrary code on millions of servers via a log message. It’s the canonical example: your app is compromised through a dependency you didn’t write.

Design defences:


7. Identification and Authentication Failures

Weak authentication. (Formerly “Broken Authentication.”)

Failures: allowing weak/breached passwords, no MFA, weak session management, credential stuffing with no protection, exposed session IDs, no account-lockout.

Design defences:


8. Software and Data Integrity Failures

Trusting code or data without verifying integrity. Includes supply-chain attacks and insecure deserialization.

🚨 The SolarWinds attack compromised a software update that thousands of organizations installed, trusting it because it was signed and came from a legitimate vendor — a supply-chain attack.

Design defences:


9. Security Logging and Monitoring Failures

You were breached and didn’t notice. The average breach goes undetected for months.

Design defences:


10. Server-Side Request Forgery (SSRF)

🚨 Increasingly important, especially in the cloud. The server is tricked into making requests to places it shouldn’t.

# ❌ Fetches a URL the user provides — including internal ones
GET /fetch?url=http://169.254.169.254/latest/meta-data/iam/security-credentials/
#          → the cloud metadata endpoint → the server's IAM credentials!

Why it’s dangerous in the cloud: the metadata endpoint (169.254.169.254) hands out the instance’s credentials to anything that asks from the instance — so tricking the server into requesting it leaks cloud credentials. This is how several major breaches happened (Capital One, 2019).

Design defences:


Two more worth knowing (not in the top 10 but common)

XSS (Cross-Site Scripting) — injecting scripts that run in other users’ browsers, stealing sessions or data. Defences: output encoding (escape user content), Content-Security-Policy headers, HttpOnly cookies (so stolen scripts can’t read the session), and framework auto-escaping. (XSS is folded into Injection in 2021.)

CSRF (Cross-Site Request Forgery) — tricking a logged-in user’s browser into making an unwanted request. Defences: SameSite cookies (the modern primary defence), CSRF tokens, and checking the Origin/Referer header. → Sessions


The design mindset

🚨 The unifying principles behind all ten:

  1. Never trust input — from users, clients, or other services. Validate, parameterize, encode.
  2. Deny by default — access, network, and features are closed unless explicitly opened.
  3. Least privilege — every user, service, and credential gets the minimum.
  4. Defence in depth — multiple layers, so one failure isn’t a breach.
  5. Assume breach — design so a compromise has limited blast radius, and you’ll detect it.

🎙️ In an interview, when asked about security, don’t recite the list — apply it: “For this API, the main risks are broken access control, so I’d authorize every resource server-side; injection, so parameterized queries throughout; and since we fetch user-supplied URLs, SSRF — I’d allowlist destinations and block the metadata endpoint. Plus rate limiting on auth and TLS everywhere.”


⚖️ Trade-offs

Security always trades against convenience, latency, and development speed:

Control Protects against Cost
MFA Account takeover User friction
Rate limiting Brute force, abuse Legitimate heavy users may hit limits
Input validation Injection, XSS Development effort; false rejections
Least privilege Blast radius More access management
Logging/monitoring Undetected breach Cost, and a new PII exposure if done carelessly

The overarching point: security is a design concern, not a feature to add later, and the cost of skipping it is a breach.


In the real world


🚨 Interview traps


🎙️ Soundbites


🛠️ Try it

1. Exploit and fix each of the top three. Build a deliberately vulnerable endpoint and: exploit an IDOR by changing an ID, exploit SQL injection with ' OR '1'='1, and exploit SSRF by fetching the metadata endpoint. Then fix each — ownership check, parameterized query, URL allowlist. Doing the attacks yourself makes the defences stick far better than reading about them. (Use a deliberately vulnerable app like OWASP Juice Shop.)

2. Run OWASP Juice Shop. It’s an intentionally vulnerable app for practicing every item on this list, with a scoreboard. Genuinely the best way to learn these hands-on.

3. Scan your own dependencies. Run npm audit / snyk test / pip-audit on a real project. You’ll almost certainly find known vulnerabilities you didn’t know you had — that’s #6, live.

4. Check a bucket. Audit a cloud storage bucket’s permissions. Is “block public access” on? Would a misconfiguration expose it? This is the most common real breach, and checking takes two minutes.


Check yourself

1. Why is broken access control the #1 web security risk? Because it's both extremely common and directly damaging, and the vulnerable code looks correct. The classic form — IDOR — is an endpoint that checks the user is authenticated but not that they're authorized for the *specific* resource requested, so changing an ID in the URL returns someone else's data. It's common because authentication is usually done centrally and well, but per-resource authorization is scattered across every endpoint and easy to forget on any one of them — and it passes casual review because there *is* a logged-in user. It's damaging because a single missing ownership check can expose every record in a table. The defence is to authorize every resource access server-side and deny by default, treating authentication and per-object authorization as separate, both-required steps.
2. How do parameterized queries prevent SQL injection? By separating the query structure from the data. In a string-concatenated query, user input becomes part of the SQL text, so input like `'; DROP TABLE users; --` is parsed as SQL commands. A parameterized query sends the query template (`SELECT * FROM users WHERE name = ?`) and the values *separately* to the database; the database compiles the template first, then treats the parameters purely as data to substitute — they can never be interpreted as SQL syntax, no matter what characters they contain. So the malicious input becomes a literal search for a username containing that odd string, which simply finds nothing, rather than executing. This single practice eliminates SQL injection entirely, which is why "always parameterize, never concatenate" is the rule.
3. Why is SSRF especially dangerous in cloud environments? Because of the cloud metadata endpoint. Cloud platforms expose an internal endpoint (typically `169.254.169.254`) that hands out the instance's IAM credentials to anything requesting it *from the instance* — it's how the instance authenticates to cloud services. If an attacker can trick your server into making a request to a URL they control (an SSRF), they can point it at the metadata endpoint, and the server dutifully fetches its own cloud credentials and returns them, giving the attacker the instance's permissions across your entire cloud account. This is exactly how the Capital One breach (100M records) happened. Defences: validate and allowlist outbound URLs, block internal IP ranges including the metadata address, use IMDSv2 (which requires a token that naive SSRF can't obtain), and apply least privilege so the instance's role can't do much even if leaked.
4. What does "Insecure Design" add that the other categories don't cover? The recognition that security is architectural, not just a matter of implementation bugs. The other categories are mostly *flaws in how something was built* — a missing check, a concatenated query, a misconfiguration. Insecure Design is about *what wasn't designed at all*: no rate limiting on sensitive operations, no protection against business-logic abuse (ordering negative quantities, applying a coupon infinitely, racing a balance check), no threat modeling, no defence in depth. You can implement every line perfectly and still be insecure if the design never accounted for how an attacker would abuse the intended functionality. This is the category most relevant to system design interviews, because it's about designing *for* abuse — asking "how would someone exploit this feature?" during design rather than patching bugs afterward.
5. What are the unifying principles behind all ten OWASP risks? Five design principles. **Never trust input** — from users, clients, or other services; validate, parameterize, and encode everything (covers injection, XSS, SSRF). **Deny by default** — access, network reachability, and features are closed unless explicitly opened (covers access control, misconfiguration). **Least privilege** — every user, service, and credential gets the minimum needed, so a compromise is contained (covers access control, SSRF blast radius, cryptographic key exposure). **Defence in depth** — multiple independent layers so one failure isn't a breach (covers everything). **Assume breach** — design so that when a compromise happens it has limited blast radius, and ensure you'll actually detect it (covers logging/monitoring, integrity). In an interview, applying these to the specific system — rather than reciting the list — is what demonstrates real security thinking.

Further reading