From 03a5a43f308f5cbd2ae79e77e9a6359d8ccee10e Mon Sep 17 00:00:00 2001 From: dekun Date: Tue, 12 May 2026 16:02:50 +0800 Subject: [PATCH] fix: session cookie options for HTTP (insecure override + auto secure) Co-authored-by: Cursor --- .env.example | 8 ++++++-- app.py | 38 +++++++++++++++++++++++++++++++++++--- 2 files changed, 41 insertions(+), 5 deletions(-) diff --git a/.env.example b/.env.example index 3851f29..6578bfc 100644 --- a/.env.example +++ b/.env.example @@ -26,7 +26,11 @@ # 在 Nginx/Caddy 等反向代理后部署时:信任 X-Forwarded-*,以便 Cookie、CSRF 与 HTTPS 判断正确 # NAV_TRUST_PROXY=1 -# 站点仅通过 https:// 对外访问时建议开启;若用 http://内网IP:端口 访问请勿开启,否则浏览器不保存登录 Cookie -# NAV_SESSION_COOKIE_SECURE=1 +# 会话 Cookie 的 Secure 标志(与浏览器地址栏是否 https 一致才有效): +# - 内网用 http://IP:端口:不要写 NAV_SESSION_COOKIE_SECURE=1;或保留 1 时另加 NAV_COOKIES_INSECURE_HTTP=1 强制关闭 Secure +# - 纯 https 对外:NAV_SESSION_COOKIE_SECURE=1 +# - 反代后同时有 http/https:可试 NAV_SESSION_COOKIE_SECURE=auto(并配合 NAV_TRUST_PROXY=1) +# NAV_COOKIES_INSECURE_HTTP=1 +# NAV_SESSION_COOKIE_SECURE=auto # CSRF 校验仍失败时,可填前端访问的完整 Origin,多个用英文逗号分隔,例如: # NAV_CSRF_TRUSTED_ORIGINS=https://nav.example.com diff --git a/app.py b/app.py index 46e6778..8c75fb6 100644 --- a/app.py +++ b/app.py @@ -55,6 +55,40 @@ login_manager = LoginManager() csrf = CSRFProtect() +def _apply_session_cookie_settings(app: Flask) -> None: + """Secure Cookie 仅在浏览器用 https:// 访问时才有意义;用 http://IP:端口 时若仍设 Secure,浏览器会丢弃会话,表现为登录后立即回到登录页。""" + if os.environ.get("NAV_COOKIES_INSECURE_HTTP") == "1": + app.config["SESSION_COOKIE_SECURE"] = False + app.config["REMEMBER_COOKIE_SECURE"] = False + if (os.environ.get("NAV_SESSION_COOKIE_SECURE") or "").strip().lower() in ( + "1", + "true", + "yes", + ): + print( + "[nav] NAV_COOKIES_INSECURE_HTTP=1:已关闭 Secure Cookie,便于 http:// 内网访问。", + flush=True, + ) + return + + mode = (os.environ.get("NAV_SESSION_COOKIE_SECURE") or "0").strip().lower() + if mode in ("1", "true", "yes"): + app.config["SESSION_COOKIE_SECURE"] = True + app.config["REMEMBER_COOKIE_SECURE"] = True + elif mode == "auto": + app.config["SESSION_COOKIE_SECURE"] = False + app.config["REMEMBER_COOKIE_SECURE"] = False + + @app.before_request + def _nav_session_cookie_secure_auto(): + sec = request.is_secure + app.config["SESSION_COOKIE_SECURE"] = sec + app.config["REMEMBER_COOKIE_SECURE"] = sec + else: + app.config["SESSION_COOKIE_SECURE"] = False + app.config["REMEMBER_COOKIE_SECURE"] = False + + def create_app() -> Flask: app = Flask(__name__) app.config["SECRET_KEY"] = os.environ.get("NAV_SECRET_KEY") or secrets.token_hex(32) @@ -64,9 +98,7 @@ def create_app() -> Flask: app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False app.config["WTF_CSRF_TIME_LIMIT"] = None - if os.environ.get("NAV_SESSION_COOKIE_SECURE") == "1": - app.config["SESSION_COOKIE_SECURE"] = True - app.config["REMEMBER_COOKIE_SECURE"] = True + _apply_session_cookie_settings(app) trusted = os.environ.get("NAV_CSRF_TRUSTED_ORIGINS", "").strip() if trusted: