Phase 3: WebSocket CSRF, SameSite=Lax, Tenant FK CASCADE
This commit is contained in:
+1
-1
@@ -39,7 +39,7 @@ class Settings(BaseSettings):
|
||||
bcrypt_rounds: int = 12
|
||||
session_cookie_name: str = "leocrm_session"
|
||||
session_cookie_secure: bool = True # Secure by default — set to False only for local HTTP development
|
||||
session_cookie_samesite: str = "strict" # Strict blocks WebSocket cookies; use Lax only if WS needed
|
||||
session_cookie_samesite: str = "lax" # Lax allows WebSocket cookies while preventing CSRF on top-level navigations
|
||||
session_cookie_httponly: bool = True
|
||||
password_reset_expiry_hours: int = 1
|
||||
|
||||
|
||||
+17
-2
@@ -116,8 +116,23 @@ def verify_ws_origin(websocket) -> bool:
|
||||
# CSRF token validation: check query parameter 'csrf_token' against session
|
||||
# The frontend must send ?csrf_token=xxx in the WebSocket URL
|
||||
# This prevents cross-site WebSocket hijacking attacks
|
||||
# Note: We skip CSRF for now if no session cookie — the WS handler will
|
||||
# authenticate the user after connection. Origin check is the primary defense.
|
||||
csrf_token = websocket.query_params.get("csrf_token", "")
|
||||
if not csrf_token:
|
||||
logger.warning("WebSocket connection rejected: missing csrf_token query parameter")
|
||||
return False
|
||||
|
||||
# Validate CSRF token against session in Redis
|
||||
session_id = websocket.cookies.get(settings.session_cookie_name)
|
||||
if not session_id:
|
||||
logger.warning("WebSocket connection rejected: missing session cookie")
|
||||
return False
|
||||
|
||||
redis = get_redis()
|
||||
session_data = await get_session_data(redis, session_id)
|
||||
if not session_data or session_data.get("csrf_token") != csrf_token:
|
||||
logger.warning("WebSocket connection rejected: invalid CSRF token")
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user