The OWASP Top 10 is the reference list security teams use to track the most critical risks facing web applications. The 2021 version is still mostly accurate, but by 2026 SSRF, Insecure Design and software supply chain attacks have grown heavier. This article summarises all ten categories with real-world examples and practical defences.

A01: Broken Access Control

The most common and highest-impact category. IDOR (Insecure Direct Object Reference), path traversal, JWT tampering and privilege escalation all live here. If /api/orders/123 does not verify that the order_id belongs to the current user — you are vulnerable.

// WRONG
app.get('/api/orders/:id', async (req, res) => {
    const order = await db.orders.findByPk(req.params.id);
    res.json(order);  // Sees other people's orders!
});

// RIGHT
app.get('/api/orders/:id', requireAuth, async (req, res) => {
    const order = await db.orders.findOne({
        where: { id: req.params.id, user_id: req.user.id }
    });
    if (!order) return res.status(404).json({ error: 'Not found' });
    res.json(order);
});

A02: Cryptographic Failures

Previously known as "Sensitive Data Exposure". Passwords hashed with MD5/SHA1, sensitive data over plain HTTP, plaintext storage instead of ciphertext, weak TLS (1.0/1.1).

  • Use bcrypt, argon2 or scrypt for passwords — never MD5/SHA
  • Require TLS 1.2, prefer 1.3
  • Add HSTS header to defeat downgrade attacks
  • Encrypt sensitive data at rest with AES-256-GCM

A03: Injection

SQL, NoSQL, OS command, LDAP and XSS all belong to the injection family. Primary defence: parameterized queries and input sanitization.

// WRONG — SQL injection
const q = `SELECT * FROM users WHERE email = '${req.body.email}'`;

// RIGHT — parameters
const { rows } = await pool.query(
    'SELECT * FROM users WHERE email = $1',
    [req.body.email]
);

A04: Insecure Design

If security was not part of the architectural thinking, no amount of clean code will save the app. Missing threat modelling, secure design patterns and defence-in-depth fall here. Examples: weak 4-digit password reset codes, login forms without rate limiting, critical actions without account lockout.

A05: Security Misconfiguration

  • Default passwords (admin/admin)
  • Verbose error messages (stack trace in production!)
  • Unnecessary open ports and services
  • Outdated frameworks and libraries
  • Public cloud buckets

A06: Vulnerable and Outdated Components

log4j-shell, Spring4Shell and PrototypeJS are recent examples. Dependency scanning belongs in CI/CD: npm audit, pip-audit, snyk test, trivy.

# Node.js
npm audit --production
npm audit fix --force

# Python
pip install pip-audit
pip-audit

# Scan Docker images
trivy image myapp:latest

A07: Identification and Authentication Failures

Weak password policies, session fixation and login endpoints exposed to credential stuffing. Mandatory 2FA, CAPTCHA, rate limits and session invalidation are the baseline controls.

A08: Software and Data Integrity Failures

Supply chain attacks have been the biggest trend of the last three years. SolarWinds, ua-parser-js and event-stream are well-known package compromises. Controls: signed packages, committed lock files, dependency pinning, SBOM generation.

A09: Security Logging and Monitoring Failures

You cannot defend against what you do not see. The average breach takes 200+ days to discover. Log every critical event (login failure, privilege change, data export) and forward to a central SIEM.

A10: Server-Side Request Forgery (SSRF)

If your app fetches URLs based on user input, SSRF is a likely risk. The classic case: a "paste a URL and we will preview it" feature where the attacker points at http://169.254.169.254/latest/meta-data/ to leak AWS credentials.

// SSRF defence: block internal IPs
const net = require('net');
function isInternalIP(ip) {
    const parts = ip.split('.').map(Number);
    return parts[0] === 10
        || (parts[0] === 172 && parts[1] >= 16 && parts[1] <= 31)
        || (parts[0] === 192 && parts[1] === 168)
        || parts[0] === 127
        || parts[0] === 169;  // metadata, link-local
}

// DNS resolve the host and check the IP before fetching
const { address } = await dns.promises.lookup(urlHost);
if (isInternalIP(address)) throw new Error('Blocked internal IP');

Defence Strategy

  • Defence in depth — independent controls at every layer
  • Least privilege — every service and user with minimum permissions
  • Zero trust — do not trust requests just because they come from the internal network
  • Threat modelling — identify threats during design
  • Pentesting — at least one third-party test per year
  • Bug bounty — public invitation for critical apps

Conclusion

OWASP Top 10 is not a checklist; it is a mental model. Run every new feature through these ten categories during design, pass every line of code through an "untrusted input" filter, and scan for misconfiguration at deploy time. Security is a process, not a product.

Security audit for your web application

OWASP Top 10 compliance testing, penetration testing and code review Contact us

WhatsApp