How to secure MCP OAuth discovery against SSRF

7 min read

A practical review plan for hosted MCP clients: validate discovery destinations, bind DNS checks to connections, control redirects and test with mock services.

On this page

To secure MCP OAuth discovery against server-side request forgery (SSRF), apply an outbound destination policy before every network request, then validate the metadata returned. Check redirects and the addresses the transport actually connects to. A valid-looking issuer or HTTPS URL alone does not establish that a destination belongs within your application's network permissions.

This guide is for engineers running an MCP client on a backend, especially when users can connect remote MCP servers. In that arrangement, the client backend performs discovery and exposes its own network reach. The steps below are an implementation review plan, not a tested SDK patch or a claim about a particular product.

Map the discovery requests first

The current MCP discovery specification distinguishes two documents. Protected-resource metadata identifies the MCP resource and its authorization servers. Authorization-server metadata describes an issuer's endpoints and capabilities.

A client uses the parsed resource_metadata URL in a WWW-Authenticate header when present. Otherwise, it tries the specified path-specific and root well-known resource-metadata locations. It then discovers metadata for the selected issuer. Follow the protocol's candidate ordering, including issuer paths; simply appending one fixed suffix to every URL is insufficient.

Inventory each actual outbound request: initial MCP connection, resource-metadata retrieval, issuer discovery, redirects and later token, registration or key retrieval where supported. An authorization endpoint usually opens in the user's browser rather than being fetched by the backend. That URL still needs appropriate validation, but browser navigation and backend SSRF are different boundaries.

1. Choose an operator-controlled network policy

Start by deciding which destinations this deployment needs. OWASP's SSRF prevention guidance distinguishes integrations with known trusted applications from applications that must contact arbitrary external services. The controls should reflect that difference.

Deployment profileSuggested policyWho can change it?
Public discoveryPermit approved protocols and ports to public destinations. Deny loopback, private, link-local and other restricted destinations.The service operator through reviewed configuration.
Managed private integrationPermit specifically approved internal hosts, ports and network ranges, with matching egress restrictions.The operator responsible for that private integration.

These profiles are a suggested design, not protocol-defined modes. Metadata must not select the private profile, add a network exception or switch off validation. A company may legitimately run a private MCP service; that does not justify letting an arbitrary public server choose destinations inside the company's network.

2. Parse URLs and apply the correct protocol rules

Use a maintained URL parser with semantics consistent with the HTTP stack. For a defensive discovery-fetch policy, reject embedded credentials, ambiguous hosts, unsupported schemes and unexpected fragments before I/O. Define allowed ports explicitly. These are deployment choices; do not label every local restriction an OAuth requirement.

Some rules are specific to particular fields. RFC 8414 requires an issuer identifier to use HTTPS and contain no query or fragment. That does not mean every OAuth endpoint URL must reject all query parameters. The MCP communication-security requirements require HTTPS for authorization-server endpoints.

Keep native-app callbacks separate. RFC 8252 permits loopback redirect URIs for native applications receiving an authorization response. This is not permission for a hosted client's metadata fetcher to contact arbitrary localhost services. Any development exception should be explicit, narrow and absent from the public production profile.

3. Bind the connection to a validated destination

A hostname check is incomplete if the transport subsequently resolves the name again without checking the result. The address can change between validation and connection. Review IPv4 and IPv6 results, including alternate addresses the client might try after a connection failure.

OWASP's domain-name guidance calls for validating resolved addresses and binding the connection to an approved address while preserving the hostname for HTTP Host, TLS SNI and certificate verification. Do not disable certificate checks to make address binding work. Use established address parsing and classification rather than a hand-written blacklist of a few familiar IP strings.

Make this a transport-level property. The review question is: can a retry, alternate address, proxy route or redirect reach a destination that the policy did not approve? A check performed only when a user saves a server URL does not answer that question.

4. Control redirects and enforce egress restrictions

The MCP SSRF guidance requires server-deployed clients to consider SSRF and implement appropriate mitigations. It recommends validating redirect destinations and considering an egress proxy. The appropriate protections depend on the network environment.

A straightforward fetch policy disables automatic redirects. If the integration needs them, validate each new destination before connecting and impose a finite redirect budget. Do not let an HTTPS public URL redirect into a private service or downgrade the request to HTTP. Keep discovery requests free of inherited resource tokens, ambient cookies and unrelated client credentials.

Apply matching controls at an egress proxy or network boundary. Check which component resolves DNS and opens the connection when a proxy is involved. A restrictive application check is of little help if another outbound route bypasses it. The aim is a controlled path whose behavior can be observed, not a list of blocked strings.

5. Validate metadata identity after the safe fetch

Network permission and metadata identity are separate checks. RFC 9728 requires the returned resource identifier to be identical to the expected resource. For well-known discovery, compare it with the resource identifier used to construct that URL. For header-directed discovery, compare it with the URL used to request the resource server. A mismatch means the metadata must not be used.

Likewise, authorization-server metadata validation requires the returned issuer to be identical to the issuer used to construct the discovery URL. Keep the expected value separately; replacing it with whatever the response says defeats the comparison.

These checks happen after retrieval, so they cannot prevent an unsafe request that already occurred. Also, issuer equality is not a universal same-origin rule for every advertised endpoint. Validate each endpoint against the applicable protocol and deployment policy rather than assuming either that every different host is invalid or that every advertised host is trusted.

6. Define bounded failure before rollout

Set response-size, connection-time, total-time and retry limits for the discovery client. Validate the expected JSON structure and required fields. Pick limits from your service's requirements and test them; this article does not prescribe a universal timeout or byte count.

Return a controlled failure when discovery is blocked or invalid. Do not automatically retry through a less-restricted HTTP client. Avoid reflecting fetched response bodies or internal network details to an untrusted server. Log a case identifier, policy decision and sanitized destination information sufficient for an operator to investigate, without recording credentials.

Treat changed metadata as new input. A cached document is not permanent permission to reach an address. RFC 9728 describes normal HTTP metadata caching; connection policy still applies when using its endpoints. Keep a record of the tested client version, resolver, proxy, network policy and any private-integration exceptions.

Worked example: a redirect changes the decision

Consider a fictional hosted assistant connecting to a document MCP server. Its public-discovery profile permits an HTTPS metadata endpoint. In a test fixture, that endpoint returns a redirect to a hostname classified by the mock resolver as internal.

The first URL's approval does not cover the second destination. The fetcher evaluates the redirect against the same profile, rejects it and records a policy reason. The mock internal service receives no connection. The assistant reports that this server's authorization configuration could not be used, rather than displaying a raw internal response.

If the customer actually needs an internal identity provider, an operator can configure a separate reviewed private integration. The original metadata cannot grant itself that exception. This distinction preserves a useful private deployment without turning a public connection feature into general internal-network access.

Test with a fake resolver and transport

Use dependency-injected DNS and HTTP components, invented .test hostnames and synthetic JSON. The cases below need no live probes, real tokens or requests to cloud metadata services. A mock can record attempted connections and supply the destination classifications required by each case.

CaseEvidence to inspect
Valid public discoveryExpected header or well-known path selection, valid resource/issuer values and the permitted mock connection.
Restricted IPv4 or IPv6 destinationRejection before a socket attempt under the public profile.
Mixed allowed and blocked DNS answersNo fallback connection can use the blocked answer.
DNS answer changes after validationThe transport uses the approved resolution or rejects the changed destination.
Redirect to a restricted destination or HTTPNo second connection; bounded handling of redirect loops.
Resource or issuer mismatchA safe fetch can complete, but its metadata is rejected.
Approved private integrationThe specified mock host works; an adjacent unapproved host remains blocked.
Oversized, slow or malformed responseBounded failure without credential leakage or an unrestricted retry.

Save expected and observed decisions, the component enforcing each restriction and the mock connection log. Mock tests check intended control flow; they do not establish how the real HTTP client resolves names or uses a proxy. Before production, separately validate that adapter and egress policy in an isolated network you own and are authorized to test, using synthetic services. Mark unexercised paths as untested. These proposed cases are not proof that an SDK, service or HOL product prevents every SSRF attack.

Continue with safe permission testing for the surrounding environment and MCP token-boundary design for credentials used after discovery. The broader secure MCP guide covers client and action-surface review.

Sources reviewed November 5, 2026. Protocol references use MCP 2026-07-28. Deployment profiles, worked example and test plan are illustrative recommendations. No network security tests were executed for this article.

All articles

Keep exploring

The engineering desk continues across the ecosystem: security research on shipping agents safely, and plugins that put the standards to work.