đŸ›Ąïž Application Security CheatSheet

SSRF (Server-Side Request Forgery) Deep Dive

SSRF happens when an application lets an attacker influence where the server makes a network request. The attacker can trick the server into requesting internal systems (databases, admin panels, cloud metadata) that the attacker cannot reach directly.

Practical note: SSRF shows up in ‘helpful’ features — link previews, imports, webhooks — and it often becomes an internal network scanner before anyone notices.

Key idea: SSRF is a trust boundary failure: you gave users a “fetch this URL” capability and the server’s network position is far more privileged than the user’s.

Why SSRF exists (root cause)

Modern apps routinely fetch URLs: webhooks, link previews, image imports, PDF converters, OAuth metadata, remote config, and integrations. SSRF appears when the app treats a user-controlled destination as data but the server treats it as instructions for routing.

SSRF impact is often not “reading a webpage” — it’s reaching internal control planes, metadata, or services that trust the network.

Mental model (experienced-level)

Think of SSRF as a routing pipeline:

  1. Source: where the destination comes from (URL param, JSON field, webhook config, import feature).
  2. Normalization: parsing, decoding, redirects, DNS resolution, and proxy behavior.
  3. Decision: allow/deny logic (host allowlist, scheme restrictions, IP blocks, port policy).
  4. Request execution: the HTTP client, its redirect rules, DNS caching, and timeout behavior.
  5. Response handling: do we return the body to the user? store it? parse it? (this decides data leakage vs blind SSRF).
experienced rule: “Validating the string is not enough.” You must validate the final resolved destination after parsing + DNS + redirects.

Common SSRF shapes (what you see in real apps)

1) Fetch/preview features

Plain: “Preview this link / import this image”. Deep: server fetches user-supplied URLs and may follow redirects.

2) Webhooks & callbacks

Plain: “Send events to this URL”. Deep: outbound requests can be aimed at internal systems or metadata.

3) Integrations / connectors

Plain: “Connect to service X”. Deep: misconfigured “service URL” fields become SSRF if not constrained.

4) Blind SSRF

Plain: server makes a request but you don’t see the response. Deep: still dangerous if it can hit internal control endpoints or trigger side effects.

Interview line: “SSRF is not only about reading internal data; it’s also about reachability and side effects from a privileged network position.”

Vulnerable vs secure code patterns (Node.js)

Vulnerable pattern (minimal)

// Node/Express (concept example)
app.post("/preview", async (req, res) => {
  const url = String(req.body.url || "");
  // ❌ Vulnerable: server fetches an attacker-influenced destination
  const r = await fetch(url, { redirect: "follow" });
  const text = await r.text();
  res.json({ ok: true, preview: text.slice(0, 500) });
});

Fixed defensive pattern (allowlist + resolution checks + safe client)

import dns from "node:dns/promises";
import net from "node:net";

// Minimal helpers (conceptual; tune for your environment)
const ALLOWED_HOSTS = new Set(["example.com", "api.example.com"]); // prefer explicit host allowlist
const ALLOWED_SCHEMES = new Set(["https:"]); // prefer https-only
const ALLOWED_PORTS = new Set([443]); // keep tight
const MAX_REDIRECTS = 0; // safest default: don't follow redirects automatically

function isPrivateIp(ip) {
  // Covers common private ranges + loopback + link-local (IPv4). Expand for IPv6 in real code.
  const parts = ip.split(".").map(Number);
  if (parts.length !== 4 || parts.some(n => Number.isNaN(n) || n < 0 || n > 255)) return false;
  const [a,b] = parts;
  if (a === 10) return true;
  if (a === 127) return true;
  if (a === 169 && b === 254) return true;
  if (a === 172 && b >= 16 && b <= 31) return true;
  if (a === 192 && b === 168) return true;
  return false;
}

async function validateDestination(raw) {
  let u;
  try { u = new URL(raw); } catch { throw new Error("Invalid URL"); }

  if (!ALLOWED_SCHEMES.has(u.protocol)) throw new Error("Blocked scheme");
  if (u.username || u.password) throw new Error("Blocked credentials in URL");
  if (!u.hostname) throw new Error("Missing host");

  // Enforce explicit host allowlist
  const host = u.hostname.toLowerCase();
  if (!ALLOWED_HOSTS.has(host)) throw new Error("Host not allowed");

  // Enforce explicit ports (URL without port implies protocol default)
  const port = u.port ? Number(u.port) : (u.protocol === "https:" ? 443 : 80);
  if (!ALLOWED_PORTS.has(port)) throw new Error("Port not allowed");

  // Resolve and block private IPs (validate the final resolved destination)
  const addrs = await dns.lookup(host, { all: true, verbatim: true });
  for (const a of addrs) {
    if (a.family === 4 && isPrivateIp(a.address)) throw new Error("Resolved to private IP");
    // For IPv6, add: block ::1, fc00::/7, fe80::/10, etc.
  }

  return u.toString();
}

app.post("/preview", async (req, res) => {
  try {
    const raw = String(req.body.url || "");
    const safeUrl = await validateDestination(raw);

    // Use strict client behavior: no redirects, short timeouts, size limits, no proxy surprises
    const ctrl = new AbortController();
    const t = setTimeout(() => ctrl.abort(), 3000);
    const r = await fetch(safeUrl, { redirect: "manual", signal: ctrl.signal });
    clearTimeout(t);

    // Optional: enforce content-type allowlist and response size caps before reading
    const ct = (r.headers.get("content-type") || "").toLowerCase();
    if (!ct.includes("text/html") && !ct.includes("application/json")) {
      return res.status(415).json({ ok: false, error: "Unsupported content type" });
    }

    const text = await r.text();
    res.json({ ok: true, preview: text.slice(0, 500) });
  } catch (e) {
    res.status(400).json({ ok: false, error: String(e.message || e) });
  }
});
Reality check: For high-risk SSRF surfaces, prefer no arbitrary URLs at all. Use indirect references (IDs) or fetch only from known upstreams.

Detection workflow (how to validate it)

Start by mapping the feature, then validate the controls rather than “trying payloads”.

Step A — Find outbound request surfaces

Step B — Determine SSRF class

Step C — Validate controls

Interview framing: “I verify final destination after parsing + DNS + redirects and treat network egress controls as defense-in-depth.”

Safe validation (defensive verification only)

Goal: demonstrate that user input can influence server outbound routing, without probing internal services or sensitive endpoints.
  1. Baseline behavior: confirm the feature makes outbound requests and document inputs that control destination.
  2. Observe controls: check for allowlists, scheme/port restrictions, redirect handling, and error messages.
  3. Confirm resolution behavior: verify that destination validation occurs after parsing and DNS resolution (and again after redirects if used).
  4. Limit scope: test only against safe, owned, or explicitly permitted endpoints (e.g., internal staging endpoints you control).
  5. Evidence: capture request logs, server-side traces, and policy decisions (blocked vs allowed) to support remediation.
Do not provide or use instructions to target internal metadata, private ranges, or sensitive services. In interviews, describe control verification, not exploitation.

Exploitation progression (attacker mindset)

Conceptual only (no step-by-step). Attackers usually move from “can I steer the server’s request?” to “can I reach something the server can access?” by exploring parsing, redirects, DNS, and egress policy gaps.

Phase 1: Find a controllable request

Phase 2: Test constraints

Phase 3: Attempt internal reachability (concept)

Phase 4: Chain with app behavior

Interview takeaway: SSRF is about server reach, final destination validation, and defense-in-depth (allowlists + egress controls).

Tricky edge cases & bypass logic (conceptual)

experienced tip: “If your rule is a substring check, assume it will fail.” Validate the parsed URL and the resolved IP.

Confidence levels (low / medium / high)

ConfidenceWhat you observedWhat you can claim
Low Suspected outbound fetch, but destination control unclear or inconsistent “Potential SSRF surface; requires confirmation of controllable destination and control evaluation”
Medium Repeatable destination influence, but tight allowlist/egress controls limit risk “Likely SSRF class issue; controls reduce exploitability but design review recommended”
High Repeatable influence with ability to reach non-allowed/internal destinations (or major side effects) during validation “Confirmed SSRF with clear root cause and actionable remediation guidance”

Fixes that hold in production

1) Eliminate arbitrary URLs (best)

2) Strict allowlist: scheme + host + port

3) Validate final destination

4) Harden the HTTP client

5) Defense-in-depth: network egress controls

Practical priority order: remove arbitrary destinations → allowlist → validate resolved IP + redirects → harden client → enforce egress controls.

Interview-ready summaries (60-second + 2-minute)

60-second answer

SSRF is when user input influences where the server makes a network request. Because servers can reach internal services and cloud metadata, SSRF can become high impact. I look for URL-fetch features and validate controls: strict scheme/host/port allowlists, DNS resolution checks to block private ranges, safe redirect handling, client hardening, and egress firewalling as defense-in-depth.

2-minute answer

I treat SSRF as a trust-boundary bug between user-controlled destinations and the server’s privileged network position. I start by mapping the feature: where destination input comes from, how URLs are parsed/normalized, whether redirects are followed, and whether the response is exposed (data leak) or it’s blind SSRF (side effects). Then I validate controls at the right layer: parse with a proper URL parser, enforce strict allowlists for scheme/host/port, resolve DNS and block private/loopback/link-local ranges (IPv4 and IPv6), and either disable redirects or re-validate each hop. Finally I harden the HTTP client (timeouts, size limits) and add network egress controls so even if app logic regresses, the network blocks unsafe routes.

Checklist

Remediation playbook

  1. Contain: disable/limit the URL-fetch feature or constrain it to known-good destinations immediately.
  2. Fix root cause: implement strict allowlists and validate the final resolved destination (DNS + redirects).
  3. Harden client: timeouts, response size caps, and safe redirect policy; remove proxy surprises.
  4. Network controls: enforce outbound egress rules to block private networks and metadata ranges.
  5. Regression prevention: centralize fetch logic, add tests for policy enforcement, and review all URL-fetch call sites.
  6. Verify: re-test the original flow and search for other “server fetches URL” surfaces across the app.

Interview Questions & Answers (Easy → Hard)

How to answer: Start simple, then go deep: trust boundary, final destination validation, redirects/DNS, and egress defense-in-depth.

Easy

  1. What is SSRF?
    A: Plain: when the server is tricked into making a request to a destination influenced by an attacker. Deep: it’s a trust-boundary failure—user input controls server routing; the server can reach internal services the attacker can’t.
  2. Why is SSRF risky?
    A: Plain: because the server has more network access. Deep: SSRF can reach internal control planes, metadata services, or “internal-only” APIs and trigger side effects or leak data depending on response exposure.
  3. What’s blind SSRF?
    A: Plain: the server makes a request but you can’t see the response. Deep: still dangerous if it can trigger internal actions or reach privileged endpoints; detection relies on server-side logs/telemetry.
  4. Best primary defense?
    A: Plain: don’t allow arbitrary URLs. Deep: use strict allowlists for destinations (scheme/host/port), validate resolved IPs, and handle redirects safely.
  5. Allowlist vs blocklist?
    A: Plain: allowlist is safer. Deep: blocklists miss edge cases (IPv6, encodings, parser quirks). Allowlists define “known-good” destinations and reduce ambiguity.
  6. What does “validate final destination” mean?
    A: Plain: check where it really goes, not what the string looks like. Deep: parse URL, resolve DNS, and re-check after redirects; block private/loopback/link-local ranges.

Medium

  1. Scenario: A “link preview” feature fetches a URL and returns HTML snippet. What risks do you assess?
    A: Plain: the server might fetch internal resources. Deep: I assess if destination is controllable, redirect behavior, DNS resolution checks, whether response is returned (data leak), and whether egress controls exist.
  2. Scenario: Webhooks let customers configure callback URLs. How do you secure it?
    A: Plain: restrict where callbacks can go. Deep: per-tenant destination allowlists, https-only, fixed ports, validation after DNS, safe redirect policy, plus outbound egress rules and auditing.
  3. Follow-up: Why do redirects matter for SSRF?
    A: Plain: redirects can change the destination. Deep: if you only validate the initial URL, a redirect can send the request elsewhere; safest is no redirects or validate every hop.
  4. Scenario: The app blocks “localhost” and private ranges using string checks. Is that enough?
    A: Plain: no. Deep: string checks are brittle; you must parse the URL, resolve DNS, and check the actual IP ranges (including IPv6) and re-check if redirects occur.
  5. Follow-up: What do you look at in the HTTP client?
    A: Plain: how it behaves with redirects/timeouts. Deep: redirect policy, proxy settings, DNS caching, timeouts, response size caps, and whether non-http schemes are possible.
  6. Scenario: Response is not returned to user. How can SSRF still be impactful?
    A: Plain: it can trigger actions. Deep: blind SSRF can hit internal endpoints with side effects, interact with trusted internal APIs, or create observable behavior via logs/metrics.
  7. Follow-up: How do you prioritize fixes?
    A: Plain: remove arbitrary destinations first. Deep: eliminate or constrain the feature (IDs/known hosts), add final destination validation and redirect/DNS safety, harden the client, then enforce egress controls.

Hard

  1. Scenario: You use a host allowlist, but DNS rebinding is a concern. What do you do?
    A: Plain: ensure the request goes to the validated IP. Deep: validate DNS resolution, block private ranges, and where feasible pin the resolved IP for the request; combine with egress firewalling so private ranges are blocked regardless of DNS tricks.
  2. Follow-up: Why do you still need network egress controls if app allowlists exist?
    A: Plain: mistakes happen. Deep: egress controls are defense-in-depth: if code regresses or a bypass is found, the network still blocks internal destinations and metadata ranges.
  3. Scenario: Multi-tenant SaaS allows “custom integration base URL”. How can that become a tenant-to-tenant risk?
    A: Plain: one tenant could point requests at another tenant’s internal endpoints. Deep: if shared infrastructure or internal APIs are reachable, SSRF can become lateral movement; enforce per-tenant isolation, strict allowlists, and separate networks.
  4. Scenario: The feature must fetch user-provided URLs for business reasons. What “secure architecture” do you propose?
    A: Plain: isolate the fetcher. Deep: run fetching in a hardened egress-restricted service or sandbox with strict outbound ACLs, minimal credentials, tight timeouts, and validated destinations; treat it as an untrusted “fetch worker”.
  5. Follow-up: What’s the most common “experienced miss” in SSRF fixes?
    A: Plain: validating the wrong thing. Deep: validating only the input string, not the parsed/resolved destination; forgetting IPv6; following redirects without re-validation; and assuming “internal-only” services are safe.
  6. Scenario: You discover SSRF in an internal admin tool. Is it lower severity?
    A: Plain: not necessarily. Deep: admin tools often run in privileged networks and may access sensitive internal systems; SSRF there can be higher impact due to reach and trust. Scope and network position matter.
  7. Follow-up: How do you communicate SSRF risk to non-security stakeholders?
    A: Plain: “users can make our servers call places they shouldn’t.” Deep: I explain server reach and internal trust, show how allowlists + egress controls reduce blast radius, and propose an incremental fix plan with minimal product disruption.
Safety note: for understanding +