fix(mcp): comparaison constante hmac + rejet si clé vide + WWW-Authenticate
Fixes deux vulnérabilités critiques en sécurité: 1. **Timing attack** — remplace la comparaison naïve `!=` par `hmac.compare_digest()` pour éviter les attaques temporelles (constant-time comparison). 2. **Clé vide acceptée** — ajoute le check `not settings.mcp_api_key` pour rejeter (401) TOUS les requêtes `/mcp` si MCP_API_KEY n'est pas configurée, empêchant l'accès unauthenticated silencieux. Bonus: ajoute l'en-tête `www-authenticate: Bearer` (RFC 9110). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,3 +1,4 @@
|
|||||||
|
import hmac
|
||||||
import json
|
import json
|
||||||
from starlette.types import ASGIApp, Receive, Scope, Send
|
from starlette.types import ASGIApp, Receive, Scope, Send
|
||||||
from app.core.config import settings
|
from app.core.config import settings
|
||||||
@@ -11,7 +12,8 @@ class MCPAuthMiddleware:
|
|||||||
if scope["type"] == "http" and scope.get("path", "").startswith("/mcp"):
|
if scope["type"] == "http" and scope.get("path", "").startswith("/mcp"):
|
||||||
headers = dict(scope.get("headers", []))
|
headers = dict(scope.get("headers", []))
|
||||||
auth = headers.get(b"authorization", b"").decode()
|
auth = headers.get(b"authorization", b"").decode()
|
||||||
if auth != f"Bearer {settings.mcp_api_key}":
|
expected = f"Bearer {settings.mcp_api_key}"
|
||||||
|
if not settings.mcp_api_key or not hmac.compare_digest(auth, expected):
|
||||||
body = json.dumps({"detail": "Unauthorized"}).encode()
|
body = json.dumps({"detail": "Unauthorized"}).encode()
|
||||||
await send({
|
await send({
|
||||||
"type": "http.response.start",
|
"type": "http.response.start",
|
||||||
@@ -19,6 +21,7 @@ class MCPAuthMiddleware:
|
|||||||
"headers": [
|
"headers": [
|
||||||
(b"content-type", b"application/json"),
|
(b"content-type", b"application/json"),
|
||||||
(b"content-length", str(len(body)).encode()),
|
(b"content-length", str(len(body)).encode()),
|
||||||
|
(b"www-authenticate", b"Bearer"),
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
await send({"type": "http.response.body", "body": body})
|
await send({"type": "http.response.body", "body": body})
|
||||||
|
|||||||
Reference in New Issue
Block a user