Skip to content

Dashboard stores bearer token in localStorage; WebSocket auth via ?token= URL query string #3620

Description

@houko

Severity: High
Location: crates/librefang-api/dashboard/src/api.ts:811, 833-840, 843-848, 2824

Problem

The dashboard stores its bearer token in localStorage (XSS-readable; any future dangerouslySetInnerHTML slip exfiltrates it — currently safe because react-markdown is used safely, but fragile). For WebSockets, buildAuthenticatedWebSocketUrl puts the token in ?token=... — query strings land in server access logs, browser history, and the HTTP Referer header if the WS URL ever leaks. dashboardLogin writes the token straight to localStorage with no expiry tracking, no refresh-token rotation, no idle timeout. Token lives forever until manual logout or 401.

Attack

  1. XSS via any future markdown-rendering regression → full token theft. 2) nginx access log compromise → all WS tokens harvested. 3) Browser history sync to a different device → token leaks across sessions.

Suggested fix

Prefer httpOnly cookie auth (the codebase already uses credentials: "same-origin" in dashboardLogout). For WebSockets, use the Sec-WebSocket-Protocol subprotocol header instead of query string. At minimum, document why localStorage is acceptable here and add an idle-timeout on the SPA side.


Filed from cross-cutting bug/design audit (2026-04-28). Line numbers may drift across commits — verify before fixing.

Metadata

Metadata

Assignees

No one assigned

    Labels

    area/apiREST/WS endpoints and dashboardarea/securitySecurity systems and auditingbugSomething isn't workingseverity/highSignificant functional, security, or performance impact

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions