Files
kandev/apps/backend/internal/gateway/websocket/origin.go
T
João Salavisa 3746a30126 fix: enforce origin validation on websocket upgrades (#1698)
* fix: enforce origin validation on websocket upgrades

The gateway /ws upgrader hardcoded CheckOrigin to true, so any web page
could open a socket to the local backend and drive session/shell actions
(cross-site WebSocket hijacking → RCE on the host).

Both upgraders now share checkWebSocketOrigin: no Origin header is
allowed (non-browser clients), same-hostname and loopback↔loopback
origins are allowed, everything else is rejected. Replaces the terminal
handler's prefix-based check, which accepted origins like
http://localhost.attacker.tld.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* refactor: share origin trust policy between cors and websocket checks

Review feedback on #1698: the CORS middleware and the WebSocket upgrader
carried two copies of the same origin policy, which could silently
diverge. Both now delegate to httpmw.AllowedOrigin, which also rejects
URL components that never appear in a browser Origin header (userinfo,
path, query, fragment, opaque data).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

---------

Co-authored-by: Kandev Agent <agent@kandev.dev>
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-07-15 01:13:46 +01:00

24 lines
756 B
Go

package websocket
import (
"net/http"
"github.com/kandev/kandev/internal/common/httpmw"
)
// checkWebSocketOrigin validates the Origin header on WebSocket upgrade
// requests to prevent cross-site WebSocket hijacking: without it, any web
// page the user visits could open a socket to the local backend and drive
// session/shell actions on the host.
//
// Requests without an Origin header are allowed (non-browser clients such as
// the CLI or curl); everything else defers to the shared origin trust policy
// in httpmw.AllowedOrigin, the same policy the CORS middleware enforces.
func checkWebSocketOrigin(r *http.Request) bool {
origin := r.Header.Get("Origin")
if origin == "" {
return true
}
return httpmw.AllowedOrigin(origin, r.Host)
}