fix(security): honor X-Forwarded-Proto when setting resource_token cookie secure flag (#6038)

This commit is contained in:
LeChristopher Blackwell
2026-07-01 07:00:22 -07:00
committed by GitHub
parent c57985d553
commit 6fef533527
2 changed files with 46 additions and 1 deletions

View File

@@ -188,12 +188,19 @@ def set_or_refresh_resource_token_cookie(
purpose="resource"
)
# 判断请求是否为 HTTPS直连协议为 https或经反向代理转发时携带 X-Forwarded-Proto: https。
# 无法确认为明文 HTTP 时按 fail-safe 默认设置 secure=True避免代理终止 HTTPS 后以 HTTP 转发导致 Cookie 明文传输。
is_https = (
request.url.scheme == "https"
or request.headers.get("x-forwarded-proto", "").lower() == "https"
)
# 设置会话级别的 HttpOnly Cookie
response.set_cookie(
key=settings.PROJECT_NAME,
value=resource_token,
httponly=True,
secure=request.url.scheme == "https", # 根据当前请求协议设置 secure 属性
secure=is_https, # 根据当前请求协议(含反向代理转发标识)设置 secure 属性
samesite="lax" # 不同浏览器对 "Strict" 的处理可能不同,设置 SameSite 为 "Lax",以平衡安全性和兼容性
)

View File

@@ -0,0 +1,38 @@
from unittest import TestCase
from fastapi import Response
from app import schemas
from app.core.security import set_or_refresh_resource_token_cookie
class FakeURL:
def __init__(self, scheme: str) -> None:
self.scheme = scheme
class FakeRequest:
"""
最小化的请求桩对象,仅提供 set_or_refresh_resource_token_cookie 所需属性。
"""
def __init__(self, scheme: str, headers: dict | None = None) -> None:
self.url = FakeURL(scheme)
self.headers = headers or {}
self.cookies: dict = {}
class ResourceTokenCookieSecureFlagTest(TestCase):
def test_secure_flag_set_when_https_terminated_at_reverse_proxy(self):
"""
当反向代理(如 nginx终止 HTTPS 并以 HTTP 转发给后端时,
资源令牌 Cookie 仍必须携带 secure 属性,不能因为直连请求协议是 http 就降级。
"""
request = FakeRequest(scheme="http", headers={"x-forwarded-proto": "https"})
response = Response()
payload = schemas.TokenPayload(sub=1, username="test", super_user=False, level=1)
set_or_refresh_resource_token_cookie(request, response, payload)
set_cookie_header = response.headers.get("set-cookie", "")
self.assertIn("Secure", set_cookie_header)