From e7dd715d8ad21cf90e1c0538b517b21bea47313f Mon Sep 17 00:00:00 2001 From: wahh3b-lgtm Date: Mon, 15 Jun 2026 23:18:40 +0330 Subject: [PATCH] security: timing-safe HMAC and CORS wildcard fix - Use hmac.compare_digest with bitwise OR for constant-time JWT signature verification - Reject wildcard CORS origins when allow_credentials=True - Fix Python 2 exception syntax (except TypeError, ValueError -> except (TypeError, ValueError)) --- app/middlewares/__init__.py | 18 ++++++++++++++++-- app/utils/jwt.py | 5 +++-- 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/app/middlewares/__init__.py b/app/middlewares/__init__.py index 0d581a4d2..905ad6373 100644 --- a/app/middlewares/__init__.py +++ b/app/middlewares/__init__.py @@ -9,10 +9,24 @@ def setup_middleware(app: FastAPI): + # Security: reject wildcard origin with credentials enabled + allowed_origins = cors_settings.allowed_origins + if "*" in allowed_origins: + import warnings + + warnings.warn( + "CORS allow_origins contains '*' with allow_credentials=True is insecure. " + "Set ALLOWED_ORIGINS to explicit origins in production.", + stacklevel=2, + ) + allow_credentials = False + else: + allow_credentials = True + app.add_middleware( CORSMiddleware, - allow_origins=cors_settings.allowed_origins, - allow_credentials=True, + allow_origins=allowed_origins, + allow_credentials=allow_credentials, allow_methods=["*"], allow_headers=["*"], ) diff --git a/app/utils/jwt.py b/app/utils/jwt.py index 2efa14451..924dd9779 100644 --- a/app/utils/jwt.py +++ b/app/utils/jwt.py @@ -1,3 +1,4 @@ +import hmac import time import jwt from base64 import b64decode, b64encode @@ -38,7 +39,7 @@ async def get_admin_payload(token: str) -> dict | None: if admin_id is not None: try: admin_id = int(admin_id) - except TypeError, ValueError: + except (TypeError, ValueError): return if not username or access not in ("admin", "sudo"): return @@ -97,7 +98,7 @@ async def get_subscription_payload(token: str) -> dict | None: sha256((u_token + await get_secret_key()).encode("utf-8")).digest(), altchars=b"-_" ).decode("utf-8")[:10] u_token_hex_resign = sha256((u_token + await get_secret_key()).encode("utf-8")).hexdigest()[:10] - if u_signature in (u_token_resign, u_token_hex_resign): + if hmac.compare_digest(u_signature, u_token_resign) | hmac.compare_digest(u_signature, u_token_hex_resign): parts = u_token_dec_str.split(",") if len(parts) == 3 and parts[0] in ("v2", "v3"): _, u_user_id_str, u_created_at_str = parts