Skip to content

Commit 2d25339

Browse files
localdenpcarleton
andauthored
(docs): Update consent prompt guidelines for MCP servers (modelcontextprotocol#1587)
* Update security_best_practices.mdx * Update security_best_practices.mdx * Update security_best_practices.mdx * Update security_best_practices.mdx * Update security_best_practices.mdx * Update security_best_practices.mdx * Update docs/specification/draft/basic/security_best_practices.mdx Co-authored-by: Paul Carleton <[email protected]> * Update docs/specification/draft/basic/security_best_practices.mdx Co-authored-by: Paul Carleton <[email protected]> --------- Co-authored-by: Paul Carleton <[email protected]>
1 parent f646f59 commit 2d25339

File tree

1 file changed

+106
-5
lines changed

1 file changed

+106
-5
lines changed

docs/specification/draft/basic/security_best_practices.mdx

Lines changed: 106 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ This section gives a detailed description of attacks on MCP implementations, alo
1818

1919
### Confused Deputy Problem
2020

21-
Attackers can exploit MCP servers proxying other resource servers, creating "[confused deputy](https://en.wikipedia.org/wiki/Confused_deputy_problem)" vulnerabilities.
21+
Attackers can exploit MCP proxy servers that connect to third-party APIs, creating "[confused deputy](https://en.wikipedia.org/wiki/Confused_deputy_problem)" vulnerabilities. This attack allows malicious clients to obtain authorization codes without proper user consent by exploiting the combination of static client IDs, dynamic client registration, and consent cookies.
2222

2323
#### Terminology
2424

@@ -38,6 +38,15 @@ the third-party authorization server. This Client ID refers to the MCP server ac
3838
to the Third-Party API. It is the same value for all MCP server to Third-Party API interactions regardless of
3939
which MCP client initiated the request.
4040

41+
#### Vulnerable Conditions
42+
43+
This attack becomes possible when all of the following conditions are present:
44+
45+
- MCP proxy server uses a **static client ID** with a third-party authorization server
46+
- MCP proxy server allows MCP clients to **dynamically register** (each getting their own client_id)
47+
- The third-party authorization server sets a **consent cookie** after the first authorization
48+
- MCP proxy server does not implement proper per-client consent before forwarding to third-party authorization
49+
4150
#### Architecture and Attack Flows
4251

4352
##### Normal OAuth proxy usage (preserves user consent)
@@ -99,8 +108,7 @@ sequenceDiagram
99108
#### Attack Description
100109

101110
When an MCP proxy server uses a static client ID to authenticate with a third-party
102-
authorization server that does not support dynamic client registration, the following
103-
attack becomes possible:
111+
authorization server, the following attack becomes possible:
104112

105113
1. A user authenticates normally through the MCP proxy server to access the third-party API
106114
2. During this flow, the third-party authorization server sets a cookie on the user agent
@@ -114,8 +122,101 @@ attack becomes possible:
114122

115123
#### Mitigation
116124

117-
MCP proxy servers using static client IDs **MUST** obtain user consent for each dynamically
118-
registered client before forwarding to third-party authorization servers (which may require additional consent).
125+
To prevent confused deputy attacks, MCP proxy servers **MUST** implement per-client consent and proper security controls as detailed below.
126+
127+
##### Consent Flow Implementation
128+
129+
The following diagram shows how to properly implement per-client consent that runs **before** the third-party authorization flow:
130+
131+
```mermaid
132+
sequenceDiagram
133+
participant Client as MCP Client
134+
participant Browser as User's Browser
135+
participant MCP as MCP Server
136+
participant ThirdParty as Third-Party AuthZ Server
137+
138+
Note over Client,ThirdParty: 1. Client Registration (Dynamic)
139+
Client->>MCP: Register with redirect_uri
140+
MCP-->>Client: client_id
141+
142+
Note over Client,ThirdParty: 2. Authorization Request
143+
Client->>Browser: Open MCP server authorization URL
144+
Browser->>MCP: GET /authorize?client_id=...&redirect_uri=...
145+
146+
alt Check MCP Server Consent
147+
MCP->>MCP: Check consent for this client_id
148+
Note over MCP: Not previously approved
149+
end
150+
151+
MCP->>Browser: Show MCP server-owned consent page
152+
Note over Browser: "Allow [Client Name] to access [Third-Party API]?"
153+
Browser->>MCP: POST /consent (approve)
154+
MCP->>MCP: Store consent decision for client_id
155+
156+
Note over Client,ThirdParty: 3. Forward to Third-Party
157+
MCP->>Browser: Redirect to third-party /authorize
158+
Note over MCP: Use static client_id for third-party
159+
160+
Browser->>ThirdParty: Authorization request (static client_id)
161+
ThirdParty->>Browser: User authenticates & consents
162+
ThirdParty->>Browser: Redirect with auth code
163+
164+
Browser->>MCP: Callback with third-party code
165+
MCP->>ThirdParty: Exchange code for token (using static client_id)
166+
MCP->>Browser: Redirect to client's registered redirect_uri
167+
```
168+
169+
##### Required Protections
170+
171+
**Per-Client Consent Storage**
172+
173+
MCP proxy servers **MUST**:
174+
175+
- Maintain a registry of approved `client_id` values per user
176+
- Check this registry **before** initiating the third-party authorization flow
177+
- Store consent decisions securely (server-side database, or server specific cookies)
178+
179+
**Consent UI Requirements**
180+
181+
The MCP-level consent page **MUST**:
182+
183+
- Clearly identify the requesting MCP client by name
184+
- Display the specific third-party API scopes being requested
185+
- Show the registered `redirect_uri` where tokens will be sent
186+
- Implement CSRF protection (e.g., state parameter, CSRF tokens)
187+
- Prevent iframing via `frame-ancestors` CSP directive or `X-Frame-Options: DENY` to prevent clickjacking
188+
189+
**Consent Cookie Security**
190+
191+
If using cookies to track consent decisions, they **MUST**:
192+
193+
- Use `__Host-` prefix for cookie names
194+
- Set `Secure`, `HttpOnly`, and `SameSite=Lax` attributes
195+
- Be cryptographically signed or use server-side sessions
196+
- Bind to the specific `client_id` (not just "user has consented")
197+
198+
**Redirect URI Validation**
199+
200+
The MCP proxy server **MUST**:
201+
202+
- Validate that the `redirect_uri` in authorization requests exactly matches the registered URI
203+
- Reject requests if the `redirect_uri` has changed without re-registration
204+
- Use exact string matching (not pattern matching or wildcards)
205+
206+
**OAuth State Parameter Validation**
207+
208+
The OAuth `state` parameter is critical to prevent authorization code interception and CSRF attacks. Proper state validation ensures that consent approval at the authorization endpoint is enforced at the callback endpoint.
209+
210+
MCP proxy servers implementing OAuth flows **MUST**:
211+
212+
- Generate a cryptographically secure random `state` value for each authorization request
213+
- Store the `state` value server-side (in a secure session store or encrypted cookie) **only after** consent has been explicitly approved
214+
- Set the `state` tracking cookie/session **immediately before** redirecting to the third-party identity provider (not before consent approval)
215+
- Validate at the callback endpoint that the `state` query parameter exactly matches the stored value in the callback request's cookies or in the request's cookie-based session
216+
- Reject any callback requests where the `state` parameter is missing or does not match
217+
- Ensure `state` values are single-use (delete after validation) and have a short expiration time (e.g., 10 minutes)
218+
219+
The consent cookie or session containing the `state` value **MUST NOT** be set until **after** the user has approved the consent screen at the MCP server's authorization endpoint. Setting this cookie before consent approval renders the consent screen ineffective, as an attacker could bypass it by crafting a malicious authorization request.
119220

120221
### Token Passthrough
121222

0 commit comments

Comments
 (0)