Websocket security
SecureVibe — prevention-first security for AI-written code. Signed SKILL.md knowledge that makes AI coding assistants write secure code at generation time, plus a deterministic CI gate. Offline · keyless · Ed25519-signed. By ShieldNet360.
npx -y skills add ShieldNet-360/secure-vibe --skill websocket-securityAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 2 stars2 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
What its author says it does
Copied from the file, not written here
Secure WebSocket endpoints: Origin validation, auth on handshake, message size/rate limits, wss-only, reconnection backoff
SKILL.md
8.0 KB, ~1.7k tokens by cl100k_base, as published. Nobody here has run it
WebSocket Security
Rules (for AI agents)
ALWAYS
- Validate the
Originheader on the WebSocket upgrade handshake against an allowlist. CORS does not apply to WebSockets — the browser will happily upgrade cross-origin and let JavaScript onattacker.comopenwss://api.example.com/wswith the user's cookies (Cross-Site WebSocket Hijacking). - Require authentication on the handshake itself, not as the first
message after connect. Either:
- Cookie-based auth on the HTTP upgrade (and CSRF-protect by verifying Origin), or
- A short-lived signed token (5–10 minute lifetime) in the
Sec-WebSocket-Protocolsubprotocol header, or - A signed query parameter token.
Never trust a
subscribe/authmessage after the upgrade — by that point the connection has already been opened with the authenticated cookie context.
- Use
wss://only in production. Plainws://over the open internet exposes session tokens, message contents, and CSRF primitives to any on-path observer. - Enforce max message size at the server (typical: 32 KiB for chat, 256 KiB for collaborative editing, much higher only when the use case demands it and the auth bar is high). Without a limit, a single open socket can OOM the server.
- Enforce a message rate limit per connection (e.g. 60 messages/minute) and a connection rate limit per source IP / per authenticated user. Real-time abuse (chat spam, presence ping flood) is a frequent DoS source.
- Implement ping / pong heartbeats (every 20–30 s) and close the connection on missed pong. Half-open TCP sockets accumulate behind load balancers otherwise.
- On the client side, use bounded exponential backoff for
reconnection (e.g. base 1 s, factor 2, max 60 s, jitter ±20%).
A naïve
setTimeout(connect, 0)reconnect loop melts the server during outages. - Treat each WebSocket message as a separate request for the purposes of input validation and authorization. The user's permissions can change after the socket is open (logout, role change, account lock) — re-check on each privileged action.
- Object-level-authorize the subject / resource id carried in each
message, not just the action type. A frame like
{"action":"write","subjectId":"X"}must be checked so the connection's handshake-authenticated principal may actually act onX. An authenticated socket must not be able to assert an arbitrary subject id per frame — that is per-frame BOLA, and the forged id reaches any consumer downstream of the socket (queue / topic / fan-out) that trusts it.
NEVER
- Skip Origin validation because "it's a WebSocket, CORS doesn't apply." That's exactly why you have to do it yourself. The documented attack is Cross-Site WebSocket Hijacking, demonstrated publicly in 2013 and still common in 2024 bug-bounty reports.
- Use a session cookie as a long-lived WebSocket token. If the WS connection is supposed to survive multiple tabs / pages, issue a refreshable short-lived JWT in the subprotocol; don't rely on the cookie sticking around forever.
- Allow arbitrary
subprotocolsfrom the client to influence server-side routing without an allowlist. Subprotocol negotiation is attacker-controlled. - Run WebSocket handlers in the same process / thread pool as HTTP request handlers without sizing limits — a slow-loris-style WebSocket can starve all HTTP work.
- Expose internal cluster topology in WebSocket messages (e.g.
{"server_id": "pod-prod-42"}). Internal IDs are reconnaissance material on a chatty real-time channel.
KNOWN FALSE POSITIVES
- Public chat / presence endpoints that are intentionally open to any
origin must still enforce per-connection rate limits and a
per-source-IP cap; they may legitimately permit
Origin: nullfor desktop / mobile clients. - Mobile / desktop native clients send no
Originheader. Decide upfront whether to allow them (and apply a different auth mode like device-cert + bearer token) or to reject them outright. - Service-to-service WebSockets (e.g. Kafka WebSocket bridge,
Apache Pulsar) inside a private VPC may legitimately use
ws://with mTLS handled at the network layer.
Context (for humans)
WebSockets are the long-lived sibling of HTTP. Most of the controls HTTP gets for free (CORS, CSP, per-request auth) do not apply out-of-the-box, and frameworks that wrap WebSockets behind a higher- level API (Socket.IO, SignalR, Phoenix Channels) hide the upgrade mechanism enough that developers forget to harden it.
The two recurring incident classes are:
- Cross-Site WebSocket Hijacking — missing Origin check + cookie auth → attacker.com opens a WS with the user's cookies and reads their stream.
- Resource exhaustion — no size / rate / connection limit + a chatty protocol → trivial DoS.
Both are simple fixes, but both are easy to forget when generating a quick chat / collab feature. This skill mirrors the OWASP cheat sheet plus the operational must-haves (heartbeats, backoff).
Verify & lock (triaging a finding)
A scanner/review hit is a candidate, not a confirmed bug. Confirm it, fix it, then lock it so it can't come back.
- Confirm it's real (probe the handshake and the first messages). Replay the
WebSocket upgrade with a hostile context: send a foreign
Origin: https://attacker.exampleheader (and the victim's session cookie if cookie auth is in play), and separately try the upgrade with no token / an expired token. Real if the socket reaches OPEN and starts streaming data — CSWSH is confirmed when a cross-origin page can establish the connection and read the user's stream; broken handshake-auth is confirmed when an unauthenticated upgrade succeeds and only a laterauth/subscribemessage gates access. For resource limits, hold one socket open and fire an oversized frame and a rapid message burst — real if neither is rejected (no size cap, no per-connection rate limit). A false positive: the upgrade is refused (4xx / close) for the disallowed Origin and the missing/expired token, and oversized or floods are dropped. - Fix, then lock with a regression test (unit or integration — dev's call):
assert a handshake carrying a disallowed
Originis rejected and one without a valid token is rejected, while an allowlisted Origin + valid token connects; assert a frame over the max size and a burst past the rate limit are closed/dropped, and that each privileged message is re-authorized (a revoked/role-changed user is denied) — plus a benign case: legitimate origin, valid token, normal-sized message at normal cadence still succeeds. Commit it to CI so the guard can't be silently dropped in a later refactor.
References
rules/websocket_hardening.json- OWASP WebSocket Security Cheat Sheet.
- CWE-1385.
- Cross-Site WebSocket Hijacking explainer.
- RFC 6455.
What ships with it: 2 files
8.9 KB alongside SKILL.md
rules/
- websocket_hardening.json3.4 KB
tests/
- corpus.json5.5 KB