Skip to content

Commit 1c4b9d5

Browse files
committed
2 parents e43ab54 + 2d25339 commit 1c4b9d5

File tree

2 files changed

+108
-7
lines changed

2 files changed

+108
-7
lines changed

docs/docs/learn/client-concepts.mdx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ Roots define filesystem boundaries for server operations, allowing clients to sp
108108

109109
#### Overview
110110

111-
Roots are a mechanism for clients to communicate filesystem access boundaries to servers. They consist of file URIs that indicate directories where servers can operate, helping servers understand the scope of available files and folders. Rather than giving servers unrestricted filesystem access, roots guide them to relevant working directories. While roots communicate intended boundaries, actual security is always maintained by the client's access controls.
111+
Roots are a mechanism for clients to communicate filesystem access boundaries to servers. They consist of file URIs that indicate directories where servers can operate, helping servers understand the scope of available files and folders. While roots communicate intended boundaries, they do not enforce security restrictions. Actual security must be enforced at the operating system level, via file permissions and/or sandboxing.
112112

113113
**Root structure:**
114114

@@ -139,7 +139,7 @@ For a complete implementation of a server that respects roots, see the [filesyst
139139

140140
#### Design Philosophy
141141

142-
Roots serve as a coordination mechanism between clients and servers, not a security boundary. The specification requires that servers "SHOULD respect root boundaries," and not that they "MUST enforce" them, because servers run code the client cannot control. This design is pragmatic: clients enforce security while roots communicate intent.
142+
Roots serve as a coordination mechanism between clients and servers, not a security boundary. The specification requires that servers "SHOULD respect root boundaries," and not that they "MUST enforce" them, because servers run code the client cannot control.
143143

144144
Roots work best when servers are trusted or vetted, users understand their advisory nature, and the goal is preventing accidents rather than stopping malicious behavior. They excel at context scoping (telling servers where to focus), accident prevention (helping well-behaved servers stay in bounds), and workflow organization (such as managing project boundaries automatically).
145145

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)