From ea0ba33212936b03f92485871606eea9db79f962 Mon Sep 17 00:00:00 2001 From: root Date: Sat, 20 Jun 2026 18:00:50 -0500 Subject: [PATCH] security: set SESSION_COOKIE_SECURE + validate IP before nginx deny write Two hardening fixes from a fresh security pass of the live code: - SESSION_COOKIE_SECURE=True: the session cookie set only HTTPONLY + SAMESITE=Lax, so it could be sent over plaintext HTTP. App is served behind nginx TLS, so no functional downside. - _nginx_ban: validate the IP with ipaddress.ip_address() before interpolating it into the nginx deny file. A malformed value could otherwise inject nginx directives at the last write before reload. Guard sits at the sink, covering all current and future callers. Co-Authored-By: Claude Opus 4.8 (1M context) --- llm_team_ui.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/llm_team_ui.py b/llm_team_ui.py index 8d46e2a..74af0c0 100644 --- a/llm_team_ui.py +++ b/llm_team_ui.py @@ -35,6 +35,7 @@ if not _flask_secret: app.secret_key = _flask_secret app.config["SESSION_COOKIE_HTTPONLY"] = True app.config["SESSION_COOKIE_SAMESITE"] = "Lax" +app.config["SESSION_COOKIE_SECURE"] = True # served behind nginx TLS; prevents cookie leak over plaintext HTTP # ─── SECURITY LOGGING ───────────────────────────────────────── # Dedicated security log for fail2ban and audit trail. @@ -7687,6 +7688,16 @@ def _nginx_ban(ip): if is_allowlisted(ip): sec_log.info("NGINX_BAN_BLOCKED ip=%s — allowlisted, refused to write deny rule", ip) return + # Defense in depth: validate IP format before interpolating into the + # nginx conf. A malformed value (e.g. "1.2.3.4;\n}\nserver{...") would + # otherwise inject directives into the deny file. Last write before + # reload — last place to stop a bad ban (matches docstring intent). + import ipaddress + try: + ipaddress.ip_address(ip) + except ValueError: + sec_log.warning("NGINX_BAN_INVALID_IP ip=%r — refused to write deny rule (possible nginx config injection)", ip) + return import subprocess line = f"deny {ip};\n" # Each step has its own try/except so we know WHICH step failed.