SSRF (Server-Side Request Forgery) — Security

Apa itu SSRF? Server-Side Request Forgery (SSRF) adalah serangan dimana attacker memaksa server (bukan browser) untuk membuat HTTP request ke tujuan yang…

Apa itu SSRF?

Server-Side Request Forgery (SSRF) adalah serangan dimana attacker memaksa server (bukan browser) untuk membuat HTTP request ke tujuan yang dipilih attacker. Yang berbahaya: server biasanya ada di jaringan internal yang tidak bisa diakses dari luar — metadata cloud, database, admin dashboard.

Skenario Klasik

// Fitur: user paste URL gambar, server fetch untuk generate preview
app.post("/fetch-preview", async (req, res) => {
  const url = req.body.url;
  const response = await fetch(url); // ← SSRF sink!
  const buffer = await response.buffer();
  res.send(buffer);
});

// Attacker mengirim:
// url = "http://169.254.169.254/latest/meta-data/iam/security-credentials/"
// Server menarik AWS credentials dari instance metadata!

Target Populer

Kasus Nyata: Capital One (2019)

Attacker mengeksploitasi SSRF di WAF misconfigured → hit EC2 metadata → curi temporary credentials → akses S3 → download 106 juta record nasabah. Kerugian: $190 juta. Root cause: SSRF + IMDSv1 yang tidak butuh auth token.

AWS IMDSv1 vs IMDSv2

# IMDSv1 (lama, vulnerable)
curl http://169.254.169.254/latest/meta-data/
# → langsung dapat data, cukup satu GET

# IMDSv2 (baru, aman)
TOKEN=$(curl -X PUT "http://169.254.169.254/latest/api/token" \
  -H "X-aws-ec2-metadata-token-ttl-seconds: 21600")
curl -H "X-aws-ec2-metadata-token: $TOKEN" \
  http://169.254.169.254/latest/meta-data/

# Kenapa IMDSv2 aman? Butuh PUT dulu (SSRF biasa hanya bisa GET),
# token memiliki TTL, header X-aws-ec2-metadata-token susah dipalsukan

Pencegahan

1. Allowlist Hostname (Paling Efektif)

const ALLOWED = ["images.cdn.com", "storage.googleapis.com"];

function isAllowed(url) {
  try {
    const u = new URL(url);
    if (u.protocol !== "https:") return false;
    return ALLOWED.includes(u.hostname);
  } catch {
    return false;
  }
}

app.post("/fetch-preview", async (req, res) => {
  if (!isAllowed(req.body.url)) return res.status(400).send("URL not allowed");
  // ... lanjutkan fetch
});

2. Block Private IP Ranges

// Resolve hostname → IP → cek apakah private
const dns = require("dns").promises;
const net = require("net");

async function isPrivate(host) {
  const addrs = await dns.resolve(host);
  return addrs.some(ip => {
    if (ip.startsWith("10.") || ip.startsWith("172.16.") || ip.startsWith("192.168.")) return true;
    if (ip === "127.0.0.1" || ip === "::1") return true;
    if (ip.startsWith("169.254.")) return true; // cloud metadata
    return false;
  });
}

Catatan: ada DNS rebinding attack — hostname yang awalnya resolve ke IP publik bisa re-resolve ke IP internal setelah validasi. Mitigasi: resolve sekali, pin IP untuk request aktual.

3. Disable Redirect

// fetch bisa di-redirect ke URL internal setelah validasi lolos
const response = await fetch(url, { redirect: "error" });

4. Network Egress Control

# Di AWS: security group / VPC route table
# Hanya izinkan outbound ke internet publik, block RFC1918

# Di Kubernetes: NetworkPolicy
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
spec:
  egress:
    - to:
        - ipBlock:
            cidr: 0.0.0.0/0
            except:
              - 10.0.0.0/8
              - 169.254.0.0/16

5. Pakai IMDSv2

# Force IMDSv2 di EC2 launch template
aws ec2 modify-instance-metadata-options \
  --instance-id i-xxx \
  --http-tokens required

Checklist SSRF

Yang akan kamu pelajari