Mert Özen Further With Every Line
Security Web Security (OWASP Top 10) — #19

OWASP Top 10: From 2017 to 2021, from Symptoms to Root Causes

Mert Özen Nov 6, 2026 7 min 5 views
OWASP Top 10: From 2017 to 2021, from Symptoms to Root Causes

We look at how the 2017 list was rebuilt in 2021: from Sensitive Data Exposure to Cryptographic Failures, Broken Access Control rising to number one, XSS joining Injection, and the arrival of Insecure Design and SSRF. A small .NET example shows that SQL parameters and HTML encoding answer the same root cause, and we walk through the 2021 methodology with its numbers.

Version note: This post compares the OWASP Top 10:2017 with the OWASP Top 10:2021; today's equivalents follow the OWASP Top 10:2025, and the code was tested with .NET 10.

Guess First

Third place in the 2017 list was Sensitive Data Exposure. The 2021 list has no item with that name. Do you think it disappeared, was folded into another category, or continued under a new name? If it continued, in which position?

The answer: it moved up one place as A02:2021 Cryptographic Failures. The reason for the rename is the main idea of this post: OWASP said that "data leaked" describes a symptom, while the root cause is usually missing or wrong cryptography. That change of view is the key to reading the 2021 list. This time the code example is small and entirely on the defensive side; still, let me repeat the general rule: only try these techniques on systems you own or have written permission to test.

The 2021 List at a Glance

The OWASP Top 10:2021 was published in September 2021. The project repository has an "It's been released" commit dated September 24, 2021. Let's read the list together with its 2017 counterparts.

A01 Broken Access Control rose from 2017's A5 to number one. A02 Cryptographic Failures is the new name of A3 Sensitive Data Exposure. A03 Injection gave up first place and absorbed XSS, which was 2017's A7. A04 Insecure Design is completely new. A05 Security Misconfiguration moved up one place from A6 and took in 2017's A4, XXE.

A06 Vulnerable and Outdated Components is the new name of A9 Using Components with Known Vulnerabilities. A07 Identification and Authentication Failures is the new name of A2 Broken Authentication. A08 Software and Data Integrity Failures is a new category that absorbed 2017's A8, Insecure Deserialization. A09 Security Logging and Monitoring Failures is the new name of A10 Insufficient Logging & Monitoring. A10 Server-Side Request Forgery (SSRF) is a new entry.

The numbering changed too: 2017's A1 became A01 in 2021. It looks like a small detail, but it matters when you search old documents; in the next post we write a small tool for that.

From Symptoms to Root Causes

Some of the 2017 categories named an outcome ("sensitive data was exposed") or a technology ("XML"). In 2021 OWASP built the categories around root causes wherever possible. A doctor analogy works well: fever is a symptom, while the treatment targets the infection. "Sensitive Data Exposure" looked at the fever; "Cryptographic Failures" looks at the infection.

The practical result of this approach: the category name tells you where the fix belongs. "Cryptographic Failures" brings TLS settings, password hashing and key management to mind. With "Sensitive Data Exposure" it was not clear where to look. XXE joining Security Misconfiguration follows the same logic: the problem is not the XML format but a dangerous parser feature left switched on.

Why Broken Access Control Is Number One

Broken Access Control, fifth in 2017, went to the top in 2021 and stayed there in 2025. My reading: as applications split into APIs, every endpoint became a separate door. Guessable ids such as /orders/42 travel in URLs, and single-page apps hide a button in the UI while the server still accepts the same request. If the authorization decision is not made on every endpoint, on the server, by looking at the resource itself, the hole is unavoidable.

We saw this on the Bookstore API in posts 3 and 4 of this series: reading another user's order (IDOR), a missing check on an admin endpoint, and therefore resource-based authorization. The rise in 2021 says this is the most common and most easily overlooked problem.

Injection Gets Wider: XSS Joins In

XSS joining Injection may look odd at first; one happens in the database, the other in the browser. But the root cause is the same: data reaches an interpreter as part of the code. The defense comes from the same pattern too: don't mix data with code, carry it by the rules of the context it goes into.

using System.Text.Encodings.Web;
using Microsoft.Data.Sqlite;

using SqliteConnection connection = new("Data Source=:memory:");
connection.Open();
using (SqliteCommand setup = connection.CreateCommand())
{
    setup.CommandText = """
        CREATE TABLE reviews (id INTEGER PRIMARY KEY, author TEXT, body TEXT);
        INSERT INTO reviews (author, body) VALUES ('ada', 'Great book'), ('alan', 'Too long');
        """;
    setup.ExecuteNonQuery();
}

string author = "ada";
string body = "<b>Great</b> book";

// SQL: the value travels as a parameter, never as part of the query text
using (SqliteCommand query = connection.CreateCommand())
{
    query.CommandText = "SELECT COUNT(*) FROM reviews WHERE author = $author";
    query.Parameters.AddWithValue("$author", author);
    Console.WriteLine($"SQL rows for '{author}': {query.ExecuteScalar()}");
}

// HTML: the value is encoded for the context it is written into
Console.WriteLine($"HTML output: <p>{HtmlEncoder.Default.Encode(body)}</p>");
$ dotnet run
SQL rows for 'ada': 1
HTML output: <p>&lt;b&gt;Great&lt;/b&gt; book</p>

In both cases the value from the user cannot touch the interpreter's grammar. On the SQL side, the parameter carries the value separately from the query text; on the HTML side, HtmlEncoder turns <b> from a tag into text. Razor's @ expression and Angular's interpolation do the second job for you; the parameterized APIs of Dapper and EF Core do the first. Since both defenses can be described in the same sentence, it makes sense for them to sit under one category.

The Newcomers: Insecure Design and SSRF

A04:2021 Insecure Design became the most unusual item on the list. While the other categories are mostly "a line written wrong in the code", this one says "a feature written correctly but designed wrong". Letting a coupon be used without limit, guessable steps in a password reset flow, unlimited attempts: the code may be flawless while the hole is in the design. The answer to this category is not a code pattern but a process: threat modeling, abuse cases and secure design patterns. It was also the category with the most CWEs (40) in 2021.

A10:2021 SSRF, by contrast, was the narrowest category: a single CWE (CWE-918). The server sends a request itself to a URL the user supplied, and resources in the internal network become reachable that way. The risk grew as internal services and metadata endpoints spread in cloud environments. The reason such a narrow category made the list is the community survey we look at below.

A Different Methodology

According to the figures OWASP published, the 2021 list is based on data from more than 500,000 applications. The number of CWEs analyzed went from about 30 in the previous edition to almost 400. About 125,000 CVEs were mapped to 241 unique CWEs. Categories contain 19.6 CWEs on average; the narrowest is A10 with a single CWE, the widest is A04 with 40.

Eight of the ten categories came from data and two from the community survey: A09 Security Logging and Monitoring Failures and A10 SSRF. The reason is simple: test data looks backward. If testing tools don't catch a vulnerability type well yet, it shows up rarely in the data, while people working in the field may have run into it long ago. The survey is the way to bring what the data cannot see onto the list. The same method continued in 2025, as we will see in the next post.

Common Pitfalls

The first is mistaking a rename for a change of content. A07 Identification and Authentication Failures covers largely the same ground as 2017's Broken Authentication; but A08 Software and Data Integrity Failures is not just a new name, it is a wider category that took in deserialization. When mapping an old item, look at its content, not its name.

The second is reading the order as a risk ranking. You cannot say A04 is "more dangerous" than A05; the order is a general prioritization OWASP made from data and the survey. The most important item for your application is the one your threat model points to.

The third is reading XSS joining Injection as "XSS lost its importance". As we saw in the previous post, XSS is the most crowded CWE in Injection today.

The fourth is trying to close Insecure Design with a tool. SAST or DAST finds little in this category; the question "how would I abuse this feature?" asked in a design meeting finds much more.

Checklist

When updating a security document written against 2017, map every item to its 2021 (and from there 2025) counterpart; move Sensitive Data Exposure under Cryptographic Failures, XXE under Security Misconfiguration, XSS under Injection and Insecure Deserialization under Software and Data Integrity Failures. Write numbers with their year. Use parameters in SQL and context-appropriate encoding in HTML; check both with the same code review question: "does data reach an interpreter as code?" When designing a new feature, write down a list of abuse cases. Think about SSRF everywhere the server sends requests outward.

A Small Experiment

★☆☆ In the program above, set body to Tom & Jerry "quoted" and see how HtmlEncoder converts & and the quotes. Then try the same value with JavaScriptEncoder.Default.Encode and note the difference.

★★☆ Ask a design question for the review endpoint in the Bookstore API: how many reviews can the same user write for the same book? Put the rule into code and write a test showing that a request breaking the rule is rejected.

★★★ Open the security documentation (README, threat model, PR template) of your own project or an open source one and map its 2017 category references to their 2021 counterparts. How many are just renames, and how many moved to another category?

In the next post we move from 2021 to 2025: SSRF joining A01, the components problem growing into the supply chain, Misconfiguration rising to second place, the new A10, and updating documents written with the old numbers using a tool.