security sweep
This commit is contained in:
@@ -0,0 +1,60 @@
|
||||
# 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()` at `web/src/server/websocket.ts:39-43` to 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:
|
||||
1. Examine `web/src/server/websocket.ts:39-43` (`getTokenFromRequest`) and `web/src/server/websocket.ts:56-67` (authentication flow)
|
||||
2. Update `getTokenFromRequest()` to:
|
||||
- First check the `Authorization` header for a Bearer token (`Authorization: Bearer <jwt>`)
|
||||
- Fall back to `?token=` query parameter with a deprecation warning (optional, for backward compatibility)
|
||||
- Return `null` if neither is present
|
||||
3. 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 `verifyClient` callback 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)
|
||||
4. 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)
|
||||
5. 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
|
||||
1. Client connects without token
|
||||
2. Server allows the connection but marks it as unauthenticated
|
||||
3. Client sends `{type: "auth", token: "..."}` within 5 seconds
|
||||
4. Server validates the token and upgrades the connection
|
||||
5. Unauthenticated connections are closed after the timeout
|
||||
- This approach avoids JWT in URLs and works with the browser WebSocket API
|
||||
Reference in New Issue
Block a user