fix(ws): add Origin allowlist to browser WebSocket upgrade (CSWSH hardening)
Closes Draco-Lunaris/Linux-Patch-Manager#10 The browser WebSocket endpoint at GET /api/v1/ws/jobs previously authenticated solely via a single-use, 60-second ticket passed as a query parameter. A leaked ticket (browser history, Referer, proxy logs, support bundles) could be redeemed from any origin, enabling Cross-Site WebSocket Hijacking (CSWSH). This change adds a second gate: the Origin header must match an explicit allowlist. The check runs BEFORE ticket validation so that rejected cross-origin probes do not consume the legitimate users ticket. Changes: - pm-core: new security.allowed_origins config field; default derived from sso_callback_url; startup warning if both are unparseable - pm-web: ws_handler extracts HeaderMap and calls check_origin first; returns 403 on missing/malformed/disallowed origins - config: documented allowed_origins key in config.example.toml - docs: security-review.md section 1.4 (WebSocket Origin Allowlist) - tests: 40 unit tests (7 pm-core, 33 pm-web)
This commit is contained in:
@ -108,6 +108,21 @@ web_tls_key_path = "/etc/patch-manager/tls/web.key"
|
||||
# Default: "http://localhost:5173/auth/sso/callback" (Vite dev server)
|
||||
sso_callback_url = "http://localhost:5173/auth/sso/callback"
|
||||
|
||||
# Allowlist of browser `Origin` values permitted to open the
|
||||
# `/api/v1/ws/jobs` WebSocket upgrade. Each entry is an exact
|
||||
# `scheme://host[:port]` string (no wildcards, no paths). When this list is
|
||||
# empty, the server derives a single-entry default from `sso_callback_url`
|
||||
# at startup (the host of the SSO callback). If the derivation also fails,
|
||||
# a warning is logged and the WS endpoint rejects all browser upgrades
|
||||
# (fail-closed).
|
||||
#
|
||||
# Add additional origins here if your SPA and API are served from different
|
||||
# hosts (e.g. SPA on https://app.example.com talking to API on
|
||||
# https://api.example.com). For typical single-host deployments the derived
|
||||
# default is correct and this line should be left commented out.
|
||||
#
|
||||
# allowed_origins = ["https://patch-manager.example.com"]
|
||||
|
||||
# ============================================================
|
||||
# Rate Limiting
|
||||
# ============================================================
|
||||
|
||||
Reference in New Issue
Block a user