Skip to content
Back to skills

Ssrf Prevention

ASecurity

Server-Side Request Forgery: allowlisting the destination of a server-side fetch, when re-resolution between check and connect matters, redirect and scheme bypasses, parsers that fetch on their own (XXE, SVG, HTML-to-PDF), cloud metadata, and keeping the response from becoming an oracle. Use when fetching a client-supplied URL, wiring webhooks, image proxies, PDF or preview renderers, or reviewing any HTTP-client wrapper.

  • 22 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added September 6, 2026
ai-agentstypescriptrustgoawsapisecuritydocumentation

Works with

  • cli
  • api

Security analysis

A100/100

Pro scans all 6 files and shows the line behind each finding

Scanned September 6, 2026

npx -y skills add ShieldNet-360/secure-vibe --skill ssrf-prevention --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Ssrf Prevention?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Ssrf Prevention
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/shieldnet-360-ssrf-prevention-c39d800c/badge)](https://www.skillsdirectory.com/skills/shieldnet-360-ssrf-prevention-c39d800c)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
id: ssrf-prevention
version: "2.0.0"
title: "SSRF Prevention"
description: "Server-Side Request Forgery: allowlisting the destination of a server-side fetch, when re-resolution between check and connect matters, redirect and scheme bypasses, parsers that fetch on their own (XXE, SVG, HTML-to-PDF), cloud metadata, and keeping the response from becoming an oracle. Use when fetching a client-supplied URL, wiring webhooks, image proxies, PDF or preview renderers, or reviewing any HTTP-client wrapper."
category: prevention
severity: critical
applies_to:
  - "when generating code that fetches a URL supplied or influenced by a client"
  - "when wiring webhooks, image proxies, PDF renderers, link previews, oEmbed fetchers"
  - "when parsing user-supplied XML, SVG, HTML, or Markdown that a renderer may resolve"
  - "when running in a cloud environment with an instance metadata service"
  - "when reviewing a URL-parsing or HTTP-client wrapper"
languages: ["*"]
token_budget:
  minimal: 1400
  compact: 1800
  full: 2400
rules_path: "rules/"
related_skills: ["api-security", "deserialization-security", "file-upload-security", "iac-security"]
last_updated: "2026-08-14"
sources:
  - "OWASP SSRF Prevention Cheat Sheet"
  - "CWE-918: Server-Side Request Forgery"
  - "AWS IMDSv2 documentation"
  - "PortSwigger Web Security Academy — SSRF labs"
---

# SSRF Prevention

## Rules (for AI agents)

### ALWAYS
- Constrain the destination with an **allowlist of hosts you intend to reach**, not a
  denylist of addresses you intend to avoid. A denylist has to enumerate every spelling
  of every internal address — decimal and octal IPv4, IPv6 and IPv4-mapped IPv6, a
  hostname whose A record is private, a public wildcard resolver like `nip.io` that
  encodes any address into a name — and it only has to be wrong once.
- Know which of the two controls you built, because it decides whether re-resolution
  matters. An **allowlist of hostnames** survives DNS rebinding: the attacker controls
  DNS only for names they own, and those names are not on the list. An **IP-range
  check** does not: the name resolves to a public address when you validate and a
  private one when you connect, and the two resolutions are independent. If your
  control is address-based, resolve once and **connect to that pinned address** —
  a custom dialer or transport, carrying the original hostname for SNI and `Host` —
  so no second lookup can occur.
- Re-validate **at every redirect hop**, or disable redirect-following entirely on
  fetchers that take user-supplied URLs. An allowed host answering `302` to
  `http://169.254.169.254/` is the single most common bypass, and it defeats a check
  performed only on the URL the user submitted.
- Restrict the scheme to `http` and `https` before anything else. `file://`,
  `gopher://`, `dict://`, `ldap://`, `jar://` and friends turn a fetcher into a file
  reader or a protocol-smuggling primitive.
- Keep the **user-URL fetcher and the internal fetcher as separate clients**, and make
  mixing them fail to compile rather than fail at runtime — distinct types in Go, Rust
  or TypeScript. A single client used for both eventually gets called with the wrong
  argument.
- Treat a **parser as a fetcher**. XML entity resolution, SVG with external references,
  HTML-to-PDF renderers, Markdown image embedding, oEmbed and link-preview
  unfurlers, and office-document converters all issue outbound requests from URLs
  inside the document, using no code you wrote. `deserialization-security` owns
  disabling external entities; what belongs here is that the request those parsers
  make is a server-side fetch and needs the same allowlist and the same egress policy.
- Decide what the **response** may reveal. Returning the body, the status code, the
  redirect chain, the content type, or the elapsed time to the caller turns a blind
  SSRF into a readable one. Return a fixed error on failure and never echo the URL or
  the fetch error back — `error-handling-security` owns the shape of that response.
- Enforce **IMDSv2** on EC2 (session token required, hop limit 1) so a plain `GET`
  from a compromised process cannot read instance credentials, and give the workload
  its own identity so the metadata service is not a credential worth reaching.
  Endpoints for each cloud, and the egress rules that go with them, are in
  `references/metadata-and-egress.md`.

### NEVER
- Treat "this service is internal / behind a VPN / on an allowlisted network" as
  mitigation. SSRF originates *inside* that network, so it reaches exactly the services
  the network control was protecting. Reachability is not authentication.
- Ship a forwarder that switches on the shape of its input —
  `path.startsWith('http') ? path : base + path`. It is a latent SSRF that activates
  the day any caller passes user-influenced input.
- Rely on an egress proxy or metadata-blocking sidecar as the **only** control. It
  constrains where traffic goes, not what the application asks for, and it is bypassed
  by any request path that does not traverse it.
- Compare a URL as a **string** — prefix match, `contains`, a regex over the raw text.
  Parse it, then compare the parsed host. `https://allowed.example.com@evil.com/`,
  `https://evil.com/?x=allowed.example.com` and `https://allowed.example.com.evil.com/`
  all defeat string matching and none defeat host comparison.
- Accept a hostname without normalizing IDN / Punycode first. Homograph forms compare
  unequal to the ASCII name they imitate, so a naive allowlist check passes them
  straight through.
- Parse the URL with one library and fetch it with another. Differential parsing
  between WHATWG and RFC 3986 implementations is a documented bypass class: the
  validator and the client disagree about which part is the host.

### KNOWN FALSE POSITIVES
- A URL that is a **configuration constant** — an operator-set integration endpoint,
  a hard-coded base — is not user input. The static config is the allowlist.
- A forwarder that appends only a path segment or query string to a **literal** base
  cannot change host, so it is not SSRF. The finding is an absolute URL that user
  input can influence.
- An outbound webhook **to a customer-supplied URL** is the intended feature of a
  webhook system; the destination is meant to be arbitrary. It still needs the private
  and link-local ranges blocked, redirects disabled, and the response withheld from
  the caller — the delivery result may be reported, never the body.
- A fetch that resolves to a private address is expected when the service's job is to
  reach an internal host — a monitoring prober, a health checker. The control there is
  that the *destination set* is operator-defined, not that the address is public.

## Context (for humans)

SSRF is worth more to an attacker than its description suggests, because the server
making the request is authenticated to things the attacker is not. Cloud instance
metadata is the famous case — a plain `GET` returning role credentials — but internal
admin panels, unauthenticated RPC endpoints, service registries and Redis all sit on
the same side of the same boundary. The vulnerability is not that the server fetched a
URL; it is that the server's network position was standing in for authentication.

The distinction the rules spend the most words on is allowlist-of-hosts versus
address-checking, because it decides whether DNS rebinding is a real threat or a
theoretical one, and most generated validators mix the two: they check the address is
public *and* the host is expected, then hand the original URL to a client that resolves
it again. Which of the two checks was load-bearing determines whether that second
resolution matters.

The class most often missing entirely is the parser that fetches on its own behalf. No
code in the application calls an HTTP client, so nothing about the code looks like a
fetcher — the URL is inside the document, and the library resolves it.

## References

- `references/verifying-findings.md` — confirm or refute a finding, then lock it
- `references/metadata-and-egress.md` — metadata endpoints per cloud and how each is
  hardened, the reserved and private ranges in both address families, pinned-dialer
  implementations per language, and the parsers that fetch
- `rules/ssrf_sinks.json`
- `rules/cloud_metadata_endpoints.json`
- [OWASP SSRF Prevention Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Server_Side_Request_Forgery_Prevention_Cheat_Sheet.html).
- [CWE-918](https://cwe.mitre.org/data/definitions/918.html).
- [AWS IMDSv2](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/configuring-instance-metadata-service.html).
- [PortSwigger SSRF](https://portswigger.net/web-security/ssrf).

Files in this skill

  • SKILL.md8.6 KB
  • references/metadata-and-egress.md6.4 KB
  • references/verifying-findings.md2.5 KB
  • rules/cloud_metadata_endpoints.json2.3 KB
  • rules/ssrf_sinks.json4.8 KB
  • tests/corpus.json3.9 KB

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.