3.3 KiB
3.3 KiB
08. Fix WebSocket JWT leakage via query parameter
meta: id: security-fixes-08 feature: security-fixes priority: P1 depends_on: [] tags: [implementation, tests-required, medium-severity]
objective:
- Move WebSocket JWT authentication from query parameter to Authorization header to prevent token leakage in logs
deliverables:
- Updated
getTokenFromRequest()atweb/src/server/websocket.ts:39-43to read from Authorization header - Updated WebSocket client code to send JWT in the Authorization header
- Backward compatibility during transition period (optional)
- Unit tests for token extraction from headers
steps:
- Examine
web/src/server/websocket.ts:39-43(getTokenFromRequest) andweb/src/server/websocket.ts:56-67(authentication flow) - Update
getTokenFromRequest()to:- First check the
Authorizationheader for a Bearer token (Authorization: Bearer <jwt>) - Fall back to
?token=query parameter with a deprecation warning (optional, for backward compatibility) - Return
nullif neither is present
- First check the
- Update the WebSocket client (browser app and extension) to:
- Include the JWT in a custom header:
new WebSocket(url, { headers: { Authorization:Bearer ${token}} }) - Note: The WebSocket constructor doesn't support custom headers in browsers; use the
verifyClientcallback on the server side to read headers from the upgrade request - Alternative: Use an HTTP handshake approach or pass the token in a message after connection (with a timeout)
- Include the JWT in a custom header:
- If browser WebSocket API limitations prevent header-based auth, implement a post-connection authentication message with a timeout (e.g., client must send
{type: "auth", token: "..."}within 5 seconds) - Update
web/src/server/websocket.ts:80-102(connection handler) to enforce the new auth flow
tests:
- Unit:
getTokenFromRequest()extracts token from Authorization header - Unit:
getTokenFromRequest()rejects connections without a token - Unit: Query parameter fallback (if implemented) logs a deprecation warning
- Integration: WebSocket connection with valid Bearer token is authenticated
- Integration: WebSocket connection without a token is rejected
acceptance_criteria:
- JWT is no longer passed in the query parameter by default
- Server accepts JWT from Authorization header (or post-connection auth message if browser limitation requires it)
- Connections without authentication are rejected
- No JWT tokens appear in server access logs or proxy logs
- Backward compatibility is handled (if implemented) with a deprecation path
validation:
cd web && bun test— all tests pass- Connect via WebSocket with a valid token in the expected location and verify authentication succeeds
- Connect without a token and verify the connection is rejected
- Check server logs to confirm JWT tokens are not visible in URLs
notes:
- Finding p8-008: The browser WebSocket API does not support custom headers in the constructor
- Recommended approach: Post-connection authentication message with a server-side timeout
- Client connects without token
- Server allows the connection but marks it as unauthenticated
- Client sends
{type: "auth", token: "..."}within 5 seconds - Server validates the token and upgrades the connection
- Unauthenticated connections are closed after the timeout
- This approach avoids JWT in URLs and works with the browser WebSocket API