Back to blog
Security & Compliancebeginner

OWASP Top 10 — The Most Common Security Mistakes Developers Make

A plain-English breakdown of the OWASP Top 10 2021 — what each vulnerability is, how attackers exploit it, and exactly how to prevent it in your code.

LearnixoApril 15, 20269 min read
SecurityOWASPVulnerabilitiesWeb SecurityBest Practices
Share:𝕏

What Is OWASP and Why Should You Care?

The Open Worldwide Application Security Project (OWASP) is a non-profit foundation that publishes free, open security resources for developers. Their Top 10 list is the industry's most-referenced document on web application security — a consensus of the most critical, most common security risks.

The 2021 edition is the current version. It is not a list of exotic edge cases. Every item on it has caused real breaches, real data loss, and real legal consequences at real companies.

If you know these 10 categories, you will catch most security problems before they ship.


A01 — Broken Access Control

What It Is

Access control means: "this user can do X, but not Y." Broken access control means your code does not actually enforce that rule. A logged-in user can access another user's data by changing a URL parameter. A regular user can reach an admin endpoint.

Real-World Example

A user sees their invoice at /invoices/1042. They change the URL to /invoices/1041. If the server does not verify that they own invoice 1041, they just read someone else's data. This is called an Insecure Direct Object Reference (IDOR) and it is the most reported vulnerability on bug bounty platforms.

Prevention

  1. Deny by default. Access should be denied unless explicitly granted — not the other way around.
  2. Check ownership server-side on every request. Never trust the client to enforce rules.
  3. Use a centralized authorization check. Do not scatter permission checks across controllers.
C#
// BAD — trusts whatever ID the user sends
var invoice = db.Invoices.Find(invoiceId);
return Ok(invoice);

// GOOD — verifies the current user owns this resource
var invoice = db.Invoices
    .Where(i => i.Id == invoiceId && i.OwnerId == currentUser.Id)
    .FirstOrDefault();
if (invoice == null) return Forbid();
return Ok(invoice);

A02 — Cryptographic Failures

What It Is

Sensitive data (passwords, credit cards, health records, tokens) is exposed because it is stored or transmitted without proper encryption — or with weak, outdated encryption.

Real-World Example

A company stores passwords as MD5 hashes. An attacker breaches the database and dumps the hashes. Because MD5 is fast, they crack 80% of passwords in hours using precomputed rainbow tables. The passwords are now plaintext.

Prevention

  1. Use HTTPS everywhere. Encrypt data in transit.
  2. Use bcrypt, Argon2, or PBKDF2 for passwords — never MD5, SHA1, or plain SHA256. (See the password hashing article.)
  3. Encrypt sensitive database columns (SSNs, card numbers, etc.) using AES-256. Do not store what you do not need.

A03 — Injection

What It Is

Injection happens when untrusted input is sent to an interpreter (SQL, shell, LDAP, XML) and the interpreter executes it as a command instead of treating it as data.

Real-World Example

A login form sends username and password to a SQL query built by string concatenation:

SQL
-- Attacker enters: username = ' OR '1'='1
SELECT * FROM users WHERE username = '' OR '1'='1' AND password = '...'
-- This returns ALL users. First row is admin. Attacker is logged in.

This is SQL Injection, the most well-known type. The same principle applies to shell commands, LDAP queries, and NoSQL query operators.

Prevention

  1. Use parameterized queries / prepared statements — never build SQL by concatenating strings.
  2. Use an ORM (Entity Framework, Dapper) — they parameterize for you by default.
  3. Validate and sanitize input — reject input that does not match the expected format.
C#
// BAD — SQL injection vulnerability
var query = $"SELECT * FROM Users WHERE Username = '{username}'";

// GOOD — parameterized query
var user = db.Users.Where(u => u.Username == username).FirstOrDefault();

A04 — Insecure Design

What It Is

A03 is about implementation bugs. A04 is about design flaws — the feature was built correctly but the feature itself is insecure. No amount of code fixes can remedy a fundamentally insecure design.

Real-World Example

A password reset flow sends a 4-digit numeric code by SMS. The code is valid for 10 minutes. An attacker writes a script that tries all 10,000 combinations. If the system does not rate-limit attempts, the account is compromised in minutes.

The bug is not a typo in the code — the design itself allowed brute force.

Prevention

  1. Threat model before you build. Ask: "What could go wrong? How would an attacker abuse this feature?"
  2. Enforce rate limiting and lockouts on sensitive flows (login, password reset, OTP entry).
  3. Apply the principle of least privilege in the design — give users only the access they actually need.

A05 — Security Misconfiguration

What It Is

The software is correct but it is configured insecurely. Default credentials, unnecessary features enabled, verbose error messages, missing security headers, open S3 buckets.

Real-World Example

A developer deploys a new service and forgets to remove the default admin credentials (admin / admin). The admin panel is internet-accessible. An attacker tries the defaults and is in.

Or: a web server returns detailed stack traces including file paths, library versions, and database connection strings — giving an attacker a roadmap.

Prevention

  1. Disable default accounts and change default passwords before deployment.
  2. Return generic error messages to users. Log the details server-side.
  3. Run security header checks with tools like securityheaders.com. Review cloud storage bucket permissions.

A06 — Vulnerable and Outdated Components

What It Is

Your application uses third-party libraries, frameworks, or runtimes that have known security vulnerabilities. If you do not update them, attackers exploit published CVEs.

Real-World Example

The 2017 Equifax breach exposed 147 million people's data. The cause: a known vulnerability in Apache Struts (CVE-2017-5638) that had a patch available for months. Equifax had not updated their dependency.

Prevention

  1. Audit your dependencies regularly. Use dotnet list package --vulnerable or npm audit.
  2. Enable automated dependency update tools (Dependabot, Renovate) — they open PRs when vulnerabilities are discovered.
  3. Remove unused dependencies — every library is an attack surface.

A07 — Identification and Authentication Failures

What It Is

Weaknesses in authentication — how you verify who a user is. Weak passwords allowed, no brute-force protection, session tokens not invalidated on logout, insecure "remember me" implementation.

Real-World Example

A site allows unlimited login attempts with no lockout. An attacker takes a list of 10 million username/password pairs from another breach and tries them all (credential stuffing). 2% success rate = 200,000 accounts compromised.

Prevention

  1. Enforce Multi-Factor Authentication (MFA) — even if one factor is compromised, the account is still protected.
  2. Rate-limit login attempts and implement temporary account lockouts.
  3. Invalidate sessions on logout — server-side. Do not just delete the client cookie.

A08 — Software and Data Integrity Failures

What It Is

Code or data is used without verifying its integrity. Untrusted plugins, libraries loaded from unverified CDNs, CI/CD pipelines that can be modified by attackers, insecure deserialization.

Real-World Example

The 2020 SolarWinds attack: attackers compromised the build pipeline and inserted malicious code into legitimate software updates. Thousands of organizations installed the update. The supply chain itself was the attack vector.

Prevention

  1. Verify the integrity of downloaded packages using lockfiles and checksums (package-lock.json, packages.lock.json).
  2. Secure your CI/CD pipeline — restrict who can modify build scripts, use signed commits.
  3. Use Subresource Integrity (SRI) hashes for scripts loaded from CDNs:
HTML
<script src="https://cdn.example.com/lib.js"
        integrity="sha384-oqVuAfXRKap7fdgcCY5uykM6+R9GqQ8K/uxy9rx7HNQlGYl1kPzQho1wx4JwY8wC"
        crossorigin="anonymous"></script>

A09 — Security Logging and Monitoring Failures

What It Is

Your application does not log security-relevant events, or it logs them but no one is watching. An attacker can probe your system for hours, find a vulnerability, and exploit it — and you only find out months later when someone tells you.

Real-World Example

The average time to detect a breach is 197 days (IBM Cost of a Data Breach Report). In most cases, the organization did not detect it at all — a third party told them. Inadequate logging is a primary reason.

Prevention

  1. Log all authentication events — successful logins, failed logins, password resets, MFA events — with timestamps and IP addresses.
  2. Log all access control failures — when a user is denied access to a resource, that is a signal.
  3. Set up alerts for suspicious patterns: 50 failed logins in one minute, access from a new country, bulk data exports.
C#
// Log failed login attempts with context
_logger.LogWarning("Failed login attempt for user {Username} from IP {IpAddress} at {Timestamp}",
    username, httpContext.Connection.RemoteIpAddress, DateTime.UtcNow);

A10 — Server-Side Request Forgery (SSRF)

What It Is

The server fetches a URL provided by the user. An attacker provides a URL pointing to an internal service — the cloud metadata endpoint, a database, an internal admin panel — that should not be accessible from outside.

Real-World Example

A web app has a feature: "enter a URL and we will fetch a preview of that page." An attacker enters http://169.254.169.254/latest/meta-data/iam/security-credentials/ — the AWS metadata endpoint. The server fetches it and returns cloud credentials. The attacker now has AWS access.

Prevention

  1. Validate and allowlist URLs — only allow fetching from specific known domains if possible.
  2. Block requests to private IP ranges (10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, 169.254.0.0/16) server-side.
  3. Do not return raw server responses to the user — process the content and return only what is needed.

Summary Table

| # | Name | Core Risk | Primary Fix | |---|------|-----------|-------------| | A01 | Broken Access Control | Users access what they should not | Check ownership server-side on every request | | A02 | Cryptographic Failures | Sensitive data exposed | HTTPS + strong password hashing | | A03 | Injection | Malicious input executed as code | Parameterized queries, input validation | | A04 | Insecure Design | Feature is fundamentally unsafe | Threat model before building | | A05 | Security Misconfiguration | Default or weak settings | Hardening checklists, disable defaults | | A06 | Vulnerable Components | Known CVEs in dependencies | Automated dependency updates | | A07 | Auth Failures | Accounts easily compromised | MFA, rate limiting, secure sessions | | A08 | Integrity Failures | Tampered code or data executed | Lockfiles, signed commits, SRI hashes | | A09 | Logging Failures | Breaches go undetected | Log security events, set up alerts | | A10 | SSRF | Server reaches internal services | URL allowlisting, block private IPs |

Learning these categories is not about memorizing acronyms. It is about developing a habit of asking "how could this be abused?" for every feature you build.

Enjoyed this article?

Explore the Security & Compliance learning path for more.

Found this helpful?

Share:𝕏

Leave a comment

Have a question, correction, or just found this helpful? Leave a note below.