Table of Contents

    OWASP risks

    SYSTEM DESIGN • CHAPTER 13.10

    OWASP Risks

    Understand the most consequential categories of web application security risk, why each arises from a design decision rather than a coding slip, and how to address them structurally.

    Learning objective: By the end of this article, you will understand the major OWASP risk categories, recognize how they manifest in distributed architectures, and know which architectural control addresses each one.

    Prerequisites

    Recommended Knowledge

    • HTTP request handling and API design
    • Authentication and session management
    • Authorization models and enforcement points
    • SQL, query construction, and parameterization
    • Encryption, hashing, and key management
    • Dependency management and build pipelines
    • Audit logging and monitoring
    • Threat modeling and trust boundaries

    What OWASP Provides

    The Open Worldwide Application Security Project publishes community-driven guidance on application security. Its most referenced output is a periodically revised list of the most significant web application risk categories, derived from observed vulnerability data and practitioner survey.

    Important framing: The list describes broad risk categories, not a checklist of specific bugs. It is awareness material, not a security standard, and passing it does not make an application secure.

    Useful For

    • Structuring a design review
    • Prioritizing where to invest effort
    • Providing shared vocabulary across teams
    • Seeding threat modeling discussions
    • Training engineers on recurring patterns

    Not Sufficient For

    • Proving an application is secure
    • Covering domain-specific business logic abuse
    • Replacing threat modeling
    • Addressing infrastructure and supply chain fully
    • Substituting for testing and review
    Every category below reflects a design decision made once and relied upon thousands of times. That is why they persist across revisions despite being well documented for years.

    Broken Access Control

    Consistently the most prevalent category. Access control fails when the server does not verify that the authenticated identity is permitted to perform the requested operation on the specific resource.

    Manifestation Description Structural Fix
    Object-level bypass Changing an identifier reaches another user's record Verify permission on the specific resource
    Function-level bypass Privileged endpoint callable by ordinary users Enforce server-side, never via hidden controls
    Tenant boundary crossing Data from another tenant returned Scope every query by tenant
    Mass assignment Client sets a role or ownership field Allow-list writable attributes
    Derived store leakage Search index omits permission filters Carry access metadata into the index
    Forced browsing Undocumented paths accessible directly Deny by default on all routes
    Insufficient Check Confirming the caller holds a role that permits reading reports, then returning whichever report identifier was supplied.
    Complete Check Confirming the role permits the operation, then separately confirming this identity is entitled to this specific record.
    async function getDocument(request, response) {
        const document = await documentRepository.findById(
            request.params.documentId
        );
    
        if (!document) {
            return response.status(404).json({ error: "not_found" });
        }
    
        if (document.tenantId !== request.principal.tenantId) {
            return response.status(404).json({ error: "not_found" });
        }
    
        const permitted = await authorizationService.canRead(
            request.principal,
            document
        );
    
        if (!permitted) {
            return response.status(404).json({ error: "not_found" });
        }
    
        return response.json(toDocumentView(document));
    }

    Cryptographic Failures

    This category covers absent, weak, or misapplied cryptography leading to exposure of data that required protection. The failure is frequently in key handling rather than algorithm choice.

    Failure Consequence Correct Practice
    Plaintext transmission Interception on the network path Enforce transport security everywhere
    Passwords encrypted not hashed Key compromise exposes all credentials Purpose-built hashing with a work factor
    Obsolete algorithms Protection is nominal only Current standard algorithms
    Predictable randomness Keys and tokens become guessable Cryptographically secure generation
    Keys stored with data One breach yields both Separate managed key service
    Unauthenticated encryption Tampering goes undetected Authenticated encryption modes
    Sensitive data in logs Protection bypassed entirely Field-level redaction
    CLASSIFY FIRST
    You cannot protect data appropriately without knowing which data is sensitive. Classification precedes every cryptographic decision.

    Injection

    Injection occurs when untrusted input is interpreted as command or query structure rather than as data. The root cause is constructing an instruction by concatenating strings.

    ROOT CAUSE
    Untrusted Input Concatenated Into Instruction Interpreted as Structure
    Vulnerable Construction Building a query by joining strings, where a crafted value terminates the intended clause and appends attacker-controlled logic.
    -- Parameterized: the value can never alter query structure
    SELECT order_id,
           customer_id,
           total_amount,
           status
    FROM orders
    WHERE customer_id = :customer_id
      AND status      = :status
      AND tenant_id   = :tenant_id
    ORDER BY created_at DESC
    LIMIT :page_size;
    Injection Type Interpreter Primary Defense
    SQL injection Database query engine Parameterized statements
    NoSQL injection Document query parser Type validation and operator restriction
    Command injection Operating system shell Avoid shell invocation, pass argument arrays
    Cross-site scripting Browser rendering engine Context-aware output encoding
    Template injection Template engine Never treat input as a template
    Log injection Log parser or viewer Structured logging with encoding
    Dynamic identifiers: Parameterization binds values, not table or column names. When a query shape must vary, select from a fixed allow-list rather than interpolating input.

    Insecure Design

    A distinct category acknowledging that some applications are implemented flawlessly against a specification that was itself unsafe. No amount of careful coding corrects a missing control.

    Design Gap Resulting Abuse Design Response
    No limit on password reset Account enumeration at scale Rate limits and uniform responses
    Unbounded resource creation Storage and cost exhaustion Per-tenant quotas
    Trusting client-supplied price Manipulated transaction totals Recompute server-side
    No approval separation Self-approved privileged actions Separation of duties
    Unlimited retry on codes Brute-forcing verification Attempt caps and expiry
    Refund without state check Repeated refunds on one order State machine with valid transitions
    Business logic abuse rarely appears in scanner output. It is found by asking how each feature could be misused, which is precisely what threat modeling provides.

    Security Misconfiguration

    Recurring Misconfigurations

    • Default credentials left unchanged
    • Administrative interfaces publicly reachable
    • Verbose errors exposing stack traces
    • Directory listing enabled
    • Unnecessary features and ports active
    • Overly permissive cross-origin policies
    • Missing security response headers
    • Storage buckets open to the public
    • Excessive cloud role permissions
    • Debug mode enabled in production
    Strict-Transport-Security: max-age=63072000; includeSubDomains
    Content-Security-Policy: default-src 'self'; frame-ancestors 'none'
    X-Content-Type-Options: nosniff
    Referrer-Policy: strict-origin-when-cross-origin
    Cache-Control: no-store
    Structural Remedy Define configuration as code, apply the same hardened baseline across environments, and detect drift automatically rather than relying on manual review.

    Vulnerable and Outdated Components

    Modern applications consist largely of code the team did not write. Transitive dependencies frequently outnumber direct ones by an order of magnitude, and each inherits its own risk.

    Control Purpose
    Dependency inventory Know what is actually deployed, including transitives
    Automated scanning Detect known vulnerabilities continuously
    Version pinning Ensure builds are reproducible
    Lockfile integrity Prevent silent substitution of packages
    Base image updates Address vulnerabilities below the application
    Removing unused packages Reduce surface with no functional loss
    Internal registry Control and vet what enters builds
    Supply chain reality: A compromised build pipeline distributes malicious code to every consumer of your artifact. Treat build infrastructure with the same rigour as production.

    Identification and Authentication Failures

    Weakness Exploitation Mitigation
    Unlimited login attempts Credential stuffing Layered limits and progressive delay
    Session not regenerated Session fixation New identifier on authentication
    Predictable identifiers Session guessing Secure random generation
    No session expiry Indefinite credential validity Idle and absolute timeouts
    Weak recovery flow Account takeover via reset Single-use expiring tokens
    Token claims unverified Forged identity accepted Signature, issuer, and audience checks
    Distinct error messages Account enumeration Uniform responses and timing

    Software and Data Integrity Failures

    This category concerns trusting code, updates, or serialized data without verifying its origin and integrity.

    Unverified Trust

    • Deserializing untrusted input into objects
    • Loading scripts without integrity checks
    • Deploying unsigned artifacts
    • Auto-updating from unverified sources
    • Pipeline plugins with unrestricted access

    Verified Trust

    • Parse structured data without object construction
    • Subresource integrity on external scripts
    • Signed artifacts verified before deployment
    • Pinned and reviewed pipeline dependencies
    • Least-privilege deployment credentials
    Deserialization Danger Reconstructing arbitrary object graphs from untrusted input can trigger code execution during construction, before any application validation runs.

    Logging and Monitoring Failures

    Insufficient logging does not cause a breach, but it determines whether one is detected in hours or discovered externally months later.

    UNDETECTED EXPOSURE
    \[ W_{\text{exposure}} = T_{\text{detected}} - T_{\text{compromised}} \]

    Minimum Viable Detection

    • Authentication successes and failures
    • Authorization denials with actor and resource
    • Privilege and role changes
    • Bulk export and mass data access
    • Administrative configuration changes
    • Alerting on patterns, not merely recording
    • Tamper-evident, append-only storage
    • Retention exceeding realistic detection delay

    Server-Side Request Forgery

    SSRF arises when a server fetches a URL supplied or influenced by a user. The server's network position becomes the attacker's, reaching internal services that are otherwise unreachable.

    THE PIVOT
    User Supplies URL Server Fetches It Internal Resource Reached
    async function fetchExternalResource(rawUrl, policy) {
        const parsed = new URL(rawUrl);
    
        if (!policy.allowedProtocols.includes(parsed.protocol)) {
            throw new Error("Protocol not permitted");
        }
    
        if (!policy.allowedHosts.includes(parsed.hostname)) {
            throw new Error("Host not on allow-list");
        }
    
        const addresses = await resolveHostname(parsed.hostname);
    
        for (const address of addresses) {
            if (isPrivateOrReservedAddress(address)) {
                throw new Error("Resolved to a restricted address");
            }
        }
    
        return await httpClient.get(parsed.toString(), {
            timeoutMs: policy.timeoutMs,
            maxRedirects: 0,
            maxResponseBytes: policy.maxResponseBytes,
            pinnedAddress: addresses[0]
        });
    }
    Validation timing matters: A hostname validated once may resolve differently when the request is made. Disable redirects and connect to the address that was actually verified.

    Mapping Risks to Controls

    Risk Category Primary Architectural Control Related Chapter Topic
    Broken access control Centralized server-side authorization RBAC and ABAC
    Cryptographic failures Managed keys and classified data Encryption
    Injection Parameterization and output encoding Threat modeling
    Insecure design Threat modeling and abuse cases Threat modeling
    Misconfiguration Configuration as code with drift detection Secrets and rotation
    Outdated components Inventory and continuous scanning Secrets and rotation
    Authentication failures Standard identity protocols OAuth2, OIDC, JWT and sessions
    Integrity failures Signed artifacts and verified sources Encryption
    Logging failures Tamper-evident audit trail with alerting Audit logs
    Server-side request forgery Allow-lists and network segmentation Threat modeling

    Cross-Cutting Principles

    1

    Never Trust the Client

    Any value from a client may be altered. Prices, identifiers, roles, and quantities must be validated or recomputed on the server.

    2

    Deny by Default

    Absence of an explicit permit is a denial. New endpoints should be inaccessible until authorization is deliberately configured.

    3

    Fail Closed

    An error during a security check provides no evidence that access is permitted. Errors must not become accidental approvals.

    4

    Layer Controls

    Assume any single control may fail. Defense in depth ensures one gap does not become a complete compromise.

    5

    Centralize Enforcement

    Security logic scattered across controllers will be applied inconsistently. One enforcement path is reviewable and testable.

    6

    Minimize Exposure

    Data not collected cannot leak, and features not enabled cannot be attacked. Reduction is the most durable control.

    Verification Approaches

    Method Detects Misses
    Static analysis Injection and unsafe API use Authorization and business logic flaws
    Dynamic scanning Misconfiguration and common patterns Context-dependent logic abuse
    Dependency scanning Known component vulnerabilities Undisclosed and first-party issues
    Secret scanning Committed credentials Secrets outside scanned locations
    Threat modeling Design gaps and abuse cases Implementation defects
    Penetration testing Chained and contextual weaknesses Issues outside the engagement scope
    Authorization tests Regressions in access rules Rules never written as tests
    describe("tenant isolation", function () {
        it("does not expose another tenant's document", async function () {
            const foreign = await createDocument({ tenantId: "tenant-a" });
    
            const response = await requestAs(
                { tenantId: "tenant-b", role: "administrator" },
                `/api/documents/${foreign.id}`
            );
    
            expect(response.statusCode).toBe(404);
        });
    
        it("rejects client-supplied role escalation", async function () {
            const response = await patchAs(
                { userId: "user-1", role: "viewer" },
                "/api/users/user-1",
                { displayName: "Updated", role: "administrator" }
            );
    
            const stored = await userRepository.findById("user-1");
    
            expect(stored.role).toBe("viewer");
            expect(response.statusCode).not.toBe(500);
        });
    });
    TESTING RULE
    Automated tools cannot know your authorization rules. Every access control decision worth making is worth encoding as a regression test.

    Common Design Mistakes

    Weak Practice

    • Treating the list as a compliance checklist
    • Relying on scanners for authorization coverage
    • Checking roles but not specific resources
    • Validating only on the client
    • Deferring security to a pre-release phase
    • Ignoring transitive dependencies
    • Differing configuration across environments
    • Logging without alerting
    • Returning verbose errors in production

    Strong Practice

    • Uses the list to seed threat modeling
    • Encodes authorization rules as tests
    • Verifies permission on each resource
    • Revalidates everything server-side
    • Reviews security at design time
    • Inventories the full dependency tree
    • Applies one hardened baseline everywhere
    • Alerts on defined attack patterns
    • Returns generic errors with a trace identifier

    System Design Interview Discussion

    Question What Your Answer Should Cover
    Which risk concerns you most? Access control, with reasoning from your design
    How is injection prevented? Parameterization and context-aware encoding
    How are tenants isolated? Scoping in queries and derived stores
    How do you find design flaws? Threat modeling and abuse cases
    How is the supply chain managed? Inventory, scanning, pinning, and signing
    How would a breach be detected? Audit events with pattern-based alerting
    Can users make you fetch a URL? SSRF controls and network segmentation
    How is this verified continuously? Layered testing plus authorization regressions

    Review Checklist

    Design Review Checklist

    • Deny by default on every route
    • Verify permission on the specific resource
    • Scope every query by tenant
    • Allow-list writable attributes on updates
    • Parameterize all queries and commands
    • Encode output for its rendering context
    • Classify data before choosing protection
    • Hash passwords with a work factor
    • Store keys in a managed service
    • Enforce transport security internally and externally
    • Apply one hardened configuration baseline
    • Disable debug output in production
    • Inventory and scan all dependencies
    • Verify artifact signatures before deployment
    • Avoid deserializing untrusted input
    • Allow-list outbound fetch destinations
    • Log security events and alert on patterns
    • Write regression tests for authorization rules
    • Re-run threat modeling on design changes

    Knowledge Check

    1

    Why is broken access control consistently prevalent?

    It must be enforced correctly on every path, and automated tools cannot infer which identity should reach which resource.

    2

    What is the root cause of injection?

    Untrusted input concatenated into an instruction, allowing it to be interpreted as structure rather than data.

    3

    Why is insecure design a separate category?

    Flawless implementation of an unsafe specification is still unsafe. Missing controls cannot be added by careful coding.

    4

    Why does SSRF matter in internal architectures?

    The server's network position lets an attacker reach internal services and metadata endpoints unreachable from outside.

    5

    Why is the list insufficient on its own?

    It describes general categories and cannot cover domain-specific business logic abuse, which requires threat modeling of your particular system.

    Summary

    The OWASP risk categories describe recurring classes of web application weakness observed across the industry. They are awareness material for structuring review and prioritizing effort, not a standard that confers security when satisfied.

    Broken access control remains the most prevalent, because it must be enforced on every path and cannot be verified automatically without knowledge of intended permissions. Injection persists wherever instructions are built by concatenation, and cryptographic failures usually trace to key handling rather than algorithm selection.

    Insecure design acknowledges that some applications implement an unsafe specification correctly. Misconfiguration, outdated components, and integrity failures extend the concern beyond application code into configuration, dependencies, and the build pipeline.

    Logging failures determine whether a compromise is detected in hours or months, and server-side request forgery turns the server's network position into an attacker's. Across all of them, the durable principles are the same: distrust the client, deny by default, fail closed, layer controls, centralize enforcement, and minimize what exists to be attacked.

    Key Takeaway

    Use the OWASP categories to start the conversation, not to end it. Map each risk to an architectural control rather than a code fix, encode your authorization rules as regression tests, and rely on threat modeling for the business logic abuse that no general list can anticipate.