
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)
| # | Risk | Description | Moved From |
|---|---|---|---|
| A01 | Broken Access Control | Users act outside intended permissions | #5 → #1 |
| A02 | Cryptographic Failures | Sensitive data exposure through weak crypto | #3 → #2 |
| A03 | Injection | SQL, NoSQL, OS, LDAP injection | #1 → #3 |
| A04 | Insecure Design | Missing security architecture and design patterns | New |
| A05 | Security Misconfiguration | Default configs, open cloud storage, verbose errors | #6 → #5 |
| A06 | Vulnerable Components | Using components with known vulnerabilities | #9 → #6 |
| A07 | Auth Failures | Broken authentication and session management | #2 → #7 |
| A08 | Software & Data Integrity | Code and infrastructure without integrity verification | New |
| A09 | Security Logging Failures | Insufficient logging and monitoring | #10 → #9 |
| A10 | SSRF | Server-Side Request Forgery | New |
A01: Broken Access Control
The #1 risk. 94% of applications tested had some form of broken access control.
Vulnerable code:
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:
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/123to/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:
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:
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:
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):
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
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 missingSecurity headers every application should set:
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:
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
1Secure Development Lifecycle (SDL):
2
3 Requirements → Threat Modeling (A04: Insecure Design)
4 │
5 Design → Security Architecture Review
6 │
7 Development → SAST scanning, secure coding standards
8 │
9 Testing → DAST scanning, penetration testing
10 │
11 Deployment → Security hardening, dependency scanning
12 │
13 Operations → WAF, monitoring, incident response
14 │
15 Review → Lessons learned, update threat modelThe 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


