SyntaxStudy
Sign Up
Web Security OWASP Top 10 2021: A01–A04 with Examples
Web Security Beginner 2 min read

OWASP Top 10 2021: A01–A04 with Examples

The OWASP Top 10 is the most widely cited standard for web application security risks, updated every three to four years based on data collected from hundreds of organisations. The 2021 edition introduced several restructured categories. A01: Broken Access Control moved to the top position, reflecting its prevalence — 94% of applications tested had some form of access control failure. Examples include IDOR (Insecure Direct Object Reference), missing function-level access control, and misconfigured CORS. Mitigations: deny by default, server-side enforcement, RBAC, and automated access control testing. A02: Cryptographic Failures (formerly Sensitive Data Exposure) covers failures in protecting data at rest and in transit — using MD5/SHA1 for password hashing, transmitting sensitive data over HTTP, exposing database backups, or storing credit cards without tokenisation. A03: Injection consolidates SQL injection, NoSQL injection, command injection, LDAP injection, and template injection — all sharing the root cause of unsanitised data being interpreted as code. A04: Insecure Design is a new category addressing architectural and design flaws that cannot be patched after the fact, such as missing rate limiting on credential recovery flows or designing authentication without considering account enumeration. Each OWASP category maps to specific CWE (Common Weakness Enumeration) identifiers used in CVE databases and security tools. Familiarity with the Top 10 helps developers communicate with security teams using shared vocabulary, understand automated SAST/DAST scanner output, and prioritise security requirements during sprint planning. The OWASP Testing Guide and OWASP Cheat Sheet Series provide detailed implementation guidance for each category.
Example
<?php
// A01: Broken Access Control — IDOR example and fix

// VULNERABLE: uses user-supplied ID directly without ownership check
// GET /api/invoices/4729
public function showInvoice(int $invoiceId): JsonResponse
{
    // BAD: Any authenticated user can read any invoice by guessing the ID
    $invoice = Invoice::findOrFail($invoiceId);
    return response()->json($invoice);
}

// SECURE: enforce ownership (or RBAC) on every object access
public function showInvoice(int $invoiceId): JsonResponse
{
    $invoice = Invoice::where('id', $invoiceId)
                      ->where('user_id', auth()->id())   // ownership check
                      ->firstOrFail();                    // 404 if not owned
    return response()->json($invoice);
}

// A03: Injection — command injection and fix
// VULNERABLE: unsanitised user input passed to shell
public function convertImage(string $filename): void
{
    // BAD: attacker sends filename = "img.jpg; rm -rf /var"
    exec("convert uploads/{$filename} output/{$filename}.png");
}

// SECURE: use escapeshellarg() and restrict characters
public function convertImage(string $filename): void
{
    if (!preg_match('/^[\w\-]+\.(jpg|png|gif)$/i', $filename)) {
        throw new \InvalidArgumentException('Invalid filename');
    }
    $safeFilename = escapeshellarg("uploads/{$filename}");
    $safeOutput   = escapeshellarg("output/{$filename}.png");
    exec("convert {$safeFilename} {$safeOutput}");
}

// A04: Insecure Design — missing rate limit on password reset
// SECURE: throttle password reset requests to prevent enumeration + abuse
// Route::post('/password/reset')->middleware('throttle:5,1');