From 5469a967a2d7a09c0c831f57305a1916285884d0 Mon Sep 17 00:00:00 2001 From: grendervill Date: Fri, 19 Jun 2026 15:37:02 +0300 Subject: [PATCH] =?UTF-8?q?version0.4.1=20=D0=9E=D0=B1=D1=89=D0=B8=D0=B5?= =?UTF-8?q?=20=D0=B8=D1=81=D0=BF=D1=80=D0=B0=D0=B2=D0=BB=D0=B5=D0=BD=D0=B8?= =?UTF-8?q?=D1=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app.py | 27 ++++++++++++++++++++++++++- templates/base.html | 1 + 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/app.py b/app.py index d451676..54b3d13 100644 --- a/app.py +++ b/app.py @@ -23,7 +23,11 @@ SSL_KEY = os.environ.get("SSL_KEY", "") app = Flask(__name__) app.secret_key = os.environ.get("FLASK_SECRET", secrets.token_hex(32)) app.config["SESSION_COOKIE_SAMESITE"] = "Lax" -app.config["SESSION_COOKIE_SECURE"] = bool(SSL_CERT) +# SESSION_COOKIE_SECURE не задаём статически — это ломает логин, если +# SSL_CERT прописан в env, но gunicorn по факту поднялся на HTTP +# (например, сертификат ещё не физически на диске, или используется +# SSH-туннель без TLS). Вместо этого выставляем Secure динамически +# по факту запроса — см. _set_cookie_secure_dynamically ниже. app.config["PERMANENT_SESSION_LIFETIME"] = timedelta(hours=8) # ── Rate limiter ───────────────────────────────────────────────────────────── @@ -595,6 +599,27 @@ def check_secret_path(): if not (path == allowed_prefix or path.startswith(allowed_prefix + "/")): abort(404) +# ── Динамическая правка Secure-флага на cookie сессии ──────────────────────── +# Если выставить Secure статически по наличию SSL_CERT в env, можно словить +# ситуацию когда сертификат прописан, но запрос реально пришёл по HTTP +# (gunicorn не поднял TLS, SSH-туннель без TLS и т.п.) — тогда браузер +# тихо отбросит cookie с флагом Secure, и человек не сможет войти, видя +# при этом "успешный" логин без какой-либо ошибки. Поэтому проверяем +# request.is_secure на каждый ответ и правим флаг по факту. +@app.after_request +def fix_session_cookie_secure(response): + if not request.is_secure: + return response + set_cookie_headers = response.headers.getlist("Set-Cookie") + if not set_cookie_headers: + return response + response.headers.remove("Set-Cookie") + for header in set_cookie_headers: + if app.session_cookie_name in header and "Secure" not in header: + header += "; Secure" + response.headers.add("Set-Cookie", header) + return response + if __name__ == "__main__": port = int(os.environ.get("PANEL_PORT", "8080")) ssl_ctx = None diff --git a/templates/base.html b/templates/base.html index 0e5b11f..10cad98 100644 --- a/templates/base.html +++ b/templates/base.html @@ -150,6 +150,7 @@ function showPage(id,btn){ if(id==='dashboard')loadDashboard(); if(id==='users')loadUsers(); if(id==='ssl')loadSSL(); + if(id==='security')loadSecurityPage(); } function openModal(title,html){ document.getElementById('modal-title-text').textContent=title;