{"record":{"id":"3ea034713ae9216d","repo":"PrefectHQ/fastmcp","slug":"cimd-jwks-uri-failed-ssrf-validation-e","errorCode":null,"errorMessage":"CIMD jwks_uri failed SSRF validation: {e}","messagePattern":"CIMD jwks_uri failed SSRF validation: (.+?)","errorType":"validation","errorClass":"CIMDValidationError","httpStatus":null,"severity":"error","filePath":"fastmcp_slim/fastmcp/server/auth/cimd.py","lineNumber":429,"sourceCode":"\n        try:\n            doc = CIMDDocument.model_validate(data)\n        except Exception as e:\n            raise CIMDValidationError(f\"Invalid CIMD document: {e}\") from e\n\n        if str(doc.client_id).rstrip(\"/\") != client_id_url.rstrip(\"/\"):\n            raise CIMDValidationError(\n                f\"CIMD client_id mismatch: document says '{doc.client_id}' \"\n                f\"but was fetched from '{client_id_url}'\"\n            )\n\n        # Validate jwks_uri if present (SSRF check for JWKS endpoint)\n        if doc.jwks_uri:\n            jwks_uri_str = str(doc.jwks_uri)\n            try:\n                await validate_url(jwks_uri_str)\n            except SSRFError as e:\n                raise CIMDValidationError(\n                    f\"CIMD jwks_uri failed SSRF validation: {e}\"\n                ) from e\n\n        logger.info(\n            \"CIMD document fetched and validated: %s (client_name=%s)\",\n            client_id_url,\n            doc.client_name,\n        )\n\n        if not policy.no_store:\n            self._store_cache_entry(\n                client_id_url,\n                _CIMDCacheEntry(\n                    doc=doc,\n                    etag=policy.etag,\n                    last_modified=policy.last_modified,\n                    expires_at=policy.expires_at,\n                    freshness_lifetime=policy.freshness_lifetime,","sourceCodeStart":411,"sourceCodeEnd":447,"githubUrl":"https://github.com/PrefectHQ/fastmcp/blob/1f021142978e0861cd910c8df4e8074bc7cf3978/fastmcp_slim/fastmcp/server/auth/cimd.py#L411-L447","documentation":"When the CIMD document declares a jwks_uri, fetch() applies the same SSRF validation to that JWKS URL as to the client_id URL. If validate_url raises SSRFError (private IP, blocked scheme, DNS pinning failure), the error is wrapped in CIMDValidationError so a hostile document cannot make the server fetch internal endpoints.","triggerScenarios":"A fetched CIMD document contains jwks_uri pointing at localhost, 127.0.0.1, RFC1918 addresses, metadata services (169.254.169.254), or a non-HTTP scheme; detected during fetch/get_client.","commonSituations":"Malicious or misconfigured client documents attempting SSRF via jwks_uri; internally-hosted JWKS endpoints that worked in dev but are blocked in production; documents authored before SSRF protections were added.","solutions":["Host the JWKS at a public HTTPS URL and update jwks_uri in the CIMD document","Use inline 'jwks' (a JWK Set object) in the document instead of jwks_uri if the keys are small","If legitimately internal, configure the SSRF allowlist in fastmcp.server.auth.ssrf to permit that endpoint","Treat repeated occurrences as an attack signal; the validation exists to block SSRF via client-supplied URLs"],"exampleFix":"// before\n\"jwks_uri\": \"http://localhost:8080/jwks.json\"\n// after\n\"jwks_uri\": \"https://keys.app.example.com/jwks.json\"","handlingStrategy":"validation","validationCode":"from urllib.parse import urlparse\ndef jwks_uri_safe(u: str) -> bool:\n    p = urlparse(u)\n    host = p.hostname or \"\"\n    return p.scheme == \"https\" and not (\n        host in (\"localhost\", \"127.0.0.1\", \"169.254.169.254\")\n        or host.startswith(\"10.\") or host.startswith(\"192.168.\")\n        or host.startswith(\"172.16.\")\n    )","typeGuard":null,"tryCatchPattern":"try:\n    doc = await fetcher.get_client(client_id)\nexcept CIMDValidationError as e:\n    if \"jwks_uri failed SSRF\" in str(e):\n        raise InvalidClientError(\"jwks_uri must be a public HTTPS URL\") from e\n    raise","preventionTips":["Host JWKS on public HTTPS endpoints","Prefer inline 'jwks' for small key sets to avoid an extra fetch surface","Never point jwks_uri at internal/metadata services"],"tags":["oauth","cimd","ssrf","security","jwks"],"backgroundTag":"ssrf-blocked-url","analyzedSha":"1f021142978e0861cd910c8df4e8074bc7cf3978","analyzedAt":"2026-08-29T14:31:16.082Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}