+ "details": "## Summary\n\nSignalK Server contains a code-level vulnerability in its OIDC login and logout handlers where the unvalidated HTTP Host header is used to construct the OAuth2 redirect_uri. Because the redirectUri configuration is silently unset by default, an **attacker spoof the Host header** to steal OAuth authorization codes and hijack user sessions in realistic deployments as The OIDC provider will then send the authorization code to whatever domain was injected.\n\n_The OIDC specification requires redirect_uri to be pre-registered and not derived from untrusted input. Constructing it from the Host header violates this requirement and introduces a trust boundary break._\nThis risk is actively amplified by SignalK's official documentation, which instructs administrators to deploy an Nginx configuration that forwards the vulnerable Host header, exposing production environments.\n\n## Vulnerability Root Cause\n\nTwo factors combine to create this vulnerability:\n\n**Factor 1: redirectUri is optional with an unsafe fallback**\nIn types.ts:30, redirectUri is declared as optional\n```\nexport interface OIDCConfig {\n // ...\n redirectUri?: string // ← Optional, no default value\n // ...\n}\n```\n\nThe defaults in types.ts:175-185 do not include a redirectUri: never checks or warns about a missing redirectUri. This means a fully \"valid\" OIDC configuration can exist without redirectUri, silently activating the vulnerable fallback path.\n```\nexport const OIDC_DEFAULTS: Omit<OIDCConfig, 'issuer' | 'clientId' | 'clientSecret'> = {\n enabled: false,\n scope: 'openid email profile',\n defaultPermission: 'readonly',\n autoCreateUsers: true,\n providerName: 'SSO Login',\n autoLogin: false\n // ← No redirectUri default\n}\n```\n\n**Factor 2: Unsafe Host header usage in two locations**\nLocation 1 — Login handler in oidc-auth.ts:278-282:\n```\nconst protocol = req.secure ? 'https' : 'http'\nconst host = req.get('host') // ← Attacker-controlled\nconst redirectUri =\n oidcConfig.redirectUri || // ← Only safe if explicitly set\n `${protocol}://${host}${skAuthPrefix}/oidc/callback` // ← Uses attacker's Host\n\n```\nThis redirectUri flows into createAuthState() → buildAuthorizationUrl() → OIDC provider's redirect_uri parameter. The OIDC provider will then send the authorization code to whatever domain was injected.\n\nLocation 2 — Logout handler in oidc-auth.ts:513-515:\n```\nconst protocol = req.secure ? 'https' : 'http'\nconst host = req.get('host') // ← Same pattern\nconst fullPostLogoutUri = `${protocol}://${host}${postLogoutRedirect}`\n```\nThis constructs the post_logout_redirect_uri sent to the OIDC provider's end_session_endpoint, allowing an attacker to redirect the user to an attacker controlled domain after logout.\n\n### Official Documentation Enables the Attack\n\nSignalK's own security documentation at docs/security.md:222-228 provides the recommended nginx reverse proxy configuration:\nThe proxy_set_header Host $host; directive forwards the client-supplied Host header to the backend unmodified. Without this directive, nginx would replace the Host header with the upstream address (localhost:3000), which would neutralize the injection.\n```\nlocation / {\n proxy_pass http://localhost:3000;\n proxy_set_header X-Forwarded-For $remote_addr;\n proxy_set_header X-Forwarded-Proto $scheme;\n proxy_set_header Host $host; # ← Forwards client's Host header to SignalK\n}\n```\nAdministrators who follow the official documentation are directly enabling this vulnerability behind their reverse proxy.\n\n## Proof of Concept \nTested against SignalK Server v2.23.0 in Docker with OIDC enabled .\n\n**Step 1 — Send login request with injected Host header:**\n`$response = Invoke-WebRequest -Uri \"http://localhost:3000/signalk/v1/auth/oidc/login\" -Headers @{\"Host\"=\"evil.com\"} -MaximumRedirection 0 -ErrorAction SilentlyContinue -UseBasicParsing`\n\n**Step 2: Decode and print the injected redirect URL**\n`[uri]::UnescapeDataString($response.Headers.Location)\n`\n<img width=\"1259\" height=\"211\" alt=\"Screenshot 2026-03-25 171251\" src=\"https://github.com/user-attachments/assets/6e4a9655-639e-48c2-a7f0-06e17ad471ff\" />\n\n## Impact\n\n* **Authorization Code Theft:** The OIDC provider sends the OAuth authorization code to the attacker's domain instead of the legitimate server.\n* **Session Hijack:** The attacker can exchange the stolen code for tokens and create a session as the victim user.\n* **Logout Redirect Hijack:** The logout handler has the same pattern, allowing post-logout redirection to an attacker domain (phishing opportunity).",
0 commit comments