Initial commit

This commit is contained in:
Zhongwei Li
2025-11-29 18:00:42 +08:00
commit cc49e355bc
37 changed files with 10917 additions and 0 deletions

View File

@@ -0,0 +1,382 @@
# JWT Security Best Practices
This reference provides comprehensive guidance for reviewing JWT (JSON Web Token) implementation and usage in pull requests.
## Critical Security Principles
### 1. Never Forward JWTs to Unintended Services
**The Problem:**
A JWT contains an `aud` (audience) claim that specifies which service(s) the token is intended for. Forwarding a JWT to a service not listed in the audience claim creates serious security vulnerabilities.
**Security Impact:**
- **Confused Deputy Attack**: The receiving service cannot verify that the forwarding service is authorized to act on behalf of the user
- **Privilege Escalation**: If the receiving service accepts tokens not intended for it, attackers can misuse tokens from one service at another
- **ALBEAST-Class Vulnerability**: Tokens intended for one tenant/service can be used at another
**What to Look For in PRs:**
```python
# ❌ CRITICAL SECURITY ISSUE
def call_external_api(user_token):
# Forwarding user's JWT directly to third-party service
response = requests.get(
"https://third-party-api.com/resource",
headers={"Authorization": f"Bearer {user_token}"}
)
```
**Severity:** CRITICAL - Must be blocked before merge
### 2. Validate Audience Claims
Every service that accepts JWTs **MUST** validate the `aud` claim matches its own identifier.
**RFC Requirements:**
- Per RFC 7519: "Each principal intended to process the JWT MUST identify itself with a value in the audience claim"
- Per RFC 9068: "The resource server MUST validate that the aud claim contains a resource indicator value corresponding to an identifier the resource server expects for itself"
- **The JWT MUST be rejected if the audience does not match**
**What to Look For in PRs:**
```python
# ❌ MISSING VALIDATION
def verify_token(token):
decoded = jwt.decode(token, public_key, algorithms=['RS256'])
# Missing audience validation!
return decoded
# ✅ CORRECT VALIDATION
def verify_token(token):
decoded = jwt.decode(
token,
public_key,
algorithms=['RS256'],
audience='https://api.myservice.com' # Validates aud claim
)
return decoded
```
**Severity:** CRITICAL if missing, HIGH if incomplete
### 3. Validate All Critical Claims
Beyond audience, validate:
**Required Validations:**
- `iss` (issuer): Verify the token came from a trusted authorization server
- `aud` (audience): Verify the token is intended for this service
- `exp` (expiration): Reject expired tokens
- `nbf` (not before): Reject tokens used before their valid time
- `alg` (algorithm): Prevent algorithm confusion attacks
**What to Look For in PRs:**
```python
# ❌ VULNERABLE TO ALGORITHM CONFUSION
def verify_token(token):
# Accepts ANY algorithm from the token header
decoded = jwt.decode(token, secret, algorithms=None)
# ✅ CORRECT - EXPLICIT ALGORITHM
def verify_token(token):
# Only accepts expected algorithm
decoded = jwt.decode(
token,
public_key,
algorithms=['RS256'], # Explicit, not from token
issuer='https://auth.example.com',
audience='https://api.example.com'
)
```
**Severity:** CRITICAL for algorithm validation, HIGH for other claims
### 4. Use Token Exchange for Service-to-Service Communication
When a service needs to call another service on behalf of a user, **use OAuth Token Exchange (RFC 8693)** rather than forwarding the original token.
**The Correct Pattern:**
```python
# ✅ SECURE TOKEN EXCHANGE PATTERN
def call_downstream_service(user_token):
# Exchange user token for service-specific token
exchange_response = requests.post(
'https://auth.example.com/token',
data={
'grant_type': 'urn:ietf:params:oauth:grant-type:token-exchange',
'subject_token': user_token,
'subject_token_type': 'urn:ietf:params:oauth:token-type:access_token',
'resource': 'https://downstream-service.example.com',
'scope': 'read:data' # Downscoped to minimum needed
}
)
downstream_token = exchange_response.json()['access_token']
# Use the service-specific token
response = requests.get(
'https://downstream-service.example.com/api/resource',
headers={'Authorization': f'Bearer {downstream_token}'}
)
```
**Benefits of Token Exchange:**
1. **Correct Audience**: New token has proper `aud` claim for downstream service
2. **Least Privilege**: Token is downscoped to minimum permissions needed
3. **Audit Trail**: Clear chain of delegation (user → service A → service B)
4. **Prevents Confused Deputy**: Downstream service can validate the token was properly issued
**What to Look For in PRs:**
- Services forwarding user JWTs to other services without exchange
- Missing token exchange implementation where multi-service calls exist
- Tokens with overly broad scopes being passed between services
**Severity:** CRITICAL in security-sensitive contexts, HIGH otherwise
### 5. Apply Principle of Least Privilege
**Scope Downscoping:**
When exchanging tokens, request only the minimum scopes needed for the specific operation.
```python
# ❌ OVER-PRIVILEGED
exchange_data = {
'scope': 'read write admin delete' # Too many permissions!
}
# ✅ LEAST PRIVILEGE
exchange_data = {
'scope': 'read:invoices' # Only what's needed
}
```
**Severity:** MEDIUM to HIGH depending on the over-privileging
### 6. Secure Token Storage and Transmission
**Transport Security:**
- JWTs MUST be transmitted over HTTPS only
- Never log full JWTs (mask them if needed for debugging)
- Never include JWTs in URLs (query parameters, path segments)
**Storage Security:**
- Don't store JWTs in localStorage if they contain sensitive data (XSS risk)
- Consider httpOnly cookies for web applications
- Use secure, encrypted storage on mobile platforms
- Implement token refresh to minimize long-lived token exposure
**What to Look For in PRs:**
```javascript
// ❌ SECURITY ISSUES
localStorage.setItem('token', jwt); // XSS vulnerability
console.log('Token:', jwt); // Logging sensitive data
const url = `/api/resource?token=${jwt}`; // Token in URL
// ✅ BETTER PRACTICES
// Use httpOnly cookie or secure storage
document.cookie = `token=${jwt}; Secure; HttpOnly; SameSite=Strict`;
console.log('Token:', jwt.substring(0, 10) + '...'); // Masked logging
```
**Severity:** HIGH for URL exposure, MEDIUM for storage issues
## Algorithm Confusion Attacks
### The Vulnerability
JWT headers include an `alg` parameter that specifies the signing algorithm. If the verification code doesn't enforce the expected algorithm, attackers can:
1. Change RS256 (RSA) to HS256 (HMAC)
2. Use the RSA public key as the HMAC secret
3. Create validly-signed tokens
**What to Look For in PRs:**
```python
# ❌ VULNERABLE
def verify_token(token):
# Algorithm taken from token header - DANGEROUS!
header = jwt.get_unverified_header(token)
alg = header['alg']
decoded = jwt.decode(token, key, algorithms=[alg])
# ✅ SECURE
def verify_token(token):
# Algorithm explicitly specified and validated
decoded = jwt.decode(
token,
public_key,
algorithms=['RS256'] # Only RS256 accepted
)
```
**Severity:** CRITICAL
### None Algorithm Attack
Some JWT libraries accept `"alg": "none"` which bypasses signature verification entirely.
**What to Look For in PRs:**
```python
# ❌ VULNERABLE
jwt.decode(token, verify=False) # Dangerous!
jwt.decode(token, algorithms=['RS256', 'none']) # Allows none!
# ✅ SECURE
jwt.decode(token, public_key, algorithms=['RS256'])
```
**Severity:** CRITICAL
## Common JWT Anti-Patterns
### 1. Treating ID Tokens as Access Tokens
**Problem:** ID tokens (from OpenID Connect) are meant for the client that requested authentication, NOT for API authorization.
```python
# ❌ WRONG
# Forwarding ID token to API
api_call(headers={'Authorization': f'Bearer {id_token}'})
# ✅ CORRECT
# Use access token for API calls
api_call(headers={'Authorization': f'Bearer {access_token}'})
```
**Severity:** HIGH
### 2. Missing Signature Validation
**Problem:** Accepting unsigned JWTs or skipping validation.
```python
# ❌ VULNERABLE
payload = jwt.decode(token, options={"verify_signature": False})
# ✅ SECURE
payload = jwt.decode(token, public_key, algorithms=['RS256'])
```
**Severity:** CRITICAL
### 3. Trusting Token Content Without Validation
**Problem:** Extracting claims before validating signature and claims.
```python
# ❌ DANGEROUS
def get_user_id(token):
# Decodes without validation!
payload = jwt.decode(token, options={"verify_signature": False})
return payload['user_id']
# ✅ SAFE
def get_user_id(token):
# Validates first, then extracts
payload = jwt.decode(
token,
public_key,
algorithms=['RS256'],
audience='https://api.example.com',
issuer='https://auth.example.com'
)
return payload['user_id']
```
**Severity:** CRITICAL
### 4. Using Weak Secrets for HS256
**Problem:** Using predictable or weak secrets for HMAC signing.
```python
# ❌ WEAK SECRET
secret = "secret123"
jwt.encode(payload, secret, algorithm='HS256')
# ✅ STRONG SECRET
# Use cryptographically random, high-entropy secret
# At least 256 bits (32 bytes) for HS256
import secrets
secret = secrets.token_bytes(32)
```
**Severity:** CRITICAL
### 5. Overly Broad Audiences
**Problem:** Using wildcard or overly generic audience values.
```python
# ❌ TOO BROAD
token_data = {
'aud': '*', # Accepts anywhere!
'aud': 'https://example.com' # Too generic
}
# ✅ SPECIFIC
token_data = {
'aud': 'https://api.example.com/v1/orders' # Specific service
}
```
**Severity:** MEDIUM to HIGH
## Review Checklist for JWT Code
When reviewing PRs involving JWT authentication/authorization:
### Critical Checks
- [ ] **Audience validation**: Every service validates `aud` claim matches its identifier
- [ ] **Algorithm enforcement**: Explicit algorithm list, no `none`, no user-controlled algorithm
- [ ] **Signature validation**: All tokens are cryptographically validated
- [ ] **Issuer validation**: `iss` claim validated against trusted issuers
- [ ] **No token forwarding**: Tokens are not forwarded to services not in their `aud` claim
### High Priority Checks
- [ ] **Token exchange**: Service-to-service calls use token exchange, not forwarding
- [ ] **Expiration validation**: `exp` claim checked and enforced
- [ ] **Scope downscoping**: Exchanged tokens request minimum necessary scopes
- [ ] **ID token misuse**: ID tokens not used for API authorization
- [ ] **Transport security**: Tokens only transmitted over HTTPS
### Medium Priority Checks
- [ ] **Least privilege**: Scopes are specific and minimal
- [ ] **Secure storage**: Tokens stored securely (not localStorage for sensitive data)
- [ ] **Token refresh**: Short-lived tokens with refresh mechanism
- [ ] **Logging safety**: Tokens not logged in full
- [ ] **Error messages**: Don't leak token information in errors
### Code Pattern Recognition
**Token Forwarding Pattern (Usually Wrong):**
```python
# Server receives user_token from client
# Server forwards user_token to another service
downstream_service.call(authorization=user_token) # ❌ Check this!
```
**Token Exchange Pattern (Usually Correct):**
```python
# Server receives user_token from client
# Server exchanges for service-specific token
new_token = auth_server.exchange_token(user_token, target_service)
# Server uses new token
downstream_service.call(authorization=new_token) # ✅ Good!
```
## Key RFCs and Standards
- **RFC 7519**: JSON Web Token (JWT) - Core standard
- **RFC 8693**: OAuth 2.0 Token Exchange - Service-to-service delegation
- **RFC 8707**: Resource Indicators for OAuth 2.0 - Explicit audience specification
- **RFC 9068**: JWT Profile for OAuth 2.0 Access Tokens - Access token best practices
- **RFC 9700**: OAuth 2.0 Security Best Current Practice (January 2025) - Latest security guidance
## When to Escalate
Escalate to security team or mark as CRITICAL if you find:
- Tokens forwarded to services not in their audience
- Missing signature validation
- Algorithm confusion vulnerabilities (accepting `none` or user-controlled algorithm)
- Tokens containing sensitive data logged or exposed in URLs
- No audience or issuer validation
- Token exchange not used in multi-service architectures

View File

@@ -0,0 +1,571 @@
# MCP (Model Context Protocol) Authorization Security
This reference provides comprehensive guidance for reviewing MCP server implementations with a focus on OAuth 2.1 authorization and security best practices.
## Overview
The Model Context Protocol (MCP) specification finalized OAuth 2.1-based authorization in June 2025. MCP servers represent high-value targets because they:
- Store authentication tokens for multiple services
- Execute actions across connected services
- Can be invoked by AI agents with varying levels of user oversight
- Potentially have access to sensitive data and operations
**Key Security Principle:** MCP servers must implement strict authorization controls to prevent unauthorized access and token misuse.
## MCP Authorization Requirements
### 1. OAuth 2.1 Compliance (MANDATORY)
The MCP specification requires OAuth 2.1 as the authorization foundation.
**Core Requirements:**
- MUST use OAuth 2.1 (not OAuth 2.0)
- MUST implement PKCE (Proof Key for Code Exchange) for all clients
- MUST implement Resource Indicators (RFC 8707)
- MUST validate tokens with correct audience claims
- MUST NOT use sessions for authentication
**What to Look For in PRs:**
```python
# ❌ NON-COMPLIANT - OAuth 2.0 without PKCE
@app.route('/authorize')
def authorize():
redirect_uri = request.args.get('redirect_uri')
# Missing code_challenge and code_challenge_method
auth_code = generate_auth_code()
return redirect(f'{redirect_uri}?code={auth_code}')
# ✅ COMPLIANT - OAuth 2.1 with PKCE
@app.route('/authorize')
def authorize():
code_challenge = request.args.get('code_challenge')
code_challenge_method = request.args.get('code_challenge_method')
if not code_challenge or code_challenge_method != 'S256':
return {'error': 'invalid_request', 'error_description': 'PKCE required'}, 400
auth_code = generate_auth_code(code_challenge)
return redirect(f'{redirect_uri}?code={auth_code}')
```
**Severity:** CRITICAL if PKCE missing, HIGH if OAuth 2.0 instead of 2.1
### 2. Resource Indicators (RFC 8707) - MANDATORY
MCP clients MUST implement Resource Indicators to explicitly specify the target MCP server.
**Purpose:**
- Ensures tokens are audience-restricted to specific MCP servers
- Prevents token misuse across different MCP servers
- Enables proper multi-tenant security
**What to Look For in PRs:**
```python
# ❌ MISSING RESOURCE INDICATOR
token_request = {
'grant_type': 'authorization_code',
'code': auth_code,
'redirect_uri': redirect_uri,
# Missing 'resource' parameter!
}
# ✅ CORRECT - WITH RESOURCE INDICATOR
token_request = {
'grant_type': 'authorization_code',
'code': auth_code,
'redirect_uri': redirect_uri,
'resource': 'https://mcp-server.example.com', # Explicit target
'code_verifier': code_verifier # PKCE
}
```
**Audience Validation:**
```python
# ✅ MCP SERVER MUST VALIDATE AUDIENCE
def validate_token(token):
try:
payload = jwt.decode(
token,
public_key,
algorithms=['RS256'],
audience='https://mcp-server.example.com', # Must match resource
issuer='https://auth.example.com'
)
return payload
except jwt.InvalidAudienceError:
raise HTTPException(403, 'Token not intended for this MCP server')
```
**Severity:** CRITICAL if missing
### 3. Strict Token Acceptance Policy
**MCP servers MUST NOT accept tokens that were not explicitly issued for them.**
This is the core defense against confused deputy attacks.
**What to Look For in PRs:**
```python
# ❌ VULNERABLE - ACCEPTS ANY TOKEN
def authorize_request(request):
token = request.headers.get('Authorization', '').replace('Bearer ', '')
# No audience validation!
payload = jwt.decode(token, public_key, algorithms=['RS256'])
return payload['user_id']
# ✅ SECURE - VALIDATES AUDIENCE
def authorize_request(request):
token = request.headers.get('Authorization', '').replace('Bearer ', '')
try:
payload = jwt.decode(
token,
public_key,
algorithms=['RS256'],
audience=MCP_SERVER_IDENTIFIER, # This server's identifier
issuer=TRUSTED_ISSUER
)
except jwt.InvalidAudienceError:
raise Forbidden('Token not issued for this MCP server')
return payload
```
**Severity:** CRITICAL
### 4. Scope Validation and Insufficient Scope Response
MCP servers MUST validate that tokens have the required scopes for operations.
**Required Behavior:**
- When a token has insufficient scope, respond with HTTP 403 Forbidden
- Include `WWW-Authenticate` header with `error="insufficient_scope"`
- Optionally include `scope` parameter indicating required scopes
**What to Look For in PRs:**
```python
# ❌ MISSING SCOPE VALIDATION
@app.route('/tools/invoke', methods=['POST'])
def invoke_tool(token_payload):
# No scope check!
result = execute_tool(request.json['tool_name'])
return result
# ✅ CORRECT SCOPE VALIDATION
@app.route('/tools/invoke', methods=['POST'])
def invoke_tool(token_payload):
required_scope = 'tools.invoke'
if required_scope not in token_payload.get('scope', '').split():
response = Response(
'Insufficient scope',
status=403,
headers={
'WWW-Authenticate': f'Bearer error="insufficient_scope", scope="{required_scope}"'
}
)
return response
result = execute_tool(request.json['tool_name'])
return result
```
**Severity:** HIGH
### 5. No Session-Based Authentication
**MCP servers MUST NOT use sessions for authentication.**
This is a specific requirement of the MCP specification.
**What to Look For in PRs:**
```python
# ❌ VIOLATES MCP SPEC - USING SESSIONS
@app.route('/tools/list')
def list_tools():
if 'user_id' not in session:
return {'error': 'unauthorized'}, 401
# Using Flask session - NOT ALLOWED
# ✅ COMPLIANT - TOKEN-BASED
@app.route('/tools/list')
def list_tools():
token = request.headers.get('Authorization', '').replace('Bearer ', '')
payload = validate_mcp_token(token) # OAuth 2.1 token validation
return get_tools_for_user(payload['sub'])
```
**Severity:** CRITICAL - violates MCP specification
### 6. Secure Session ID Generation (if used for OAuth flow)
If the MCP server or authorization server uses session IDs during the OAuth flow (not for authentication):
**Requirements:**
- MUST use secure, non-deterministic session IDs
- MUST use cryptographically secure random number generators
- MUST have sufficient entropy to prevent guessing
**What to Look For in PRs:**
```python
# ❌ WEAK SESSION ID
import random
session_id = str(random.randint(1000, 9999)) # Predictable!
# ✅ SECURE SESSION ID
import secrets
session_id = secrets.token_urlsafe(32) # Cryptographically secure
```
**Severity:** HIGH
## Token Forwarding to MCP Servers
### The Critical Security Issue
A common anti-pattern is forwarding user authentication tokens from an inference server (or other service) to an MCP server.
**Example of Vulnerable Flow:**
```
User → Inference Server (with user JWT)
Inference Server → MCP Server (forwarding user JWT) ❌ INSECURE
```
**Why This Is Dangerous:**
1. **Audience Mismatch**: User JWT has `aud` claim for inference server, not MCP server
2. **Confused Deputy**: MCP server cannot verify inference server is authorized to delegate
3. **No Scope Restriction**: MCP server receives user's full permissions, not downscoped
4. **Violates MCP Spec**: MCP servers must only accept tokens issued for them
**What to Look For in PRs:**
```python
# ❌ CRITICAL VULNERABILITY
class InferenceServer:
def call_mcp_tool(self, user_token, tool_name, params):
# Forwarding user token directly to MCP server!
response = requests.post(
f'{MCP_SERVER_URL}/tools/invoke',
headers={'Authorization': f'Bearer {user_token}'}, # WRONG!
json={'tool': tool_name, 'params': params}
)
return response.json()
```
**Severity:** CRITICAL - Must be blocked before merge
### The Correct Pattern: Token Exchange
**Secure Flow:**
```
User → Inference Server (with user JWT)
↓ (validate user JWT)
Inference Server → Auth Server (token exchange request)
↓ (receives MCP-scoped token)
Inference Server → MCP Server (with MCP-specific token) ✓ SECURE
```
**What to Look For in PRs:**
```python
# ✅ CORRECT - TOKEN EXCHANGE
class InferenceServer:
def call_mcp_tool(self, user_token, tool_name, params):
# Step 1: Validate user token
user_claims = self.validate_user_token(user_token)
# Step 2: Exchange for MCP-specific token
mcp_token = self.exchange_token_for_mcp(
user_token=user_token,
mcp_server='https://mcp-server.example.com',
required_scopes=['tools.invoke']
)
# Step 3: Use MCP-specific token
response = requests.post(
f'{MCP_SERVER_URL}/tools/invoke',
headers={'Authorization': f'Bearer {mcp_token}'}, # Correct audience
json={'tool': tool_name, 'params': params}
)
return response.json()
def exchange_token_for_mcp(self, user_token, mcp_server, required_scopes):
"""Exchange user token for MCP-server-specific token"""
exchange_response = requests.post(
f'{AUTH_SERVER_URL}/token',
data={
'grant_type': 'urn:ietf:params:oauth:grant-type:token-exchange',
'subject_token': user_token,
'subject_token_type': 'urn:ietf:params:oauth:token-type:access_token',
'resource': mcp_server, # RFC 8707 Resource Indicator
'scope': ' '.join(required_scopes) # Downscoped
}
)
if exchange_response.status_code != 200:
raise AuthorizationError('Token exchange failed')
return exchange_response.json()['access_token']
```
**Key Security Benefits:**
1. ✅ MCP token has correct `aud` claim for the MCP server
2. ✅ Token is downscoped to minimum needed permissions
3. ✅ Clear audit trail preserved (user → inference → MCP)
4. ✅ Complies with MCP OAuth 2.1 specification
5. ✅ Prevents confused deputy attacks
6. ✅ MCP server can properly validate the token
**Severity:** Implementation of token exchange is CRITICAL when missing
## MCP Server Architecture Patterns
### Pattern 1: Embedded Authorization Server
The MCP server includes its own auth system.
**Characteristics:**
- Handles login, user sessions, consent UI
- Issues and verifies tokens
- Acts as both authorization server and resource server
**Security Considerations:**
- Must implement full OAuth 2.1 spec
- Requires secure user credential management
- Needs proper consent UI for user authorization
- Session management during OAuth flow (not for MCP operations)
### Pattern 2: External Authorization Server (Recommended)
The MCP server delegates OAuth to a trusted external provider.
**Characteristics:**
- Authorization server handles authentication, user management, consent
- MCP server only validates tokens and enforces scopes
- Clear separation of concerns
**Security Benefits:**
- Leverages battle-tested auth infrastructure
- Reduces attack surface of MCP server
- Easier to audit and maintain
- Better compliance with OAuth 2.1 spec
**What to Look For in PRs:**
```python
# ✅ EXTERNAL AUTH SERVER PATTERN
class MCPServer:
def __init__(self, auth_server_url, mcp_server_id):
self.auth_server_url = auth_server_url
self.mcp_server_id = mcp_server_id
# Fetch public keys from auth server
self.public_keys = self.fetch_jwks()
def validate_token(self, token):
"""Validate token issued by external auth server"""
try:
payload = jwt.decode(
token,
self.public_keys,
algorithms=['RS256'],
audience=self.mcp_server_id,
issuer=self.auth_server_url
)
return payload
except jwt.InvalidTokenError as e:
raise Unauthorized(str(e))
def fetch_jwks(self):
"""Fetch JSON Web Key Set from auth server"""
response = requests.get(f'{self.auth_server_url}/.well-known/jwks.json')
return response.json()
```
## Transport Security
### Mutual TLS (mTLS)
For production MCP deployments, especially in enterprise environments:
**Requirement:** Transport security SHOULD use mutual TLS (mTLS) for bidirectional verification.
**What to Look For in PRs:**
```python
# ✅ mTLS CONFIGURATION
import ssl
context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
context.verify_mode = ssl.CERT_REQUIRED
context.load_cert_chain(certfile='server.crt', keyfile='server.key')
context.load_verify_locations(cafile='client_ca.crt')
# Use context in server configuration
server = HTTPServer(('localhost', 8443), MCPRequestHandler)
server.socket = context.wrap_socket(server.socket, server_side=True)
```
**Severity:** MEDIUM for internal deployments, HIGH for external-facing servers
### HTTPS Mandatory
**All MCP communication MUST occur over HTTPS (or equivalent secure transport).**
**What to Look For in PRs:**
- HTTP URLs in production configurations (red flag)
- Missing TLS/SSL certificate validation
- Disabled certificate verification (`verify=False`)
**Severity:** CRITICAL if HTTP used in production
## Security Risks Specific to MCP
### 1. High-Value Target
MCP servers store tokens for multiple services. If compromised:
- Attacker gains access to all connected service tokens
- Can execute actions across all integrated services
- Potential for lateral movement across services
**Mitigation:**
- Encrypt tokens at rest
- Use short-lived tokens with refresh
- Implement rate limiting and anomaly detection
- Audit all token usage
### 2. Prompt Injection Attacks
AI agents can be manipulated through prompt injection to:
- Execute unintended MCP tool invocations
- Access unauthorized resources
- Exfiltrate data through tool responses
**What to Look For in PRs:**
- Input validation on tool parameters
- Scope restrictions preventing dangerous operations
- Audit logging of all tool invocations
- User confirmation for sensitive operations
**Severity:** HIGH to CRITICAL depending on MCP server capabilities
### 3. Cross-Prompt Injection
Malicious content embedded in documents or UI can override agent instructions.
**Mitigation:**
- Sanitize external content before processing
- Implement content security policies
- Separate user instructions from external content
- Require explicit user confirmation for sensitive actions
**Severity:** HIGH
### 4. Remote Code Execution Risk
If MCP tools can execute code or system commands, prompt injection could lead to RCE.
**What to Look For in PRs:**
- Code execution capabilities without sandboxing
- System command execution from tool parameters
- File system access without path validation
- Network access without destination whitelisting
**Severity:** CRITICAL
## Review Checklist for MCP Code
When reviewing PRs involving MCP server implementation:
### Critical Checks
- [ ] **OAuth 2.1 compliance**: Using OAuth 2.1 (not 2.0)
- [ ] **PKCE mandatory**: All authorization flows use PKCE with S256
- [ ] **Resource Indicators**: Token requests include explicit `resource` parameter
- [ ] **Audience validation**: Server validates `aud` claim matches its identifier
- [ ] **No token forwarding**: Server doesn't accept tokens issued for other services
- [ ] **Token exchange**: Uses token exchange for upstream service calls
- [ ] **No session auth**: Doesn't use sessions for MCP operation authentication
### High Priority Checks
- [ ] **Scope validation**: Validates required scopes and returns proper insufficient_scope errors
- [ ] **Issuer validation**: Validates `iss` claim against trusted issuers
- [ ] **Algorithm enforcement**: Explicit algorithm list (no `none`, no user-controlled)
- [ ] **Signature validation**: All tokens cryptographically validated
- [ ] **HTTPS/TLS**: All communication over secure transport
- [ ] **Input validation**: Tool parameters validated and sanitized
### Medium Priority Checks
- [ ] **Token storage**: Tokens encrypted at rest
- [ ] **Audit logging**: Tool invocations logged with user context
- [ ] **Rate limiting**: Prevents abuse of MCP endpoints
- [ ] **Least privilege**: Scopes are minimal and specific
- [ ] **mTLS consideration**: mTLS for production deployments
- [ ] **Secure session IDs**: If used in OAuth flow, cryptographically random
### MCP-Specific Security Patterns
**Inference Server + MCP Architecture:**
```
✅ SECURE PATTERN:
1. User authenticates to inference server
2. Inference server validates user token
3. Inference server exchanges token for MCP-specific token
4. Inference server invokes MCP with proper token
5. MCP server validates audience matches its identifier
❌ INSECURE PATTERN:
1. User authenticates to inference server
2. Inference server forwards user token to MCP
3. MCP server accepts token not issued for it
```
## Platform-Specific Considerations
### Windows 11 MCP Proxy
Windows 11 provides centralized MCP proxy for security:
**Features:**
- Proxy-mediated communication for all MCP interactions
- Centralized policy enforcement
- Consistent authentication and authorization
- Enhanced audit and monitoring
**What to Look For in PRs:**
- Windows platform should integrate with Windows 11 MCP proxy
- Proxy configuration and policy settings
- Proper certificate validation for proxy communication
**Severity:** MEDIUM for Windows deployments
## Key Standards and Specifications
- **MCP Authorization Spec** (June 2025): Official OAuth 2.1 authorization for MCP
- **OAuth 2.1**: Required authorization framework
- **RFC 7636**: PKCE - Mandatory for all MCP clients
- **RFC 8707**: Resource Indicators - Mandatory for MCP token requests
- **RFC 8693**: Token Exchange - Recommended for service-to-service delegation
- **RFC 9068**: JWT Profile for OAuth 2.0 Access Tokens
- **RFC 9700**: OAuth 2.0 Security Best Current Practice (January 2025)
## When to Escalate
Escalate to security team or mark as CRITICAL if you find:
1. **Token forwarding**: Inference/API servers forwarding user tokens to MCP servers
2. **Missing audience validation**: MCP server accepts tokens without validating `aud` claim
3. **No PKCE**: Authorization flow missing PKCE implementation
4. **No Resource Indicators**: Token requests missing `resource` parameter
5. **Session-based auth**: Using sessions for MCP operation authentication
6. **Prompt injection risks**: MCP tools with code execution and no sandboxing
7. **Missing OAuth 2.1**: Still using OAuth 2.0 instead of 2.1
8. **HTTP communication**: Production MCP server using HTTP instead of HTTPS
## Summary
MCP authorization security requires:
1. **OAuth 2.1** with PKCE (mandatory)
2. **Resource Indicators** (RFC 8707) for explicit audience targeting
3. **Strict token validation** (audience, issuer, signature, scopes)
4. **Token exchange** for service-to-service delegation (not forwarding)
5. **No session-based authentication** for MCP operations
6. **Secure transport** (HTTPS/TLS, preferably mTLS)
7. **Input validation** and prompt injection protection
The most common vulnerability is token forwarding from inference servers to MCP servers. This must be replaced with proper token exchange flows.