From grimoire
Validates URLs with allowlists and IP-range blocking before making server-side HTTP requests. Use for web scrapers, webhook receivers, URL preview services, and import-from-URL features.
How this skill is triggered — by the user, by Claude, or both
Slash command
/grimoire:prevent-ssrfThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Block server-side request forgery by validating and allowlisting URLs before making server-side HTTP requests — preventing attackers from using your server as a proxy to reach internal metadata endpoints, databases, or cloud infrastructure.
Block server-side request forgery by validating and allowlisting URLs before making server-side HTTP requests — preventing attackers from using your server as a proxy to reach internal metadata endpoints, databases, or cloud infrastructure.
Adopted by: OWASP added SSRF as its own category in Top 10 2021 (A10) due to increasing severity in cloud environments. AWS, GCP, and Azure now offer IMDS v2 (token-required metadata endpoints) specifically to mitigate SSRF. Google exposed $31,337 in SSRF bug bounties. Capital One's 2019 breach (100M records) was executed entirely via SSRF exploiting AWS EC2 metadata at 169.254.169.254.
Impact: In cloud environments, SSRF can reach the instance metadata service (IMDS) and retrieve IAM credentials, enabling full cloud account takeover without any direct database access. Capital One breach (2019) affected 100M+ records via a WAF SSRF vulnerability. Netflix, GitLab, and Shopify have all paid significant SSRF bug bounties. In containerized environments, SSRF can reach Kubernetes API servers on internal networks.
Why best: Denylist-based approaches (blocking 169.254.169.254, localhost, 127.0.0.1) fail because attackers bypass them with DNS rebinding, IPv6 addresses (::1, ::ffff:127.0.0.1), decimal encoding (2130706433 = 127.0.0.1), or redirects. Allowlist-based validation (permitting only known external domains) is the only reliable defense.
Sources: OWASP SSRF Prevention Cheat Sheet; Capital One breach report (2019); AWS IMDS v2 documentation; CWE-918
Use an allowlist of approved URL patterns — define which domains/URLs your feature should reach:
ALLOWED_DOMAINS = {'api.example.com', 'uploads.example.com', 'cdn.partner.com'}
from urllib.parse import urlparse
import ipaddress
def validate_url(url):
parsed = urlparse(url)
if parsed.scheme not in ('http', 'https'):
raise ValueError("Only HTTP/HTTPS allowed")
hostname = parsed.hostname
if hostname not in ALLOWED_DOMAINS:
raise ValueError(f"Domain not allowed: {hostname}")
return url
Block all private and link-local IP ranges after DNS resolution — resolve the hostname and check the resulting IP, not just the domain name:
import socket
import ipaddress
BLOCKED_RANGES = [
ipaddress.ip_network('10.0.0.0/8'),
ipaddress.ip_network('172.16.0.0/12'),
ipaddress.ip_network('192.168.0.0/16'),
ipaddress.ip_network('127.0.0.0/8'),
ipaddress.ip_network('169.254.0.0/16'), # AWS/GCP/Azure IMDS
ipaddress.ip_network('::1/128'),
ipaddress.ip_network('fc00::/7'),
]
def is_safe_ip(hostname):
try:
ip = ipaddress.ip_address(socket.gethostbyname(hostname))
except (socket.gaierror, ValueError):
return False
return not any(ip in net for net in BLOCKED_RANGES)
Prevent DNS rebinding — resolve DNS once and use the resolved IP for the actual connection:
import requests
from requests.adapters import HTTPAdapter
class SSRFSafeAdapter(HTTPAdapter):
def send(self, request, **kwargs):
# Resolve and validate before sending
hostname = urlparse(request.url).hostname
if not is_safe_ip(hostname):
raise SecurityError("Blocked SSRF attempt")
return super().send(request, **kwargs)
session = requests.Session()
session.mount('http://', SSRFSafeAdapter())
session.mount('https://', SSRFSafeAdapter())
Disable HTTP redirects, or re-validate after each redirect:
# Option A: disable redirects entirely
response = requests.get(url, allow_redirects=False)
# Option B: validate each redirect destination
def safe_get(url, max_redirects=3):
for _ in range(max_redirects):
resp = requests.get(url, allow_redirects=False)
if resp.is_redirect:
url = resp.headers['Location']
validate_url(url) # re-validate the redirect target
else:
return resp
raise SecurityError("Too many redirects")
In cloud environments, enforce IMDS v2 — IMDSv2 requires a PUT request to obtain a token before accessing metadata. Even if SSRF reaches the endpoint, the attacker cannot get credentials without the token step.
AWS: Set HttpTokens: required on EC2 instance metadata options.
GCP: Block metadata.google.internal via VPC firewall rules.
Azure: Enable IMDS v2 (managed identity with required tokens).
Use an isolated outbound proxy for user-facing fetch features — route all user-driven HTTP requests through a dedicated proxy with strict allowlist enforcement, separate from your internal service traffic. Prevents SSRF from reaching internal microservices.
http://169.254.169.254 will bypass hostname-only checks.file:///etc/passwd) and other non-HTTP schemes must be blocked explicitly.if '169.254' in url is bypassed by http://0251.0376.0251.0376/ (octal encoding).npx claudepluginhub jeffreytse/grimoire --plugin grimoireDetects SSRF vulnerabilities in HTTP requests to user-controlled URLs. Invoke when implementing webhook receivers, URL preview features, or proxying user-supplied URLs.
Detects Server-Side Request Forgery (SSRF) vulnerabilities where user-controlled URLs access internal services, cloud metadata, or bypass networks in JS/TS, Python, Go, Ruby code. Audits webhooks, URL previews, imports.
Identifies and exploits SSRF vulnerabilities to access internal services, cloud metadata, and restricted network resources during authorized penetration tests.