Files
home_hub/backend/app/core/mcp_auth.py
T
gilles 24598c836b 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>
2026-05-25 22:49:45 +02:00

30 lines
1.2 KiB
Python

import hmac
import json
from starlette.types import ASGIApp, Receive, Scope, Send
from app.core.config import settings
class MCPAuthMiddleware:
def __init__(self, app: ASGIApp) -> None:
self.app = app
async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None:
if scope["type"] == "http" and scope.get("path", "").startswith("/mcp"):
headers = dict(scope.get("headers", []))
auth = headers.get(b"authorization", b"").decode()
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()
await send({
"type": "http.response.start",
"status": 401,
"headers": [
(b"content-type", b"application/json"),
(b"content-length", str(len(body)).encode()),
(b"www-authenticate", b"Bearer"),
],
})
await send({"type": "http.response.body", "body": body})
return
await self.app(scope, receive, send)