OWASP Top 10: The Most Critical Web Application Security Risks Explained

OWASP Top 10: The Most Critical Web Application Security Risks Explained

Why Every Developer Should Know the OWASP Top 10

The OWASP Top 10 is the most widely referenced standard for web application security risks. Published by the Open Web Application Security Project, it represents a broad consensus about the most critical security threats to web applications. If you build software that touches the internet, understanding these vulnerabilities isn't optional—it's essential.

The current list (2021, with 2025 update in progress) reflects a shift toward design flaws and architectural weaknesses rather than just implementation bugs.

The OWASP Top 10 (2021)

#RiskDescriptionMoved From
A01Broken Access ControlUsers act outside intended permissions#5 → #1
A02Cryptographic FailuresSensitive data exposure through weak crypto#3 → #2
A03InjectionSQL, NoSQL, OS, LDAP injection#1 → #3
A04Insecure DesignMissing security architecture and design patternsNew
A05Security MisconfigurationDefault configs, open cloud storage, verbose errors#6 → #5
A06Vulnerable ComponentsUsing components with known vulnerabilities#9 → #6
A07Auth FailuresBroken authentication and session management#2 → #7
A08Software & Data IntegrityCode and infrastructure without integrity verificationNew
A09Security Logging FailuresInsufficient logging and monitoring#10 → #9
A10SSRFServer-Side Request ForgeryNew

A01: Broken Access Control

The #1 risk. 94% of applications tested had some form of broken access control.

Vulnerable code:

python
1# INSECURE: No authorization check
2@app.route('/api/users/<user_id>/profile')
3def get_profile(user_id):
4    # Any authenticated user can access ANY user's profile
5    user = User.query.get(user_id)
6    return jsonify(user.to_dict())
7
8# INSECURE: Client-side role check only
9@app.route('/api/admin/users')
10def admin_users():
11    # Only checking role in frontend JavaScript, not backend
12    return jsonify([u.to_dict() for u in User.query.all()])

Fixed code:

python
1# SECURE: Proper authorization
2@app.route('/api/users/<user_id>/profile')
3@login_required
4def get_profile(user_id):
5    # Users can only access their own profile
6    if str(current_user.id) != user_id and not current_user.is_admin:
7        abort(403)
8    user = User.query.get_or_404(user_id)
9    return jsonify(user.to_dict())
10
11# SECURE: Server-side role verification
12@app.route('/api/admin/users')
13@login_required
14@require_role('admin')
15def admin_users():
16    return jsonify([u.to_dict() for u in User.query.all()])

Common patterns:

  • IDOR (Insecure Direct Object Reference): Changing /api/orders/123 to /api/orders/124
  • Privilege escalation: Regular user accessing admin endpoints
  • Missing function-level access control
  • CORS misconfiguration allowing unauthorized origins

A02: Cryptographic Failures

Vulnerable patterns:

python
1# INSECURE: MD5 for password hashing
2import hashlib
3password_hash = hashlib.md5(password.encode()).hexdigest()
4
5# INSECURE: Hardcoded secret key
6SECRET_KEY = "my-super-secret-key-123"
7
8# INSECURE: HTTP for sensitive data
9API_URL = "http://api.example.com/payments"

Fixed code:

python
1# SECURE: bcrypt with salt
2from bcrypt import hashpw, gensalt, checkpw
3password_hash = hashpw(password.encode(), gensalt(rounds=12))
4
5# SECURE: Environment variable for secrets
6import os
7SECRET_KEY = os.environ["SECRET_KEY"]
8
9# SECURE: HTTPS enforced
10API_URL = "https://api.example.com/payments"

A03: Injection

Injection dropped from #1 to #3, but remains dangerous. Modern frameworks help, but raw queries still appear in production code.

SQL Injection example:

python
1# INSECURE: String concatenation in SQL
2@app.route('/api/search')
3def search():
4    query = request.args.get('q')
5    # Attacker sends: q='; DROP TABLE users; --
6    results = db.execute(
7        f"SELECT * FROM products WHERE name LIKE '%{query}%'"
8    )
9    return jsonify(results)
10
11# SECURE: Parameterized query
12@app.route('/api/search')
13def search():
14    query = request.args.get('q', '')
15    results = db.execute(
16        "SELECT * FROM products WHERE name LIKE :query",
17        {"query": f"%{query}%"}
18    )
19    return jsonify(results)

NoSQL Injection (MongoDB):

javascript
1// INSECURE: Direct user input in query
2app.post('/api/login', (req, res) => {
3  // Attacker sends: {"username": {"$gt": ""}, "password": {"$gt": ""}}
4  User.findOne({
5    username: req.body.username,
6    password: req.body.password
7  });
8});
9
10// SECURE: Input validation + sanitization
11app.post('/api/login', (req, res) => {
12  const username = String(req.body.username);
13  const password = String(req.body.password);
14  User.findOne({ username })
15    .then(user => bcrypt.compare(password, user.passwordHash));
16});

A04: Insecure Design

New in 2021. This isn't about implementation bugs—it's about missing threat modeling and security patterns at the design phase.

Example: An e-commerce site allows unlimited password reset attempts with a 4-digit code. Even with rate limiting per IP, an attacker with a botnet can brute-force any account.

Secure design patterns:

  • Threat modeling during architecture phase (STRIDE, PASTA)
  • Secure design patterns library (reference architectures)
  • Paved road: Make the secure way the easy way for developers
  • Abuse case testing alongside functional testing

A05: Security Misconfiguration

text
1Common Misconfigurations:
2
3☐ Default credentials on admin panels (admin/admin)
4☐ Unnecessary HTTP methods enabled (PUT, DELETE, TRACE)
5☐ Directory listing enabled on web server
6☐ Stack traces exposed in error responses
7☐ Cloud storage buckets publicly accessible (S3, GCS)
8☐ CORS set to Access-Control-Allow-Origin: *
9☐ Debug mode enabled in production
10☐ Default security headers missing

Security headers every application should set:

nginx
1# Nginx security headers
2add_header X-Content-Type-Options "nosniff" always;
3add_header X-Frame-Options "DENY" always;
4add_header X-XSS-Protection "0" always;
5add_header Referrer-Policy "strict-origin-when-cross-origin" always;
6add_header Content-Security-Policy "default-src 'self'; script-src 'self'" always;
7add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
8add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always;

A06: Vulnerable and Outdated Components

78% of codebases contain at least one vulnerability in third-party dependencies. Automated scanning is essential:

bash
1# npm audit — check Node.js dependencies
2npm audit
3npm audit fix
4
5# pip-audit — check Python dependencies
6pip install pip-audit
7pip-audit
8
9# Snyk — multi-language vulnerability scanner
10snyk test
11snyk monitor  # continuous monitoring
12
13# GitHub Dependabot — automatic PRs for vulnerable dependencies
14# .github/dependabot.yml
15version: 2
16updates:
17  - package-ecosystem: "npm"
18    directory: "/"
19    schedule:
20      interval: "weekly"

A07-A10: Remaining Risks

A07 - Authentication Failures: Weak passwords, missing MFA, credential stuffing. Use established libraries (Passport.js, Django auth, NextAuth) rather than custom implementations.

A08 - Software & Data Integrity: Unverified CI/CD pipelines, unsigned updates, deserialization attacks. Always verify integrity of code and data through signatures and checksums.

A09 - Security Logging Failures: If you can't detect a breach, you can't respond. Log authentication events, access control failures, input validation failures, and application errors.

A10 - SSRF (Server-Side Request Forgery): The server makes requests on behalf of an attacker. Cloud metadata endpoints (169.254.169.254) are prime targets. Validate and sanitize all URLs, use allowlists.

Integrating OWASP into Your Development Process

text
1Secure Development Lifecycle (SDL):
2
3 Requirements → Threat Modeling (A04: Insecure Design)
45   Design → Security Architecture Review
67 Development → SAST scanning, secure coding standards
89  Testing → DAST scanning, penetration testing
1011 Deployment → Security hardening, dependency scanning
1213 Operations → WAF, monitoring, incident response
1415   Review → Lessons learned, update threat model

The OWASP Top 10 isn't a checklist to complete—it's a framework for thinking about application security. Organizations that internalize these risks into their development culture build more resilient software.

Sources: OWASP Top 10 (2021), OWASP Testing Guide, OWASP Cheat Sheet Series