MCPSafe.io
RegistryThreatsMethodologyDocsPricingScanSign in
MCPSafe.io

Security checks for MCP servers — public packages and private repos, fast or deep.

Legal

Privacy PolicyCookie PolicyTerms of ServiceSecurity disclosure

Resources

State of MCP SecuritySupportSystem statusMade in Germany 🇩🇪

© 2026 MCPSafe. All rights reserved.

GDPR — Privacy Policy
← Threat Catalog

Server Implementation

Server-side request forgery (SSRF)

HIGHAIVSS 8.2CWE: CWE-918OWASP: LLM02Agentic: T02Rule: MCP-060

A tool takes a URL argument and fetches it server-side, so an attacker can reach internal services, cloud metadata, or the loopback interface through the MCP process.

What it is

SSRF is the bug where the server acts as an open HTTP proxy for whoever can call it. A fetch tool that accepts any URL will happily retrieve `http://169.254.169.254/latest/meta-data/` (AWS IMDS), `http://localhost:6379/` (a neighbouring Redis), or an internal admin panel that trusted its IP-based network boundary. The attack succeeds not because of a parser bug but because the server's *network position* is more privileged than the caller's.

Why it matters for MCP

"Fetch this URL" is one of the most commonly-shipped MCP tools — every browse-the-web, read-this-documentation, summarise-this-link server implements it. Those servers run inside VPCs, container networks, or developer laptops with access to private infrastructure the LLM's end-user never intended to expose. Indirect prompt injection can make the LLM issue the SSRF request without the user noticing.

Vulnerable example

example.py
1
@server.tool()
2
def fetch_page(url: str) -> str:
3
    # No allowlist, no CIDR check — reaches 169.254.169.254, 127.0.0.1, 10.0.0.0/8
4
    return requests.get(url, timeout=5).text

Secure example

example.py
1
import ipaddress, socket
2
from urllib.parse import urlparse
3
4
_BLOCKED = [ipaddress.ip_network(c) for c in
5
            ("127.0.0.0/8", "10.0.0.0/8", "172.16.0.0/12",
6
             "192.168.0.0/16", "169.254.0.0/16", "::1/128", "fc00::/7")]
7
8
@server.tool()
9
def fetch_page(url: str) -> str:
10
    parsed = urlparse(url)
11
    if parsed.scheme not in ("http", "https"):
12
        raise ValueError("only http(s) allowed")
13
    ip = ipaddress.ip_address(socket.gethostbyname(parsed.hostname))
14
    if any(ip in net for net in _BLOCKED):
15
        raise ValueError("private address not allowed")
16
    return requests.get(url, timeout=5, allow_redirects=False).text

How MCPSafe detects this

We flag HTTP clients (`requests`, `httpx`, `urllib`, `fetch`, `axios`) that take a tool-handler URL parameter without a reserved-CIDR check or an explicit allowlist. `allow_redirects=True` on an unvalidated URL is also flagged because follow-ups re-enable SSRF.

See the full threat catalog for every documented detection.

Framework alignment

OWASP LLM Top-10 (2025)
LLM02 — Sensitive Information Disclosure
OWASP Agentic AI Top-10
T02 — Tool Misuse
AIVSS v0.5
8.2 (HIGH)AIVSS:1.0/S:HIGH/AV:N/AU:M/BR:H/CD:I

Illustrative CVEs

CVEs of the same CWE class. Not MCP-specific, but exemplify the failure mode MCPSafe detects.

  • CVE-2021-22214 — GitLab unauthenticated SSRF — CI webhook reached internal services

Further reading

  • CWE-918: Server-Side Request Forgery
  • OWASP SSRF Prevention

Scan an MCP server for this issue

MCPSafe runs this check — and every other rule in the catalog — on any MCP server you paste in.

Scan now