2.4 KiB
Phase: 8 Sequence: 008 Slug: websocket-jwt-query-param Verdict: VALID Rationale: WebSocket JWT passed in query parameter is visible in server/proxy/access logs; captured JWTs can be replayed to hijack WebSocket connections Severity-Original: medium Severity: medium PoC-Status: pending Pre-FP-Flag: none Debate: piolium/attack-surface/balanced-chamber-summary.md
Summary
The WebSocket server in web/src/server/websocket.ts authenticates connections by extracting a JWT from the `?token=[REDACTED:secret]] query parameter. This means the JWT token is visible in server access logs, proxy/load balancer logs (nginx, CloudFront, Vercel edge logs), browser network history, and any log aggregation system. An attacker with access to these logs can capture JWTs and connect to the WebSocket server as any user.
Location
web/src/server/websocket.tslines 39–43 (token extraction)web/src/server/websocket.tslines 56–67 (authentication)
Attacker Control
An attacker with access to server/proxy logs can capture JWTs from WebSocket connection URLs. The captured JWT can be replayed to establish WebSocket connections as the victim user.
Trust Boundary Crossed
Authentication boundary. JWT tokens are exposed through log layers, allowing attackers to authenticate as any user by replaying captured tokens.
Impact
JWT token leakage through server logs enables WebSocket connection hijacking for any user whose token appears in logs. The attacker gains read-only access to real-time alerts (darkwatch exposures, voiceprint alerts, spam notifications).
Evidence
function getTokenFromRequest(req: IncomingMessage): string | null {
const url = new URL(req.url ?? "/", "http://localhost");
return url.searchParams.get("token"); // JWT in query string
}
Reproduction Steps
- Legitimate user connects WebSocket with `ws://host:3001/?token=[REDACTED:secret]]
- Server logs the full URL including the JWT token
- Attacker gains access to server logs (via log aggregation compromise, shared hosting, etc.)
- Attacker replays the JWT to establish a WebSocket connection as the victim
- Attacker receives real-time alerts for the victim's account
Defense Search Results
- JWT verification (
verifyJWT()) validates signature and expiry - No
Originheader validation on WebSocket upgrade (see p8-009) - No
Sec-WebSocket-Protocolheader validation - No message size limit
- Heartbeat timeout (30s interval + 10s pong timeout) prevents slow-loris DoS