Antoine Duno
Founder of ZeriFlow · 10 years fullstack engineering · About the author
Key Takeaways
- The OWASP Top 10 is the most widely referenced framework for web application security risks. This guide covers all ten vulnerabilities with real-world examples, concrete testing methods, and specific fixes — not abstract theory.
- Includes copy-paste code examples and step-by-step instructions.
- Free automated scan available to verify your implementation.
OWASP Top 10 Web Application Vulnerabilities (2026 Edition)
The OWASP Top 10 is a standard awareness document published by the Open Web Application Security Project. It represents broad consensus about the most critical security risks to web applications, compiled from data contributed by hundreds of organisations and security researchers.
Understanding the OWASP Top 10 is not just useful for passing a security audit — it is the foundation of defensive web development. Each category in the list represents a class of vulnerabilities responsible for real-world breaches, affecting organisations from startups to Fortune 500 companies.
This guide covers all ten categories: what they are, how they are exploited, how to test for them, and how to fix them.
A01: Broken Access Control
What it is: Access control enforces the policy that users can only act within their intended permissions. Broken access control occurs when these rules are not properly enforced — users can access resources or perform actions they should not be able to.
Common examples:
- Changing a URL parameter to access another user''s account: /api/orders/12345 — increment the ID to see other users'' orders
- Accessing admin endpoints without being an admin
- Viewing unpublished content by guessing a direct URL
- Privilege escalation by modifying a JWT or cookie to set role: admin
Real-world pattern:
GET /api/v1/users/1001/profile → Returns your profile
GET /api/v1/users/1002/profile → Returns another user''s profile (IDOR)How to test: - Log in as User A, copy the URL or API endpoint for your resources - Log in as User B (or log out entirely) and try accessing those URLs - Use Burp Suite to modify user ID parameters in API requests
How to fix:
// Bad: trusting client-supplied user ID
app.get(''/api/orders/:orderId'', async (req, res) => {
const order = await Order.findById(req.params.orderId);
res.json(order);
});
// Good: verify the resource belongs to the authenticated user
app.get(''/api/orders/:orderId'', authenticate, async (req, res) => {
const order = await Order.findOne({
_id: req.params.orderId,
userId: req.user.id, // Scope to authenticated user
});
if (!order) return res.status(404).json({ error: ''Not found'' });
res.json(order);
});Deny by default: only grant access when access is explicitly permitted, not by checking if something should be denied.
A02: Cryptographic Failures
What it is: Previously called "Sensitive Data Exposure," this category covers failures related to cryptography that expose sensitive data. It includes transmitting data in clear text, using weak or deprecated algorithms, and poor key management.
Common examples: - Database contains passwords hashed with MD5 or SHA1 - Credit card numbers stored unencrypted - Sensitive API responses served over HTTP - Weak encryption keys or hard-coded keys - Using ECB mode for block cipher encryption
How to test: - Check that HTTPS is enforced on all pages - Review password hashing algorithm in the codebase - Scan for sensitive data patterns in HTTP responses - Check if data is encrypted at rest in the database
How to fix: - Use bcrypt, Argon2, or scrypt for passwords — never MD5 or SHA1 - Use TLS 1.2+ for all data in transit - Use AES-256-GCM for symmetric encryption, never ECB mode - Store encryption keys outside of application code (use a secrets manager)
A03: Injection
What it is: Injection vulnerabilities occur when untrusted data is sent to an interpreter as part of a command or query. The attacker''s hostile data tricks the interpreter into executing unintended commands or accessing unauthorised data.
Types: - SQL Injection (SQLi) - NoSQL Injection - OS Command Injection - LDAP Injection - Expression Language (EL) Injection
Classic SQLi example:
-- Input: username = "admin'' OR ''1''=''1"
SELECT * FROM users WHERE username = ''admin'' OR ''1''=''1'' AND password = ''...'';
-- Returns all usersHow to test:
- Try '' OR 1=1-- in form fields and URL parameters
- Use SQLMap for automated SQL injection testing:
sqlmap -u "https://yourdomain.com/users?id=1" --dbs
- Review code for string concatenation in database queries
How to fix:
// Vulnerable:
const user = await db.query(`SELECT * FROM users WHERE email = ''${email}''`);
// Safe: parameterised queries
const user = await db.query(''SELECT * FROM users WHERE email = $1'', [email]);
// Safe: ORM with bound parameters
const user = await User.findOne({ where: { email } }); // SequelizeNever build queries or commands by concatenating user input. Always use parameterised queries, prepared statements, or ORMs with proper escaping.
A04: Insecure Design
What it is: This category addresses fundamental design flaws — security risks arising from missing or ineffective security controls in the design phase. Unlike implementation bugs, these cannot be fixed by writing better code without redesigning the feature.
Common examples:
- Password reset flow that allows unlimited guesses on the reset token
- "Security questions" as an authentication recovery mechanism
- Credential recovery by revealing the password (not resetting it)
- Multi-tenant application storing all tenant data in shared tables with only an tenant_id filter
How to test: Threat modelling sessions during design. Ask: "What happens if a user does not follow the happy path? What if they manipulate inputs? What if they automate this endpoint?"
How to fix: - Perform threat modelling before building features - Apply security by design: define security requirements as part of user stories - Use established patterns (OAuth 2.0, WebAuthn) rather than rolling your own auth - Implement rate limiting and account lockout as architectural requirements, not afterthoughts
A05: Security Misconfiguration
What it is: The most commonly seen issue in practice. Security misconfiguration includes default credentials left unchanged, unnecessary features enabled, overly permissive CORS, missing security headers, verbose error messages, and unpatched software.
Common examples:
- Default admin passwords on databases or admin panels
- Directory listing enabled
- Cloud storage buckets set to public
- Debug mode left on in production
- Access-Control-Allow-Origin: * on an API with sensitive data
How to test:
# Check for common misconfigs
curl -I https://yourdomain.com # Check headers
curl https://yourdomain.com/phpinfo.php # PHP info page
curl https://yourdomain.com/.env # Exposed env file
curl https://yourdomain.com/admin # Exposed admin panelHow to fix: Infrastructure as Code (IaC) with security controls baked in. Automated scanning after every deployment — tools like ZeriFlow catch misconfiguration issues across headers, TLS, and DNS automatically.
A06: Vulnerable and Outdated Components
What it is: Using components (libraries, frameworks, runtime environments) with known vulnerabilities. Once a CVE is published, automated scanners run by attackers can find and target affected instances within hours.
Real-world examples: - Log4Shell (CVE-2021-44228): Remote code execution in Log4j, affecting millions of Java applications - event-stream npm package: Supply chain attack that targeted Bitcoin wallets - ImageMagick vulnerabilities allowing server-side request forgery
How to test:
# npm
npm audit
# Python
pip install safety && safety check
# Ruby
bundle auditOr use ZeriFlow''s Advanced Scan on a GitHub repository for automated dependency CVE checking.
How to fix:
- Enable Dependabot or Renovate for automated dependency update PRs
- Run npm audit --audit-level=critical in CI/CD to block deployments with critical CVEs
- Subscribe to security advisories for your main dependencies
- Remove unused dependencies (they are still attack surface)
A07: Identification and Authentication Failures
What it is: Authentication-related weaknesses that allow attackers to impersonate other users. Previously called "Broken Authentication."
Common examples: - No brute-force protection on login endpoints - Weak or predictable session tokens - Session tokens not invalidated on logout - Password requirements too permissive (allowing "123456") - No multi-factor authentication on privileged accounts
How to test: - Attempt 50+ rapid login requests and check for rate limiting - Log out and attempt to reuse the old session token - Check session token entropy (should be at least 128 bits)
How to fix:
// Rate limiting login endpoint
import rateLimit from ''express-rate-limit'';
const loginLimiter = rateLimit({
windowMs: 15 * 60 * 1000,
max: 5,
message: ''Too many login attempts.'',
});
app.post(''/login'', loginLimiter, loginController);
// Proper session invalidation on logout
app.post(''/logout'', authenticate, (req, res) => {
req.session.destroy(); // Destroy server-side session
res.clearCookie(''session'');
res.json({ success: true });
});Use established authentication libraries rather than building your own. Implement MFA for all user accounts, mandatory for admin roles.
A08: Software and Data Integrity Failures
What it is: Code and infrastructure that does not protect against integrity violations. This includes insecure CI/CD pipelines, auto-updating functionality with no signature verification, and deserialisation of untrusted data.
Common examples: - Loading JavaScript from a CDN without Subresource Integrity (SRI) verification - Deserialising untrusted data without type checking (Java object deserialisation) - CI/CD pipeline that executes code from an unverified third-party action - Software update mechanism that does not verify package signatures
How to fix:
Use SRI hashes for third-party scripts:
<script
src="https://cdn.example.com/jquery-3.7.0.min.js"
integrity="sha384-NXgwF8Kv9SSAr+jemKKcbvQsz+teULH/a5UNJvZc6kP47hZgl62M1vGnw6gHQhb3"
crossorigin="anonymous">
</script>Validate and sanitise all deserialised data. Avoid native language deserialisation of untrusted data entirely where possible.
A09: Security Logging and Monitoring Failures
What it is: Insufficient logging and monitoring allows attackers to achieve their goals without detection. On average, it takes 207 days to identify a breach — largely because logging is inadequate.
What should be logged: - All authentication events (success, failure, lockout) - Access control failures - Input validation failures - Application errors and exceptions - Admin actions
What should NOT be in logs: - Passwords (even failed ones) - Session tokens - Full credit card numbers - Other sensitive user data
How to fix:
// Structured logging with appropriate detail
import logger from ''./logger''; // Winston, Pino, etc.
app.post(''/login'', async (req, res) => {
try {
const user = await authenticate(req.body.email, req.body.password);
logger.info(''auth.login.success'', { userId: user.id, ip: req.ip });
// ...
} catch (err) {
logger.warn(''auth.login.failure'', { email: req.body.email, ip: req.ip, reason: err.message });
res.status(401).json({ error: ''Invalid credentials'' });
}
});Ship logs to a centralised monitoring system (Datadog, CloudWatch, Splunk) with alerting for anomalous patterns.
A10: Server-Side Request Forgery (SSRF)
What it is: SSRF occurs when an application fetches a remote resource based on user-supplied input without validating the URL. An attacker can make the server send requests to internal services, cloud metadata endpoints, or localhost — bypassing firewall rules.
Classic attack:
# Legitimate use:
POST /api/fetch-preview
{"url": "https://example.com/image.jpg"}
# SSRF attack:
POST /api/fetch-preview
{"url": "http://169.254.169.254/latest/meta-data/iam/security-credentials/"}
# Returns AWS instance credentialsThe AWS metadata endpoint at 169.254.169.254 is a well-known SSRF target. Attackers have used it to steal cloud credentials and pivot to S3 buckets, RDS databases, and IAM roles.
How to test: Identify any endpoint that accepts a URL as input. Try http://localhost/, http://127.0.0.1/admin, and http://169.254.169.254/latest/meta-data/.
How to fix:
import { URL } from ''url'';
function isAllowedUrl(urlString) {
try {
const url = new URL(urlString);
// Only allow HTTPS to known domains
if (url.protocol !== ''https:'') return false;
const allowedDomains = [''example.com'', ''cdn.example.com''];
if (!allowedDomains.includes(url.hostname)) return false;
return true;
} catch {
return false;
}
}
app.post(''/fetch-preview'', (req, res) => {
if (!isAllowedUrl(req.body.url)) {
return res.status(400).json({ error: ''URL not allowed'' });
}
// ... fetch and return preview
});At the infrastructure level, block access to internal IP ranges (10.x.x.x, 172.16.x.x, 192.168.x.x, 169.254.x.x) from your application servers at the firewall or network policy level.
Tools for Testing Against the OWASP Top 10
| Tool | Best For | Cost |
|---|---|---|
| ZeriFlow | Infrastructure misconfig, headers, TLS, secrets | Free tier available |
| OWASP ZAP | Automated web app scanning (SQLi, XSS, etc.) | Free |
| Burp Suite Community | Manual testing, request interception | Free (Community) |
| SQLMap | SQL injection testing | Free |
| npm audit / Snyk | Dependency CVE scanning | Free tier available |
| truffleHog | Secrets in git history | Free |
No single tool covers every OWASP category. A thorough assessment requires a combination of automated scanning and manual testing.
Summary
The OWASP Top 10 provides a practical framework for web application security:
- 1Broken Access Control — enforce authorisation on every endpoint
- 2Cryptographic Failures — use modern algorithms, encrypt sensitive data
- 3Injection — use parameterised queries, never concatenate user input
- 4Insecure Design — threat model during design, not after
- 5Security Misconfiguration — automate config checks, remove defaults
- 6Vulnerable Components — automate dependency scanning in CI/CD
- 7Authentication Failures — rate limit, use strong sessions, invalidate on logout
- 8Integrity Failures — verify third-party code, use SRI
- 9Logging Failures — log security events, alert on anomalies
- 10SSRF — validate URLs, block internal network access
Start with an automated scan at ZeriFlow to identify infrastructure-level issues across several of these categories instantly.
Check if your site is vulnerable to these attacks — free.
80+ automated security checks in under 60 seconds.