Reject cross-site websocket upgrades and API calls - #847
Open
yuzi-co wants to merge 1 commit into
Open
Conversation
This was referenced Aug 1, 2026
yuzi-co
force-pushed
the
security/websocket-origin-check
branch
2 times, most recently
from
August 3, 2026 06:07
76ccba1 to
9d40430
Compare
All five websocket upgraders accepted any Origin, and no endpoint checked it either, while the session is carried by a cookie. A page the operator visits while logged in could therefore open the root shell at /api/vm/terminal, inject keystrokes over /api/ws, or POST to any endpoint. Only the browser's SameSite default stood in the way, and Firefox does not apply one. The auth middleware now rejects a request whose Origin names a different host, the upgraders use the same check through middleware.SameOrigin, and the token cookie is set with SameSite=Strict, plus Secure when served over https. A request with no Origin header is allowed: non-browser clients do not send one, and they are not what this defends against. Only the hostname is compared. The device serves the same UI over http and https, so an origin that arrives on the other port is still the same device. Both sides read it through url.URL.Hostname, so a port or the brackets around an IPv6 literal are stripped identically on each.
yuzi-co
force-pushed
the
security/websocket-origin-check
branch
from
August 13, 2026 17:48
9d40430 to
10b1918
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
All five websocket upgraders accept any
Origin:service/vm/terminal.goservice/ws/service.goservice/stream/direct/h264.goservice/stream/webrtc/h264.goservice/picoclaw/gateway_proxy.goeach with
CheckOrigin: func(r *http.Request) bool { return true }, and no endpoint checks it either. The session is carried by a cookie.Why that matters
A page the operator visits while logged in can open a websocket to the device and act as them.
/api/vm/terminalis a root shell./api/wsinjects keystrokes. Plain POST endpoints are reachable the same way.The only thing standing in the way today is the browser default for
SameSite, which is not something to rely on: Firefox does not applySameSite=Laxby default, so on Firefox this is exploitable as it stands.What this does
middleware.SameOrigincompares theOriginhost against the request host, and all five upgraders use it.SameSite=Strict, andSecurewhen served over https.A request with no
Originheader is allowed. Non-browser clients do not send one, and they are not what this defends against; browsers always send it on cross-origin requests.Only the hostname is compared. The device serves the same UI over http and https, so an origin that arrives on the other port is still the same device. Both sides read the host through
url.URL.Hostname, so a port or the brackets around an IPv6 literal are stripped identically on each.Tests
middleware/origin_test.goandmiddleware/jwt_test.gocover same-origin, cross-origin, absent header, IPv6 hosts, and port and scheme mismatches.Verified with
go build,go vet,go test, andGOOS=linux GOARCH=riscv64 go build. Tests need-tags novision(#846) to run off-device.Updated: the webrtc read limit that was originally bundled here has been split into its own PR, as offered above. This PR is now only the origin check.