Back to Web Vulnerabilities: SQLi, XSS, CSRF, SSRF
Q5Question 5 of 7

SSRF (Server-Side Request Forgery) and How to Prevent It

Key Concepts
  • SSRF tricks the server into making HTTP requests to attacker-chosen destinations by supplying a crafted URL.
  • Because the request originates from the trusted server, it can reach internal resources the attacker cannot access directly.
  • The primary defense is a strict allowlist of permitted hosts plus blocking private, loopback, and link-local IP ranges.
  • The defense must check the resolved IP at connection time, not just the textual URL, to defeat DNS rebinding.
  • The cloud metadata endpoint (169.254.169.254) is the most dangerous target: it can hand out IAM credentials.
  • Never pass raw user input as a URL to an HTTP client; route outbound calls through a controlled egress proxy.

Definition

SSRF makes your trusted server fetch attacker-chosen URLs: allowlist destinations and block internal and metadata addresses.
  • SSRF (Server-Side Request Forgery) – a vulnerability where an attacker supplies a crafted URL that the server fetches, making HTTP requests to a destination of the attacker's choosing.
  • Internal access – the request comes from the trusted server, so it can reach internal APIs, admin panels, and the cloud metadata endpoint (169.254.169.254).
  • Primary defense – strict allowlist of permitted hosts and schemes, with resolved IP validation.
  • IP check – reject private (10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16), loopback (127.0.0.0/8), and link-local (169.254.0.0/16) addresses.
  • Egress proxy – route all outbound requests through a controlled gateway that enforces destination rules centrally.

Imagine National Bank of India has an errand boy who fetches whatever address a customer writes on a slip. Normally customers write public addresses. But a clever attacker writes 'go to the bank's own internal safe-room and read the master key list.' Because the errand boy is a trusted insider with a pass, he walks straight into the internal room a customer could never enter, and brings back the secrets. SSRF prevention is giving the errand boy a short allowlist of addresses he may visit and strict orders to refuse any address that points inside the building.

Understanding the Concepts

What is SSRF?

Server-Side Request Forgery (SSRF) is a vulnerability where an attacker tricks the server into making HTTP requests to a destination of the attacker's choosing. The attacker supplies a crafted URL, and the server fetches it. Because the request originates from the trusted server, it can reach internal resources the attacker could never reach directly.

The most dangerous targets are the cloud metadata endpoint (169.254.169.254), which can hand out temporary IAM credentials, and internal APIs that are not exposed to the internet.

TEXT
SSRF attack targets:
  http://169.254.169.254/latest/meta-data/iam/security-credentials/
    --> Leaks temporary IAM credentials (catastrophic)
  http://localhost:8080/admin
    --> Accesses internal admin panel
  http://10.0.0.5:5432/
    --> Reaches internal database
  file:///etc/passwd
    --> Reads local files (non-HTTP schemes)

Where does SSRF typically appear?

SSRF appears wherever an application fetches a user-supplied URL: webhook callbacks, import-from-URL features, PDF generators, image fetchers, link previews, or partner integrations.

The vulnerable pattern is httpClient.get(userSuppliedUrl) with no validation.

JAVA
package com.bank.vulnerability;

import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.net.URI;

public class VulnerableUrlFetcher {

    private static final HttpClient CLIENT = HttpClient.newHttpClient();

    // DANGEROUS: no validation on the URL
    public String fetch(String userUrl) throws Exception {
        HttpRequest request = HttpRequest.newBuilder()
            .uri(URI.create(userUrl))
            .build();
        HttpResponse<String> response = CLIENT.send(request,
            HttpResponse.BodyHandlers.ofString());
        return response.body();
    }

    // Attacker calls: fetch("http://169.254.169.254/latest/meta-data/")
    // Server fetches the metadata endpoint and returns IAM credentials!
}

How do you prevent SSRF with URL validation?

The primary defense is a strict allowlist of permitted hosts and schemes, combined with IP address validation after DNS resolution. The check must happen on the resolved IP, not just the textual URL.

JAVA
package com.bank.security;

import java.net.InetAddress;
import java.net.URI;
import java.util.Set;

public class SsrfValidator {

    private static final Set<String> ALLOWED_HOSTS = Set.of(
        "api.partner.com",
        "webhook.paymentgateway.com"
    );

    public boolean isAllowed(String url) throws Exception {
        URI uri = URI.create(url);

        // Step 1: Only allow https scheme
        if (!"https".equals(uri.getScheme())) {
            System.out.println("BLOCKED: non-HTTPS scheme: " + uri.getScheme());
            return false;
        }

        // Step 2: Host must be on the allowlist
        String host = uri.getHost();
        if (!ALLOWED_HOSTS.contains(host)) {
            System.out.println("BLOCKED: host not on allowlist: " + host);
            return false;
        }

        // Step 3: Resolve IP and check it is not private/internal
        InetAddress address = InetAddress.getByName(host);
        if (isPrivateOrReserved(address)) {
            System.out.println("BLOCKED: resolved to private IP: " + address);
            return false;
        }

        System.out.println("ALLOWED: " + url);
        return true;
    }

    private boolean isPrivateOrReserved(InetAddress addr) {
        return addr.isLoopbackAddress()    // 127.0.0.0/8
            || addr.isSiteLocalAddress()    // 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16
            || addr.isLinkLocalAddress();   // 169.254.0.0/16 (includes metadata)
    }

    public static void main(String[] args) throws Exception {
        SsrfValidator validator = new SsrfValidator();
        validator.isAllowed("https://api.partner.com/webhook");
        validator.isAllowed("http://169.254.169.254/latest/meta-data/");
        validator.isAllowed("https://evil.com/callback");
        validator.isAllowed("https://api.partner.com/internal");
    }
}
TEXT
ALLOWED: https://api.partner.com/webhook
BLOCKED: non-HTTPS scheme: http
BLOCKED: host not on allowlist: evil.com
ALLOWED: https://api.partner.com/internal

Why must you check the resolved IP?

Attackers use DNS rebinding to bypass hostname-based checks. The attacker's domain resolves to a public IP on the first lookup (passing the check) and then to a private IP (169.254.169.254) on the actual connection.

The defense is to resolve the hostname, check the resolved IP, and then pin that IP for the connection.

TEXT
DNS rebinding attack:
  1. Attacker registers evil.com
  2. First DNS lookup: evil.com --> 93.184.216.34 (public IP, passes check)
  3. Server opens connection
  4. Second DNS lookup (for the actual request): evil.com --> 169.254.169.254!
  5. Server connects to metadata endpoint

Defense: pin the resolved IP
  1. Resolve evil.com --> 93.184.216.34
  2. Check: is 93.184.216.34 private? No --> proceed
  3. Connect to 93.184.216.34 specifically (pinned)
  4. DNS rebinding cannot redirect the connection

What about redirect-based bypasses?

Attackers use HTTP redirects to bypass URL checks. The initial URL passes validation, but the server follows a redirect to an internal address.

The defense is to disable automatic redirect following or re-validate the destination after each redirect.

JAVA
package com.bank.security;

import java.net.http.HttpClient;
import java.net.http.HttpClient.Redirect;

public class SafeHttpClientConfig {

    // Disable automatic redirect following
    public HttpClient createSafeClient() {
        return HttpClient.newBuilder()
            .followRedirects(Redirect.NEVER) // do not auto-follow
            .build();
        // Manually handle redirects: validate each hop
    }
}

What is the egress proxy pattern?

Route all outbound HTTP calls through a dedicated egress proxy or gateway that enforces destination rules centrally. This prevents any service from directly contacting disallowed destinations.

DIAGRAM
  Egress Proxy Pattern:
  ┌──────────────────┐
  │  Bank Service    │  wants to call external URL
  │                  │
  │  Cannot reach    │── outbound blocked by firewall
  │  internet        │
  │  directly        │
  └────────┬─────────┘
           │ sends request to egress proxy
           ▼
  ┌──────────────────┐
  │  Egress Proxy    │
  │                  │
  │  Allowlist check │── allowed? ──► forward to internet
  │  IP check        │── blocked? ──► reject request
  │  Logging         │
  └──────────────────┘

How do you protect the cloud metadata endpoint?

Block the metadata endpoint (169.254.169.254) at the network level with firewall rules, and prefer IMDSv2, which requires a token obtained via a PUT request that SSRF typically cannot perform.

TEXT
Defenses against metadata access:
  1. Block 169.254.169.254 in the egress proxy and firewall
  2. Use IMDSv2 (token-required): SSRF cannot issue the PUT request to get a token
  3. Validate resolved IPs: link-local range (169.254.0.0/16) is always blocked
  4. Run services with least-privilege IAM so even leaked credentials are limited

What are common mistakes?

The biggest mistake is validating only the textual URL without checking the resolved IP. The hostname can look innocent while resolving to an internal address.

JAVA
// WRONG: check hostname only
if (!url.contains("169.254") && !url.contains("localhost")) {
    httpClient.get(url); // DNS rebinding bypasses this!
}

// CORRECT: resolve and check IP
InetAddress addr = InetAddress.getByName(uri.getHost());
if (addr.isLoopbackAddress() || addr.isSiteLocalAddress()
        || addr.isLinkLocalAddress()) {
    throw new SecurityException("Blocked: internal address");
}

What should a senior engineer know?

Senior engineers know that SSRF defense must operate on the resolved IP at connection time, not just the textual URL. They route all outbound calls through a controlled egress proxy, disable automatic redirects, block the metadata service at the network level, and run with least-privilege IAM so even leaked credentials are limited.

Senior engineers also know that SSRF can use non-HTTP protocols (gopher://, file://) and that scheme validation is essential.

Visual Overview

DIAGRAM
  SSRF Attack:
  ┌──────────────┐  crafted URL        ┌──────────────┐
  │  Attacker    ├─────────────────────►│  Bank Server │
  │              │  "fetch this URL"    │              │
  │              │                      │  Fetches URL │
  └──────────────┘                      └──────┬───────┘
                                               │
                    Server is TRUSTED           │
                    Can reach internal resources│
                                               ▼
                                        ┌──────────────┐
                                        │ 169.254.169. │
                                        │ 254 metadata │
                                        │              │
                                        │ Returns IAM  │
                                        │ credentials! │
                                        └──────────────┘

  SSRF Defense:
  ┌──────────────┐  URL           ┌──────────────────┐
  │  Request     ├───────────────►│  SSRF Validator   │
  │              │                │                    │
  │              │                │ 1. Scheme: https? │
  │              │                │ 2. Host: allowlist?│
  │              │                │ 3. Resolve IP      │
  │              │                │ 4. IP: public?     │
  │              │                │ 5. Not private?    │
  │              │                │    Not loopback?   │
  │              │                │    Not link-local? │
  │              │                │                    │
  │              │                │ All pass? --> fetch │
  │              │                │ Any fail? --> block │
  └──────────────┘                └──────────────────┘

Key Takeaway

Server-Side Request Forgery, or SSRF, is a vulnerability where an attacker tricks the server into making HTTP requests to a destination of the attacker's choosing by supplying a crafted URL that the server fetches. Because the request comes from the trusted server, it can reach internal-only resources the attacker could never reach directly: internal APIs, admin panels, databases, and most dangerously the cloud metadata endpoint at 169.254.169.254, which can hand out temporary IAM credentials.

In the National Bank of India project, the risk appears in features that fetch a user-supplied URL, like partner webhooks or import-from-URL. The vulnerable pattern is passing that raw URL straight to an HTTP client. Without defenses, an attacker who registered the metadata endpoint or an internal private IP as a webhook would make the bank's own server fetch those and leak credentials.

The primary defense is a strict allowlist of permitted hosts and schemes combined with checking the resolved IP. The scheme is validated to allow only HTTPS, the host is confirmed on the allowlist, the hostname is resolved, and the address is rejected if it falls in loopback, link-local (169.254.0.0/16), or private ranges. An allowlisted public URL is accepted while the metadata endpoint, an internal IP, and a non-allowlisted host are all blocked.

The senior points are that the decision must be made on the resolved IP at connection time, not just the textual URL, because attackers use DNS rebinding and redirects to bypass naive hostname checks. Outbound calls should route through a controlled egress proxy, automatic redirects should be disabled, the metadata service should be blocked at the network level with IMDSv2 preferred, and services should run with least-privilege IAM.

Remember one sentence: never pass raw user input as a URL to an HTTP client; allowlist hosts, check the resolved IP, and block all internal and metadata addresses.

---

Share
Use ← → arrow keys to navigate