mirror of
https://github.com/eduardogsilva/wireguard_webadmin.git
synced 2026-03-17 22:36:17 +00:00
enhance security by sanitizing headers and preventing open redirects
This commit is contained in:
@@ -1,3 +1,5 @@
|
|||||||
|
import re
|
||||||
|
|
||||||
from auth_gateway.services.policy_engine import evaluate_ip_access, extract_client_ip
|
from auth_gateway.services.policy_engine import evaluate_ip_access, extract_client_ip
|
||||||
from auth_gateway.services.resolver import resolve_request_context
|
from auth_gateway.services.resolver import resolve_request_context
|
||||||
from auth_gateway.web.dependencies import (
|
from auth_gateway.web.dependencies import (
|
||||||
@@ -51,15 +53,18 @@ async def auth_check(request: Request):
|
|||||||
login_url = build_external_url(request, "/login", next=context.path)
|
login_url = build_external_url(request, "/login", next=context.path)
|
||||||
return RedirectResponse(login_url, status_code=302)
|
return RedirectResponse(login_url, status_code=302)
|
||||||
|
|
||||||
|
def _safe_header(value: str) -> str:
|
||||||
|
return re.sub(r"[\r\n\x00]", "", value)
|
||||||
|
|
||||||
response = PlainTextResponse("OK", status_code=200)
|
response = PlainTextResponse("OK", status_code=200)
|
||||||
if session:
|
if session:
|
||||||
if session.username:
|
if session.username:
|
||||||
response.headers["X-Auth-User"] = session.username
|
response.headers["X-Auth-User"] = _safe_header(session.username)
|
||||||
if session.email:
|
if session.email:
|
||||||
response.headers["X-Auth-Email"] = session.email
|
response.headers["X-Auth-Email"] = _safe_header(session.email)
|
||||||
if session.groups:
|
if session.groups:
|
||||||
response.headers["X-Auth-Groups"] = ",".join(session.groups)
|
response.headers["X-Auth-Groups"] = _safe_header(",".join(session.groups))
|
||||||
if session.auth_factors:
|
if session.auth_factors:
|
||||||
response.headers["X-Auth-Factors"] = ",".join(session.auth_factors)
|
response.headers["X-Auth-Factors"] = _safe_header(",".join(session.auth_factors))
|
||||||
response.headers["X-Auth-Policy"] = effective_policy.name
|
response.headers["X-Auth-Policy"] = _safe_header(effective_policy.name)
|
||||||
return response
|
return response
|
||||||
|
|||||||
@@ -69,7 +69,7 @@ async def login_page(request: Request, next: str = "/"):
|
|||||||
if effective_policy.mode == "deny":
|
if effective_policy.mode == "deny":
|
||||||
return _render(request, "error.html", status_code=403, title="Access denied", message="This route is blocked by policy.")
|
return _render(request, "error.html", status_code=403, title="Access denied", message="This route is blocked by policy.")
|
||||||
if effective_policy.mode == "bypass":
|
if effective_policy.mode == "bypass":
|
||||||
return RedirectResponse(next, status_code=303)
|
return RedirectResponse(context.path, status_code=303)
|
||||||
|
|
||||||
if session and session_is_allowed(session, effective_policy):
|
if session and session_is_allowed(session, effective_policy):
|
||||||
current_factors = set(session.auth_factors)
|
current_factors = set(session.auth_factors)
|
||||||
@@ -79,11 +79,11 @@ async def login_page(request: Request, next: str = "/"):
|
|||||||
current_factors.add("ip")
|
current_factors.add("ip")
|
||||||
missing_factors = [factor for factor in effective_policy.required_factors if factor not in current_factors]
|
missing_factors = [factor for factor in effective_policy.required_factors if factor not in current_factors]
|
||||||
if not missing_factors:
|
if not missing_factors:
|
||||||
return RedirectResponse(next, status_code=303)
|
return RedirectResponse(context.path, status_code=303)
|
||||||
if missing_factors == ["totp"]:
|
if missing_factors == ["totp"]:
|
||||||
return RedirectResponse(build_external_url(request, "/login/totp", next=next), status_code=303)
|
return RedirectResponse(build_external_url(request, "/login/totp", next=context.path), status_code=303)
|
||||||
if missing_factors == ["oidc"]:
|
if missing_factors == ["oidc"]:
|
||||||
return RedirectResponse(build_external_url(request, "/login/oidc/start", next=next), status_code=303)
|
return RedirectResponse(build_external_url(request, "/login/oidc/start", next=context.path), status_code=303)
|
||||||
|
|
||||||
available_methods = []
|
available_methods = []
|
||||||
if effective_policy.password_method_names:
|
if effective_policy.password_method_names:
|
||||||
@@ -94,16 +94,16 @@ async def login_page(request: Request, next: str = "/"):
|
|||||||
available_methods.append("totp")
|
available_methods.append("totp")
|
||||||
|
|
||||||
if available_methods == ["password"]:
|
if available_methods == ["password"]:
|
||||||
return RedirectResponse(build_external_url(request, "/login/password", next=next), status_code=303)
|
return RedirectResponse(build_external_url(request, "/login/password", next=context.path), status_code=303)
|
||||||
if available_methods == ["oidc"]:
|
if available_methods == ["oidc"]:
|
||||||
return RedirectResponse(build_external_url(request, "/login/oidc/start", next=next), status_code=303)
|
return RedirectResponse(build_external_url(request, "/login/oidc/start", next=context.path), status_code=303)
|
||||||
if available_methods == ["totp"]:
|
if available_methods == ["totp"]:
|
||||||
return RedirectResponse(build_external_url(request, "/login/totp", next=next), status_code=303)
|
return RedirectResponse(build_external_url(request, "/login/totp", next=context.path), status_code=303)
|
||||||
|
|
||||||
return _render(
|
return _render(
|
||||||
request,
|
request,
|
||||||
"login.html",
|
"login.html",
|
||||||
next=next,
|
next=context.path,
|
||||||
methods=available_methods,
|
methods=available_methods,
|
||||||
application_name=context.application.name,
|
application_name=context.application.name,
|
||||||
policy_name=context.policy_name,
|
policy_name=context.policy_name,
|
||||||
@@ -265,13 +265,20 @@ async def login_oidc_callback(request: Request, state: str):
|
|||||||
return _redirect_with_cookie(request, oidc_state.next_url, session)
|
return _redirect_with_cookie(request, oidc_state.next_url, session)
|
||||||
|
|
||||||
|
|
||||||
|
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("/"):
|
||||||
|
return "/"
|
||||||
|
return url
|
||||||
|
|
||||||
|
|
||||||
def _do_logout(request: Request, next_url: str = "/") -> RedirectResponse:
|
def _do_logout(request: Request, next_url: str = "/") -> RedirectResponse:
|
||||||
session_cookie = request.cookies.get(request.app.state.settings.cookie_name)
|
session_cookie = request.cookies.get(request.app.state.settings.cookie_name)
|
||||||
session = request.app.state.session_service.get_session(session_cookie)
|
session = request.app.state.session_service.get_session(session_cookie)
|
||||||
request.app.state.session_service.delete_session(session_cookie)
|
request.app.state.session_service.delete_session(session_cookie)
|
||||||
if session:
|
if session:
|
||||||
logger.info("AUTH logout for '%s'", session.username or session.email or "unknown")
|
logger.info("AUTH logout for '%s'", session.username or session.email or "unknown")
|
||||||
response = RedirectResponse(next_url or "/", status_code=303)
|
response = RedirectResponse(_safe_redirect_path(next_url), status_code=303)
|
||||||
response.delete_cookie(request.app.state.settings.cookie_name, path="/")
|
response.delete_cookie(request.app.state.settings.cookie_name, path="/")
|
||||||
return response
|
return response
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user