{"id":"CVE-2026-34083","aliases":["GHSA-cxj8-ggf2-p57c"],"url":"https://o3.security/vulnerability/CVE-2026-34083","summary":"signalk-server: OAuth Authorization Code Theft via Unvalidated Host Header in OIDC Flow","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).","published":"2026-04-02T16:14:38.893Z","modified":"2026-08-12T03:51:12.674166431Z","cvss":{"score":6.1,"severity":"MEDIUM","vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N"},"epss":null,"cisaKev":null,"exploitsKnown":0,"affectedPackages":[{"ecosystem":"npm","name":"signalk-server","fixedVersion":"2.24.0"}],"fix":null,"references":[{"type":"WEB","url":"https://github.com/SignalK/signalk-server/releases/tag/v2.24.0"},{"type":"ADVISORY","url":"https://github.com/CVEProject/cvelistV5/tree/main/cves/2026/34xxx/CVE-2026-34083.json"},{"type":"ADVISORY","url":"https://github.com/SignalK/signalk-server/security/advisories/GHSA-cxj8-ggf2-p57c"},{"type":"ADVISORY","url":"https://nvd.nist.gov/vuln/detail/CVE-2026-34083"},{"type":"PACKAGE","url":"https://github.com/SignalK/signalk-server"}],"provenance":{"sources":["OSV.dev","FIRST.org (EPSS)"],"lastVerified":"2026-08-12T03:51:12.674166431Z"}}