Day 62: SSRF and Tool Allowlists

Day 62: SSRF and Tool Allowlists

An agent that can fetch(url) will eventually fetch http://169.254.169.254/. SSRF defenses are not optional: allowlists, DNS pinning, and egress proxies that block link-local and cloud metadata.

⚡ TL;DR: Default-deny URL tools. Allow only https to known hosts. Resolve DNS yourself and block private/link-local ranges. Force egress through a proxy that strips IMDS.

Allowlist first

# net/allow.py
from urllib.parse import urlparse
import ipaddress, socket

ALLOW_HOSTS = {"api.github.com", "docs.internal.example"}

def assert_safe_url(url: str):
    u = urlparse(url)
    if u.scheme != "https":
        raise ValueError("scheme")
    if u.hostname not in ALLOW_HOSTS:
        raise ValueError("host")
    infos = socket.getaddrinfo(u.hostname, 443)
    for info in infos:
        ip = ipaddress.ip_address(info[4][0])
        if ip.is_private or ip.is_loopback or ip.is_link_local:
            raise ValueError("blocked_ip")  # ✅ DNS rebinding defense

Egress proxy

# block IMDS and RFC1918
deny 169.254.0.0/16;
deny 10.0.0.0/8;
deny 172.16.0.0/12;
deny 192.168.0.0/16;

Run agent tasks with careful NO_PROXY — never let the task hit IMDS hop-by-hop.

Tool shape

export async function httpGet(url: string) {
  assertSafeUrl(url);
  return fetch(url, { redirect: "error" }); // ❌ no blind redirects
}

Closing checklist

  • [ ] HTTPS + host allowlists
  • [ ] Resolve + block private IPs
  • [ ] Disable automatic redirects or re-validate
  • [ ] Egress proxy denying IMDS
  • [ ] Unit tests for metadata IPs and rebinding

Series navigation

Day 61: Prompt Injection in Jira, Email, and RAG · Day 63: IAM for Agents: Roles, Not God Keys

Last updated September 11, 2026


Discover more from CheatCoders

Subscribe to get the latest posts sent to your email.

Comments

No comments yet. Why don’t you start the discussion?

Leave a Reply