From fb17394099be0e0f74edb63f8c72e93c522937d7 Mon Sep 17 00:00:00 2001 From: Eduardo Silva Date: Mon, 16 Mar 2026 20:36:49 -0300 Subject: [PATCH] enhance security by adding cache control headers, validating password length, and rejecting encoded slashes in path processing --- .../auth_gateway/services/password_service.py | 5 +++++ .../auth-gateway/auth_gateway/web/auth_routes.py | 1 + .../auth-gateway/auth_gateway/web/login_routes.py | 9 ++++++--- containers/caddy/process_config.py | 14 ++++++++++++++ 4 files changed, 26 insertions(+), 3 deletions(-) diff --git a/containers/auth-gateway/auth_gateway/services/password_service.py b/containers/auth-gateway/auth_gateway/services/password_service.py index da824ca..3ba9dae 100644 --- a/containers/auth-gateway/auth_gateway/services/password_service.py +++ b/containers/auth-gateway/auth_gateway/services/password_service.py @@ -7,7 +7,12 @@ from auth_gateway.models.auth import UserModel password_hasher = PasswordHasher() +MAX_PASSWORD_LENGTH = 1024 + + def verify_user_password(username: str, password: str, users: dict[str, UserModel]) -> UserModel | None: + if not password or len(password) > MAX_PASSWORD_LENGTH: + return None user = users.get(username) if not user or not user.password_hash: return None diff --git a/containers/auth-gateway/auth_gateway/web/auth_routes.py b/containers/auth-gateway/auth_gateway/web/auth_routes.py index 5e45837..33f0c82 100644 --- a/containers/auth-gateway/auth_gateway/web/auth_routes.py +++ b/containers/auth-gateway/auth_gateway/web/auth_routes.py @@ -57,6 +57,7 @@ async def auth_check(request: Request): return re.sub(r"[\r\n\x00]", "", value) response = PlainTextResponse("OK", status_code=200) + response.headers["Cache-Control"] = "no-store" if session: if session.username: response.headers["X-Auth-User"] = _safe_header(session.username) diff --git a/containers/auth-gateway/auth_gateway/web/login_routes.py b/containers/auth-gateway/auth_gateway/web/login_routes.py index cef94b3..41d2e8b 100644 --- a/containers/auth-gateway/auth_gateway/web/login_routes.py +++ b/containers/auth-gateway/auth_gateway/web/login_routes.py @@ -317,6 +317,7 @@ async def login_oidc_start(request: Request, next: str = "/"): @router.get("/login/oidc/callback") +@limiter.limit(AUTH_RATE_LIMIT) async def login_oidc_callback(request: Request, state: str): runtime_config = get_runtime_config(request) oidc_state = request.app.state.session_service.consume_oidc_state(state) @@ -352,10 +353,12 @@ async def login_oidc_callback(request: Request, state: str): def _safe_redirect_path(url: str | None) -> str: - """Accept only relative paths to prevent open redirects.""" - if not url or "://" in url or not url.startswith("/"): + """Accept only relative paths to prevent open redirects, including protocol-relative URLs.""" + if not url: return "/" - return url + from urllib.parse import urlsplit + path = urlsplit(url).path or "/" + return path if path.startswith("/") else "/" def _do_logout(request: Request, next_url: str = "/") -> RedirectResponse: diff --git a/containers/caddy/process_config.py b/containers/caddy/process_config.py index 9e7093a..03eb6db 100644 --- a/containers/caddy/process_config.py +++ b/containers/caddy/process_config.py @@ -81,6 +81,19 @@ def build_caddyfile(apps, auth_policies, routes): for header_name in AUTH_IDENTITY_HEADERS: lines.append(f"{indent}request_header -{header_name}") + def emit_encoded_slash_block(): + # Reject paths containing %2f or %2F (percent-encoded slash). + # Caddy's path matcher does not decode percent-encoding, so /%2fadmin + # would NOT match path /admin and would fall through to the default + # (potentially bypass) handler, even though upstreams may decode it to /admin. + lines.append(" @encoded_slash {") + lines.append(" path_regexp (?i)%2f") + lines.append(" }") + lines.append(" handle @encoded_slash {") + lines.append(" respond 400") + lines.append(" }") + lines.append("") + def emit_route_matcher(matcher_name, path_prefix): matcher_name = re.sub(r"[^A-Za-z0-9_]", "_", matcher_name) normalized_prefix = path_prefix.strip().rstrip("/") or "/" @@ -139,6 +152,7 @@ def build_caddyfile(apps, auth_policies, routes): lines.append(" request_header -X-Forwarded-Host") emit_identity_header_sanitization() lines.append("") + emit_encoded_slash_block() emit_auth_portal() for static_route in static_routes: