security sweep
This commit is contained in:
@@ -0,0 +1,55 @@
|
||||
Phase: 8
|
||||
Sequence: 009
|
||||
Slug: websocket-no-origin-validation
|
||||
Verdict: VALID
|
||||
Rationale: WebSocket server on port 3001 does not validate Origin header during upgrade handshake; combined with JWT-in-query-param, any website can initiate WebSocket connections using stolen tokens
|
||||
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` does not validate the `Origin` header during the HTTP upgrade request. Combined with JWT authentication via query parameter, this means any website can initiate a WebSocket connection to the server on behalf of an authenticated user (if the user's JWT is known or leaked via p8-008).
|
||||
|
||||
## Location
|
||||
- `web/src/server/websocket.ts` lines 80–102 (connection handler)
|
||||
- `web/src/server/websocket.ts` lines 56–67 (authentication)
|
||||
|
||||
## Attacker Control
|
||||
An attacker controlling a malicious website (e.g., evil.com) can initiate WebSocket connections to the server. If the attacker has obtained the victim's JWT (via log exposure, XSS, or other means), they can authenticate as the victim without Origin validation.
|
||||
|
||||
## Trust Boundary Crossed
|
||||
Cross-origin boundary. The WebSocket server accepts connections from any origin without validation, allowing cross-origin WebSocket connections that bypass same-origin policy protections.
|
||||
|
||||
## Impact
|
||||
Cross-origin WebSocket connections without Origin validation. Combined with JWT-in-query-parameter (p8-008), this creates a complete authentication bypass chain accessible from any website.
|
||||
|
||||
## Evidence
|
||||
```typescript
|
||||
wss.on("connection", async (ws: WsClient, req: IncomingMessage) => {
|
||||
const userId = await authenticateConnection(ws, req);
|
||||
// No Origin header check anywhere
|
||||
// req.origin is available but never inspected
|
||||
if (!userId) {
|
||||
ws.close(4001, "Authentication failed");
|
||||
return;
|
||||
}
|
||||
ws.userId = userId;
|
||||
addSocket(userId, ws);
|
||||
});
|
||||
```
|
||||
|
||||
## Reproduction Steps
|
||||
1. Attacker controls a malicious website (evil.com)
|
||||
2. User is authenticated on Kordant (has valid JWT)
|
||||
3. If JWT is leaked (see p8-008), attacker crafts WebSocket connection from evil.com
|
||||
4. WebSocket server accepts the connection without Origin validation
|
||||
5. Attacker receives real-time alerts for the victim's account
|
||||
|
||||
## Defense Search Results
|
||||
- No `verifyClient` option used on WebSocketServer
|
||||
- CORS middleware in `web/src/middleware.ts` does not apply to WebSocket upgrade (different handler, port 3001)
|
||||
- JWT verification validates signature and expiry but not origin
|
||||
- No per-user connection limit
|
||||
- Heartbeat timeout prevents unresponsive connections
|
||||
Reference in New Issue
Block a user