Skip to content

WS auth token is replayable within 5-min window and falls back to empty HMAC secret #212

Description

@v1ktorrr0x

Bug

Two independent issues in WS token handling:

  1. Replay window — tokens are an HMAC over slab:timestamp with no nonce and no binding to client IP or connection ID. An observed valid token is replayable by any party within the 5-minute freshness window.

  2. Empty-secret fallbackWS_AUTH_SECRET || "" keys HMACs with the empty string when the env var is unset. Production exits on a missing secret (ws.ts:61-64), but a misconfigured non-production deploy with WS_AUTH_REQUIRED=false accepts tokens signed with "", which are trivially forgeable.

Source

src/routes/ws.ts

76  const WS_SECRET = WS_AUTH_SECRET || "";  // empty string when unset
313 function verifyWsToken(token: string, expectedSlab?: string) {
322   // window: 5 minutes + 30s clock-skew tolerance, no nonce
324   if (now - timestamp > 5 * 60 * 1000 || timestamp > now + 30_000) ...

Note: the Number.isNaN guard at :319-320 that precedes the window comparison is correct and handles the NaN-comparison bypass class — do not change it.

Fix

  • Bind tokens to the connecting client IP (include it in the HMAC payload and reject on mismatch at verification).
  • Refuse all HMAC operations when WS_AUTH_SECRET is empty, regardless of WS_AUTH_REQUIRED. Throw at module load for non-development environments; emit a critical log warning in development.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions