Back to Guides

    API7:2023 - Server Side Request Forgery (SSRF)

    Server Side Request Forgery happens when an API fetches attacker-controlled URLs without enforcing safe destination rules, allowing access to internal resources from server-side context.

    1. What Is It?

    Server Side Request Forgery happens when an API accepts a user-supplied URL or destination and then makes the request from the server side without properly restricting where that request is allowed to go.

    The attacker does not fetch the target directly. Instead, they trick the application server into doing the fetch on their behalf.

    In the previous guide, we covered abuse of sensitive business flows. This guide focuses on a different risk: when the server itself becomes the network client and can be forced to reach internal or trusted-only destinations.

    Read the API6 Guide

    2. Why It Matters

    SSRF is dangerous because:

    • The server may have access that the attacker does not.
    • Internal-only services can become reachable through the API.
    • Metadata-like or infrastructure endpoints may be exposed.
    • Trust boundaries collapse when outbound destinations are uncontrolled.

    In real-world systems, this can lead to:

    • Access to cloud metadata or instance secrets.
    • Exposure of internal service tokens and configuration.
    • Pivoting into internal network resources.
    • Unexpected access to localhost or private hosts.

    Why OWASP classifies it as API7

    APIs often expose helper features that fetch URLs, retrieve webhooks, validate integrations, import remote data, or preview content. If those server-side requests are not constrained by destination allowlists or egress rules, attackers can abuse the API as a proxy into internal network space.

    3. How It Happens (Technical)

    This issue appears when the backend accepts a client-controlled URL and resolves it from server-side context without validating whether the destination is safe.

    The dangerous part is not the URL syntax itself. The danger comes from where the server is allowed to connect once it follows that input.

    Common technical causes:

    • No destination allowlist for outbound fetches.
    • No blocking of localhost, link-local, or internal hostnames.
    • Trusting parsed hostnames without enforcing egress policy.
    • Integration helpers that treat any absolute URL as acceptable.
    • Server-side fetch features designed for convenience but not isolation.

    Core Issue

    api/integrations_fetch.py
    # vulnerable
    @app.post("/api/integrations/fetch")
    def fetch_url(payload: FetchRequest):
        parsed = urlparse(payload.url)
        host = (parsed.hostname or "").lower()
    
        if host in {"169.254.169.254", "internal.apy.local", "localhost"}:
            return {
                "source": "internal",
                "metadata": {
                    "service": "payments-worker",
                    "token": FLAG,
                },
            }
    
        return {
            "source": "external",
            "url": payload.url,
            "preview": "ok",
        }
    The server follows user-controlled destinations without enforcing safe outbound rules.

    Correct Direction

    api/integrations_fetch.py
    # safer
    ALLOWED_HOSTS = {"api.partner.com", "status.partner.com"}
    
    @app.post("/api/integrations/fetch")
    def fetch_url(payload: FetchRequest):
        parsed = urlparse(payload.url)
        host = (parsed.hostname or "").lower()
    
        if host not in ALLOWED_HOSTS:
            raise HTTPException(status_code=400, detail="Destination not allowed")
    
        return safe_fetch(payload.url)
    The server should only fetch explicitly allowed destinations.

    Key concept: the problem is not that the user can supply a URL. The problem is that the server trusts that URL enough to make outbound requests into destinations the attacker should never be able to reach.

    Attacker’s Perspective

    From an attacker’s perspective, SSRF is about turning the application server into a network client that can see more than the attacker can.

    • Find features that fetch or preview remote URLs.
    • Establish a harmless external baseline request first.
    • Swap the destination to internal-style hosts or metadata paths.
    • Observe whether server-side context exposes new data.

    If the API can be convinced to fetch internal destinations, SSRF is present.

    4. Real-World Example

    This benchmark models an integration helper that fetches user-supplied URLs for remote content resolution.

    Relevant routes in the benchmark:

    • POST /api/auth/login issues a bearer token.
    • POST /api/integrations/fetch performs the server-side URL resolution.

    The important issue is that destination validation is effectively missing. The integration flow accepts an attacker-controlled URL and allows the backend to resolve internal-looking hosts such as 169.254.169.254 , localhost, or internal.apy.local.

    Example endpoint:

    POST /api/integrations/fetch

    Expected behavior: the feature may allow remote fetches for approved integrations, but it should never let the server reach internal-only destinations or metadata-style hosts.

    Vulnerable Logic

    api/integrations_fetch.py
    parsed = urlparse(payload.url)
    host = (parsed.hostname or "").lower()
    
    if host in {"169.254.169.254", "internal.apy.local", "localhost"}:
        return {
            "source": "internal",
            "metadata": {
                "service": "payments-worker",
                "token": FLAG,
            },
        }
    Instead of blocking internal destinations, the server follows them and returns sensitive internal metadata.

    What is wrong: the backend processes untrusted URL destinations with server-side network privileges and does not enforce an outbound destination policy.

    Example attack flow:

    1. Send a safe external URL to establish the normal behavior.
    2. Replace it with an internal-style target such as http://169.254.169.254/latest/meta-data/.
    3. Observe that the server resolves it from backend context.
    4. Extract internal metadata returned in the response body.

    Result: the attacker never reaches the internal host directly. The application server does it on their behalf and returns the sensitive result.

    Common Variations

    • Metadata Access: link-local or cloud-style metadata endpoints become reachable through the backend.
    • Localhost Access: services bound to localhostare exposed indirectly through server-side fetch helpers.
    • Internal Host Reachability: private DNS names or internal service hosts become accessible through integration logic.
    • Preview and Import Abuse: URL preview, import, webhook validation, or callback testing features become SSRF entry points.

    5. How To Prevent

    1. Enforce destination allowlists

    Server-side fetch features should only connect to explicitly approved hosts or domains. Do not allow arbitrary destinations.

    2. Block internal and local network targets

    Outbound requests should reject localhost, link-local, loopback, and private/internal destinations unless there is a very specific and isolated reason to allow them.

    network/allowlist.py
    def is_allowed_host(host: str) -> bool:
        return host in {"api.partner.com", "status.partner.com"}
    Allow only known-safe integration targets.

    3. Treat helper fetch features as high risk

    URL preview, integration testing, import, callback validation, and similar helper functions should be treated as network-execution features, not harmless convenience tools.

    4. Separate fetch logic from trusted network zones

    If remote retrieval is necessary, isolate it through controlled egress, sandboxing, or network policy rather than giving the main application unrestricted outbound reach.

    5. Never return internal response details blindly

    Even when a fetch succeeds, the API should not reflect raw internal metadata, tokens, or service details back to the caller.

    Key Principle

    • Untrusted URLs must never imply trusted destinations
    • The server should not fetch arbitrary network targets
    • Integration helpers are outbound execution surfaces
    • Protect network reachability, not just request format

    6. Detection Tips (Scanner Perspective)

    Detecting API7 requires testing whether a server-side fetch feature can be redirected from safe external URLs toward internal or trusted-only destinations.

    Common techniques:

    • Send a harmless public URL to establish baseline behavior.
    • Replace it with localhost, link-local, or internal host patterns.
    • Observe whether the response source or content changes.
    • Check for metadata, tokens, or service-identifying fields.
    • Probe integration helpers, importers, and preview features first.

    Key signal: if a user-controlled URL can cause the server to reach an internal destination and return backend-only data, SSRF is present.

    7. Final Takeaway

    API7 is about network trust abuse through the application server.

    It exists whenever:

    • The client controls a server-side fetch destination.
    • Internal or trusted-only hosts are reachable through the backend.
    • Outbound destination policy is missing or too weak.
    • Server-side fetch helpers return sensitive internal results.

    Every URL-fetching feature should answer: If the user controls the destination, what stops the server from reaching internal resources?

    If the answer is unclear, the API is exposed.