#AI#AI Agent#DevOps

Agent Network Egress Protection: Blocking SSRF, DNS Rebinding, and Redirect Bypass

Letting agents freely fetch URLs turns the model into an intranet request proxy. This article covers URL normalization, full-address validation, connection IP pinning, per-hop redirect inspection, egress proxies, and an attack regression suite.

Key Points

  • Topic Focus: AI、AI Agent、DevOps
  • Suitable for: indie hackers, full-stack engineers, AI application developers
  • Core value: quickly understand background, practical paths, common pitfalls, and engineering advice
Author @admin·Published July 26, 2026·Updated July 26, 2026

Browsing tools, webhook validators, and document fetchers typically accept model-generated URLs. By hiding http://169.254.169.254/, http://localhost:..., or a domain pointing to an internal network inside a web page, an attacker can make the agent read cloud metadata, admin panels, and services that are only exposed inside the cluster. Even without response echo, status codes and latency can be used for scanning; if an internal endpoint mistakenly produces side effects via GET, the risk is even higher.

Coding Agent Sandbox Permissions recommends restricting network access, but a generic "can access the internet" setting is still too broad. MCP Security Audit covers the overall threat surface of tools, while this article focuses solely on the full HTTP egress chain from URL intake to socket establishment, particularly post-validation DNS changes and redirect bypass.

Default to a destination-domain allowlist

The strongest policy is to declare a limited set of destinations for each tool. For example, a document fetcher only accesses approved official sites, and a model-vendor client only accesses fixed API domains. Rules match the normalized full hostname and do not use string checks like endsWith("example.com") that would let evil-example.com slip through; subdomain rules must express boundaries explicitly. If a tool has no business reason to access the arbitrary internet, do not provide a generic fetch.

When web search genuinely needs to be opened up, also restrict it to HTTP/HTTPS, standard ports, no username or password, a maximum URL length, and acceptable paths. Reject file:, gopher:, ftp:, data:, credentials embedded in URLs, and ambiguous IP representations. First parse with a standard URL parser, then make decisions on the hostname produced by the parser; do not rely on regex to handle user@host, IPv6 brackets, trailing dots, and percent encoding.

Validate all resolved addresses, not just the domain

Query both A and AAAA for the hostname, and check every address in the returned set. As long as the set contains loopback, private, link-local, shared, multicast, documentation-reserved ranges, IPv4-mapped IPv6, or any other RFC-defined special-purpose address, reject the entire request; do not pick one public address and continue. 0.0.0.0, 127.0.0.0/8, ::1, fc00::/7, fe80::/10, and cloud-metadata link-local addresses are all within the rejection scope.

ts
const target = parseAndNormalize(inputUrl);
assertAllowedSchemeAndPort(target);
const answers = await dns.promises.lookup(target.hostname, {
  all: true,
  verbatim: true,
});
if (!answers.length || answers.some(({ address }) => !isPublicAddress(address))) {
  throw new Error("EGRESS_TARGET_NOT_PUBLIC");
}

Resolving only at validation time is not enough. A malicious domain can return a public IP the first time and, after passing the check, return 127.0.0.1 when the HTTP client resolves it again — this is the DNS-rebinding time-of-check/time-of-use gap. A secure requester should select the validated address and pin the connection to that IP, while keeping the original hostname as the HTTP Host and TLS SNI; the client, proxy, or system must not re-resolve at connection time. The certificate is still verified against the original hostname; do not disable TLS verification just because the connection is established over an IP.

Treat every redirect as a new request

Disable the library's automatic redirects, read Location manually, resolve relative addresses against the current URL, then re-run the full chain from scheme, port, and DNS through IP pinning. Set a maximum of three to five hops, a visited set, and a total time budget, and reject loops. A public short link that redirects to an internal network is equivalent to the user directly submitting an internal URL; validating only the first hop provides no security.

Requests do not forward user cookies, Authorization headers, internal tracing baggage, or proxy credentials. Use a dedicated User-Agent, a method allowlist, connect and read timeouts, a maximum response size in bytes, a post-decompression cap, and content-type constraints. Downloading large files requires a separate tool and isolated storage; a generic fetcher must not decompress without bounds. Before returning content to the model, perform text extraction and untrusted-markup tagging; Prompt Injection Defense handles content-layer risk, and SSRF filtering cannot replace it.

Build a second boundary at the network layer

Application validation may have implementation flaws, so the agent worker egresses through a dedicated egress proxy, and network policy prohibits direct access to the VPC, Pod/Service CIDRs, the host, the control plane, and metadata addresses. The proxy re-resolves and enforces the same public-address and domain policy, logging the rule version, target domain, pinned IP, port, redirect chain, and rejection reason, but not sensitive query strings.

The DNS resolver only uses controlled servers and does not accept modifications to /etc/hosts or proxy environment variables from the task container. Fix the interpretation of HTTP_PROXY and NO_PROXY in the startup configuration so that tool output cannot change them. The subnets and security groups from Cloud Networking Fundamentals provide the final routing block: even if the application incorrectly allows a request, it still cannot reach internal assets.

Build a dedicated SSRF regression environment

The test service provides public addresses, private addresses, mixed A/AAAA, hexadecimal or IPv4-mapped representations, URLs with userinfo, non-standard ports, overly long redirects, and public-to-private jumps. Then deploy a controllable DNS that returns a public address on the first resolution and loopback on the second, proving that the actual socket still connects to the address reviewed the first time. The success criterion is not model rejection, but that the target service receives no packets at all and the audit shows a deterministic code.

Positive tests cover allowed domains, legitimate CDN multi-address responses, relative redirects, and certificate renewals, to avoid rules blocking all normal fetches. Before going live, first observe the real targets that would be rejected in audit mode, but audit-phase requests may only be sent in a test environment without sensitive networks; production must never "log but allow" private-network requests.

Disable generic egress on incidents; do not relax rules

After detecting SSRF or a parsing anomaly, immediately disable the generic HTTP tool and keep only the dedicated clients for fixed vendors and approved domains; block the attacking domain and target IP at the proxy, rotate any metadata credentials that may have been exposed, query the egress audit for the same run, and isolate the results. Do not rely on DNS cache to expire naturally.

When fixing, restore the previous signed policy and requester, first verify mixed addresses, rebinding, and per-hop redirects in the regression environment, then reopen domains in small batches. If the new validator falsely rejects, a rollback can only return to the old controlled allowlist; do not temporarily switch to arbitrary URLs. Network tools failing closed reduces some functionality, but it is far easier to recover from than letting the agent become an intranet proxy.

Further reading

Related

Related Content