Skip to content

Security: HMAC-sign login_user cookie + drop Origin/Referer fallback in frontend URL - #174

Merged
keysersoft merged 1 commit into
mainfrom
keysersoft/fix-cookie-bypass
May 12, 2026
Merged

keysersoft merged 1 commit into
mainfrom
keysersoft/fix-cookie-bypass

Conversation

@keysersoft

Copy link
Copy Markdown
Contributor

Summary

Two real CodeQL findings, both auth-adjacent, both pre-launch blockers.

1. `login_user` cookie was forgeable (CodeQL #56 `js/user-controlled-bypass`)

`local-oauth.strategy.ts` read `req.cookies.login_user`, base64-decoded the JSON, and trusted the resulting profile as the authenticated user. The cookie was set without a secret in `cookieParser()`, so it was NOT HMAC-signed — anyone who knew a target user's id/email could forge a profile, set the cookie in their browser, navigate to `/callback`, and log in as that user.

Fix:

  • `main.ts` — initialise `cookieParser(secret)` with `COOKIE_SECRET` (or `JWT_SECRET` as fallback). Throws at startup if neither is present. Logs a warning in production when the fallback is used.
  • `login.controller.ts` — set the cookie with `{ signed: true }`.
  • `local-oauth.strategy.ts` — read from `req.signedCookies` (not `req.cookies`). Unsigned/forged cookies silently rejected → redirect to login.
  • `.env.example` — document `COOKIE_SECRET` as optional separate secret for defense in depth.

2. XSS in password-reset email via Origin header (CodeQL #11 `js/xss`)

`auth.controller.ts#getFrontendUrl()` preferred `req.headers.origin` over the `FRONTEND_URL` env var. That value was then interpolated into the password-reset email HTML inside an `` attribute. Attacker crafting an Origin header like `" onmouseover="alert(...)" ` could inject HTML attribute content into the recipient's email.

Fix: drop the Origin/Referer fallback entirely. Frontend URL must come from server-side config (`FRONTEND_URL` → `SERVER_URL` → `localhost`), which is operator-controlled.

Test plan

  • Lint clean on the modified files
  • After merge: existing OAuth login flow still works (cookie now signed, but cookieParser auto-signs/verifies — frontend doesn't notice anything)
  • After merge: password-reset email uses FRONTEND_URL, not request Origin
  • After merge: CodeQL alerts Fix ARM64 Docker build timeout #56 and feat: generate MongoDB-native tools for MongoDB connectors #11 auto-close on next scan
  • Manual check: setting a hand-crafted `login_user` cookie in a fresh browser no longer logs in — redirects to /login

Migration note for self-hosters

No env-var change required for existing deployments (falls back to `JWT_SECRET` if `COOKIE_SECRET` not set). Strongly recommended to add a separate `COOKIE_SECRET=<32+ chars>` in production .env for defense in depth.

…r XSS

## Bug 1 — Forgeable login_user cookie (CodeQL #56, user-controlled-bypass)

The OAuth callback flow read req.cookies.login_user, base64-decoded the
JSON, and trusted the resulting 'profile' as the authenticated user. The
cookie was set without a secret in cookieParser(), so it was NOT
HMAC-signed — anyone who knew a target user's id/email could forge a
profile, set the cookie in their browser, navigate to /callback, and log
in as that user.

Fix:
- main.ts: initialise cookieParser(secret) with COOKIE_SECRET (or
  JWT_SECRET as fallback). Throws at startup if neither is present.
  Logs a warning in production when the fallback is used.
- login.controller.ts: set the login_user cookie with { signed: true }.
- local-oauth.strategy.ts: read from req.signedCookies (not req.cookies).
  Unsigned/forged cookies are silently rejected → redirected to /login.
- .env.example: document COOKIE_SECRET as an optional separate secret
  for defense in depth.

## Bug 2 — XSS in password-reset email via Origin header (CodeQL #11)

auth.controller.ts#getFrontendUrl() preferred req.headers.origin over
the FRONTEND_URL env var. That value was then interpolated into the
password-reset email HTML (email.service.ts:46) inside an <a href>
attribute. An attacker crafting an Origin header like
\" onmouseover=\"alert(...)\" could inject HTML attribute content
into the recipient's email.

Fix: drop the Origin/Referer fallback entirely. Frontend URL must come
from server-side config (FRONTEND_URL → SERVER_URL → localhost) which
is operator-controlled, not attacker-controlled.
@keysersoft
keysersoft enabled auto-merge (squash) May 12, 2026 08:56
@keysersoft
keysersoft merged commit d0af3a3 into main May 12, 2026
11 checks passed
@keysersoft
keysersoft deleted the keysersoft/fix-cookie-bypass branch May 12, 2026 08:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant