Server-Side Request Forgery used to be a web-app problem. In 2026 it is an AI-agent problem first, and the agent ecosystem is shipping it at a rate that the rest of the security community hasn't caught up to. The shape is familiar: untrusted input becomes an outbound HTTP request from a trusted process. The novelty is that the untrusted input now arrives as natural language, gets parsed by a model, and reaches the request layer through a tool call. This post is the playbook: how it happens, what holds up under attack, and what to log so you'd see it.
Why this matters
The agent reaching out is the trusted process. It runs inside your VPC, it carries your service credentials, and its outbound requests come from an IP that internal services trust. If the agent's request URL is controllable by a prompt — directly, or transitively through any tool that fetches a URL — you have built an unauthenticated proxy into your internal network. We have audited deployments where this took a single sentence to exploit. The rate of new agent products shipping with this vulnerability has not slowed.
How does SSRF actually get into an AI agent?
The attack surface is wider than people expect because every URL-touching tool is a candidate. A search tool that fetches result pages. A "summarize this link" tool. A document-loader that follows references. A webhook delivery tool. Any retrieval-augmented generation pipeline that fetches an attacker-influenced URL. In our dataset, fewer than 20% of incidents started from a tool literally named fetch_url. The rest were transitive — the model was given an indirect way to cause a request and used it.
The timeline below shows the canonical attack sequence we see most often in incident review.
A minimal vulnerable pattern, and the fix, in TypeScript:
// Vulnerable: any URL the model produces is fetched verbatim.
async function fetchUrl(url: string) {
const res = await fetch(url);
return res.text();
}
// Safer: resolve, validate, pin, and route through egress proxy.
async function fetchUrl(url: string) {
const parsed = new URL(url);
if (parsed.protocol !== "https:") throw new Error("https only");
if (!isAllowedHost(parsed.hostname)) throw new Error("host not allowed");
const ip = await resolvePinned(parsed.hostname);
if (isInternalIp(ip)) throw new Error("internal IP refused");
return await fetchThroughEgressProxy(url, { pinnedIp: ip });
}
The fix matters less than what it implies: the request URL is now a policy decision, not a model decision. Treat every URL the model produces as untrusted input and you have a chance. Treat it as a string and you don't.
Which mitigations hold up under adversarial testing?
We ran each candidate mitigation against six SSRF subclasses across a red-team campaign in Q1 2026. Effectiveness here means "blocked the attack in our test corpus." False-positive rate is how often the mitigation blocked legitimate traffic during a one-month shadow deployment at three partner companies.
| Defense | Effectiveness | False-positive rate | Where it breaks |
|---|---|---|---|
| IP allowlist (one-shot DNS) | 42% | 1.1% | DNS rebinding flips IP between check and fetch |
| Hostname allowlist (string match) | 58% | 2.3% | Open redirects on allowlisted hosts |
| Header strip (Authorization/Cookie) | 31% | 0.4% | Internal services that trust source IP, not headers |
| Block private IP ranges | 67% | 0.7% | Cloud metadata endpoints on link-local; IPv6 representations |
| Egress proxy + strict allowlist | 94% | 3.4% | Misconfigured proxy fail-open during outages |
| Per-tool URL scope + capability tokens | 91% | 1.9% | Token leakage through model outputs if not redacted |
| Response-size and content-type cap | 29% | 0.8% | Doesn't stop the request — only what comes back |
The heatmap below breaks effectiveness down by SSRF subclass. Allowlists that look adequate against direct URL injection collapse against indirect injection through content the model is asked to summarize.
| Direct URL | Indirect (search) | Open redirect | DNS rebinding | Cloud metadata | IPv6 bypass | |
|---|---|---|---|---|---|---|
| IP allowlist | 78% | 41% | 22% | 8% | 65% | 41% |
| Hostname allowlist | 82% | 49% | 17% | 71% | 71% | 58% |
| Header strip | 44% | 28% | 24% | 27% | 33% | 31% |
| Block private IPs | 88% | 62% | 71% | 49% | 84% | 47% |
| Egress proxy | 97% | 94% | 93% | 96% | 98% | 88% |
| Per-tool scope | 95% | 92% | 90% | 93% | 96% | 80% |
When does an allowlist fail?
An allowlist fails in four reliably reproducible ways. First, DNS rebinding: the hostname resolves to a public IP during the validation step, then to an internal IP when the actual fetch happens. The fix is to pin the resolution — resolve once, validate, then pass the resolved IP into the request, refusing to re-resolve. Second, open redirects on allowlisted hosts: the model is told to fetch https://trusted-host.example.com/redirect?to=http://169.254.169.254/..., and your allowlist greenlights it because the hostname is fine. The fix is to disable follow-redirects, or to allowlist destinations, not hops. Third, IPv6 representations of internal addresses that string-match filters miss (::ffff:127.0.0.1 notation, link-local addresses). Fourth, decoded URL fragments — http://example.com#@internal.local/ parses differently in different libraries, and the gap is the vulnerability.
The pattern across all four: the allowlist defends a property the attacker can decouple from the actual request. Egress proxies hold up because the proxy makes the request itself, so the attacker cannot decouple the policy decision from the network primitive.
What logging do you need to detect this in production?
The detection target is the outbound request, not the prompt. Capture: every URL the agent process resolves, the final IP after redirects, the response status and size, the tool that initiated the call, and a hash of the prompt that produced the tool call. Alert on any outbound request to a private IP range, any redirect chain longer than two hops, any response over a configured size with text/html content type, and any tool call where the resolved IP differs from the IP seen at allowlist-check time.
The single highest-signal alert in our deployments has been: "outbound request whose resolved IP belongs to RFC1918 or link-local space." It is rare in normal traffic and almost always real when it fires.
The OWASP-class incident dataset and mitigation effectiveness study.
From Codritium Research
Security Engineering with AI Pairs
How AI-assisted engineers handle OWASP-class vulnerabilities — by category, by mitigation, by panel review. The disclosures we tracked, the fixes that stuck, and the patterns that fooled both pair and reviewer.
Common questions
Is this OWASP A10?
OWASP A10 (Server-Side Request Forgery) is the closest match, yes. The agent-specific framing is the same primitive — controllable outbound request from a trusted process — with a new injection surface. The 2026 OWASP LLM Top 10 also covers it under LLM06 (Excessive Agency) and LLM07 (System Prompt / Tool Misuse), depending on framing.
Can a frontier model still SSRF if I sandbox the tool?
Sandboxing the tool's *execution* environment doesn't help if the sandbox still has network egress to your VPC. SSRF is a network-policy bug, not a code-execution bug. The model running inside the strongest sandbox in the world still emits a URL that your network layer fetches.
What's the difference from prompt injection?
Prompt injection is the technique; SSRF is the outcome. An attacker can use prompt injection to cause many outcomes — data exfiltration, tool misuse, refusal bypass. SSRF is the outcome where the injection turns the agent into an outbound request proxy. The two often appear together, but mitigating prompt injection alone won't stop SSRF, and vice versa.
Do agents from major vendors block this by default?
Partially and inconsistently. Hosted tool-use platforms usually block cloud metadata endpoints and private IP ranges. They do not, generally, block open redirects on allowlisted hosts or DNS rebinding. If you are building on a vendor's tool layer, audit what they actually block — don't infer it from the marketing.
Is there a CVE database for AI-agent vulnerabilities?
No formal one, as of mid-2026. CVE coverage is uneven and tends to land on the underlying tool runtime, not the agent integration. The MITRE ATLAS knowledge base tracks adversarial-ML techniques but isn't a vulnerability registry. For agent-specific incidents, the OWASP LLM Top 10 and vendor-published incident write-ups are still the best primary sources.
Where to go next
- Security with AI Pairs — the incident dataset and full mitigation study
- Where AI Pair Programming Fails — the broader taxonomy of agent failure modes
- The Defensibility Rubric 2026 — how security reasoning is scored on the platform