A WebSocket turns one HTTP request into a long-lived, two-way channel, and most of the controls teams rely on for HTTP stop at the handshake. After the 101 response there are no more request headers, no per-message cookies, no CSRF tokens and often no WAF inspection, so authentication, authorisation, input validation and rate limiting have to be rebuilt inside the message layer. Chat, trading, collaboration, dashboards, AI streaming interfaces and device management consoles all use WebSockets, and admin interfaces built on them have been exploited in the wild. This guide covers the handshake, cross-site WebSocket hijacking, authentication and session handling, per-message authorisation and validation, resource limits, gateway configuration, testing and monitoring. It is for application security engineers, developers and platform teams who build or review real-time features.
Key takeaways
- Validate Origin on every handshake. Browsers attach cookies to cross-site WebSocket handshakes, so without an Origin allowlist any website a user visits can open an authenticated socket in their name (cross-site WebSocket hijacking). OWASP ASVS 5.0 V4.4.2 requires it.
- The handshake is not authorisation. RFC 6455 does not prescribe client authentication, and an open connection is not blanket permission: check authorisation on every message that reads data or performs an action.
- Use wss:// only. ASVS V4.4.1 requires WebSocket over TLS for all connections; unencrypted ws:// exposes messages and session tokens to any network observer.
- Keep tokens out of URLs. Query string tokens end up in proxy and access logs. Prefer the existing session cookie with Origin checks, or a short-lived ticket or token sent in the first message.
- Bound everything. Message size, message rate, connections per user, idle time and session lifetime all need explicit limits, because a single socket can otherwise hold resources indefinitely.
How the WebSocket protocol works, and what changes for security
RFC 6455 defines the protocol. The client sends an HTTP/1.1 GET with Upgrade: websocket, Connection: Upgrade, a random Sec-WebSocket-Key and Sec-WebSocket-Version: 13, and optionally Origin, Sec-WebSocket-Protocol (subprotocols) and Sec-WebSocket-Extensions. The server answers 101 Switching Protocols with a Sec-WebSocket-Accept value derived from the key and a fixed GUID. Any other status means the handshake failed. After that, both sides exchange frames over the same TCP connection: ws:// defaults to port 80 and wss:// to port 443.
- Sec-WebSocket-Key is not a security token: it only proves the server understood the WebSocket handshake. It does nothing for authentication or CSRF protection.
- Masking protects intermediaries, not data: clients must mask every frame they send and servers must not mask theirs. Masking exists to stop crafted traffic confusing proxies and poisoning caches; it is not encryption.
- Origin is advisory for non-browsers: browsers set Origin so servers can reject untrusted sites, typically with 403. Scripts and native clients can send any Origin they like, so Origin checks defend users' browsers, not the server against direct attackers.
- Close codes carry policy: RFC 6455 section 7.4.1 defines 1008 (policy violation), 1009 (message too big to process) and 1011 (unexpected server condition), useful for consistent rejection and for monitoring.
- HTTP/2 and HTTP/3: RFC 8441 runs WebSockets over a single HTTP/2 stream using extended CONNECT with a
:protocolpseudo-header ofwebsocket, enabled by theSETTINGS_ENABLE_CONNECT_PROTOCOLsetting. Sec-WebSocket-Key and Accept are not processed, but Origin and most of RFC 6455's security considerations still apply. Check that gateways enforce the same policy on both paths.
Cross-site WebSocket hijacking and Origin validation
Cross-site WebSocket hijacking (CSWSH) is CSRF against the handshake. PortSwigger describes the conditions: the handshake relies solely on cookies for session handling and contains no CSRF token or other unpredictable value. A malicious page opens a socket to the target, the browser attaches the victim's cookies, and the server treats the connection as the victim. Unlike classic CSRF, the attacker gets two-way interaction: they can send messages as the victim and read whatever the server sends back. See the CSRF Prevention Guide.
| Defence | How | Notes |
|---|---|---|
| Origin allowlist | Compare the full Origin value (scheme, host, port) against an exact list during the handshake; reject with 403 | No wildcards or substring matches; evil-example.com must not match example.com |
| CSRF token in handshake | Include the application's anti-CSRF token as a handshake parameter or in the first message | Reuses existing CSRF design |
| SameSite cookies | Session cookies set SameSite=Lax or Strict | Defence in depth; does not cover same-site subdomains |
| Token-based authentication | Authenticate with a token the browser does not send automatically | Removes the ambient credential CSWSH depends on |
The OWASP WebSocket Security Cheat Sheet notes that with the Node.js ws library the Origin check belongs in the HTTP server's upgrade event before handleUpgrade is called. In frameworks such as Spring, Socket.IO or ASP.NET Core, confirm where allowed origins are configured and that the default is not "allow all". Origin checks and CORS are separate mechanisms: a correct CORS policy does not protect a WebSocket endpoint. See the CORS Misconfiguration Guide.
Authentication and session handling
RFC 6455 leaves client authentication to the application, which can use any mechanism an HTTP server can use, such as cookies, HTTP authentication or TLS client certificates. ASVS 5.0 adds two requirements: if standard session management cannot be used, dedicated tokens must meet the session management requirements (V4.4.3), and dedicated WebSocket tokens must be obtained or validated through the already authenticated HTTPS session (V4.4.4).
| Approach | Strengths | Risks | Use when |
|---|---|---|---|
| Session cookie on handshake | No extra code; reuses existing session | CSWSH unless Origin is validated | Same-origin web apps with strict Origin checks |
| Short-lived ticket from authenticated HTTPS call | Single use, bound to the session, expires in seconds | Needs a ticket store | Browser apps that want token-based sockets |
| Token in first message | Keeps tokens out of URLs and logs | Server must accept only the auth message until validated | SPAs and mobile clients using OAuth access tokens |
| Token in query string | Simple | Leaks into access, proxy and analytics logs | Avoid |
| Mutual TLS | Strong client identity | Certificate lifecycle overhead | Service-to-service and device connections |
- Authenticate before any data flows: with first-message authentication, accept only the auth message until validation succeeds, send no protected data, and close the connection on failure or after a short timeout.
- Tie connections to sessions: keep a mapping from session to open connections so logout, password change, account disablement or token revocation closes every socket for that user. The cheat sheet suggests close code 1008 when a session expires.
- Re-validate long-lived connections: sockets can stay open for hours. Re-check the session or token periodically (the cheat sheet cites 30 minutes as common) and rotate tokens on long connections.
- Treat socket tokens as session credentials: exclude them from message logs. See the Token and Session Security Guide.
Per-message authorisation and input validation
Once connected, the WebSocket channel is an API with its own verbs, usually a type or action field in JSON, or a STOMP or Socket.IO event name. It needs the same controls as a REST API, applied to every message.
- Authorise every action: check the user's permission for the action and the object it names (room, document, account, device) on each message. Subscribing to another tenant's channel by changing an ID is the WebSocket form of broken object level authorisation. See the API Security Guide.
- Validate structure with a schema: define a JSON schema per message type, reject unknown types and fields, and enforce types, lengths and ranges. Parse with a JSON parser, never
eval(). ASVS V2.2.1 and V2.2.2 (positive validation at a trusted service layer) apply unchanged. - Encode output for its sink: chat messages rendered into a page are a classic stored or reflected XSS path. See the Cross-Site Scripting (XSS) Prevention Guide.
- Do not build commands or queries from messages: WebSocket injection reaches SQL, shell, template and deserialisation sinks exactly like HTTP input. Use parameterised queries and safe binary formats; verify uploaded file types by content, not declared type.
- Prevent replay where actions matter: add nonces or timestamps to state-changing messages and reject duplicates.
- Validate server-to-server routing: when a gateway routes sockets to back ends based on a path or subprotocol, never let client input choose an internal host or URL. That turns the gateway into an SSRF proxy. See the SSRF Prevention Guide.
Resource limits and denial of service
| Limit | Typical starting point | Why |
|---|---|---|
| Maximum message size | 64 KB or less (for example maxPayload in ws); close with 1009 | The protocol allows 63-bit payload lengths, far beyond any real need |
| Message rate per connection | Around 100 messages per minute for chat-like apps | Stops flooding and brute force over the socket |
| Connections per user and per IP | A small number per user; per-IP only for unauthenticated sockets | Stops connection exhaustion |
| Unauthenticated connection timeout | Seconds | Stops idle pre-auth sockets holding slots |
| Idle timeout and ping/pong heartbeat | Aligned with proxy read timeouts | Cleans up dead connections |
| Backpressure and send buffer cap | Per connection | Slow consumers cannot exhaust server memory |
| Compression | Disable permessage-deflate unless needed | Compression bombs, CPU cost, and CRIME/BREACH-style leaks when secrets and attacker input share a message |
The values above are the OWASP cheat sheet's starting points; tune them to real traffic. Rate limits on sockets follow the same design as HTTP limits: count per user and per action, not just per connection. See the Rate Limiting and API Abuse Guide.
Gateways, proxies and infrastructure
- Terminate TLS deliberately: wherever TLS ends (CDN, load balancer, gateway), re-encrypt to the back end if the network is untrusted, and keep TLS 1.2 or later. ASVS V4.4.1 applies to the client-facing connection.
- Pass upgrades explicitly: proxies must forward the
UpgradeandConnectionheaders and have read timeouts long enough for heartbeats. Configure only the routes that should upgrade, not the whole site. - Know what your WAF inspects: many WAFs inspect the handshake but not frames afterwards. If message inspection matters, use a gateway that parses frames, or validate in the application. See the Web Application Firewall Guide.
- Support only RFC 6455: remove fallbacks to pre-standard drafts (Hixie-76, hybi-00), which have known weaknesses.
- Keep libraries patched: WebSocket libraries and frameworks have had denial-of-service and code execution flaws. Track them in your SCA tooling.
- Do not expose admin consoles: management interfaces that use WebSockets should be reachable only from management networks.
- Local WebSocket servers: desktop agents, developer tools and browser companions often listen on
localhost. Any website the user visits can try to connect, so local servers need Origin checks and authentication as strict as internet-facing ones.
Lessons from incidents
CVE-2024-55591 in Fortinet FortiOS and FortiProxy, in CISA's Known Exploited Vulnerabilities catalog since 14 January 2025, is the clearest recent example of a WebSocket path bypassing authentication. Fortinet's advisory FG-IR-24-535 classifies it as CWE-288 (authentication bypass using an alternate path or channel): crafted requests to the Node.js websocket module let an unauthenticated remote attacker gain super-admin privileges. Affected versions included FortiOS 7.0.0 to 7.0.16, fixed in 7.0.17. Indicators included admin logins and new admin accounts created through the jsconsole interface with random usernames, followed by SSL VPN users and firewall policy changes. Fortinet's workarounds were to disable the HTTP/HTTPS administrative interface or restrict it to trusted management addresses with local-in policies. The lessons generalise: a WebSocket endpoint is an alternate entry point that must enforce the same authentication as the login page, and management interfaces should not be internet-facing. See the Web Application Attacks Guide.
Implementation and testing
- Find every socket: search client code for
ws://andwss://, review proxy configurations for upgrade routes, and list local WebSocket listeners on managed endpoints. - Document each endpoint: purpose, authentication method, message types, allowed origins, limits and owner.
- Fix the handshake first: enforce wss://, add an exact Origin allowlist in log-only mode for a week, review rejected origins, then enforce.
- Move authentication out of URLs: introduce tickets or first-message tokens alongside the old method, migrate clients, then remove query string support.
- Add per-message authorisation and schemas: roll out per message type, starting with state-changing and data-returning actions.
- Apply limits in count mode, then enforce: measure real message sizes and rates before setting caps; keep limits in configuration so they can be relaxed without redeploying.
- Test: OWASP WSTG-CLNT-10 covers identifying sockets, cross-origin connection tests for CSWSH, TLS checks, authentication, authorisation and replaying or fuzzing messages with an intercepting proxy such as ZAP. Include sockets in every penetration test scope. See the Penetration Testing Guide.
- Roll back safely: keep the previous handshake policy behind a feature flag during migration, and monitor close-code and connection-failure rates after each change.
Decision guidance
- WebSocket, server-sent events or polling: if the server only pushes updates, server-sent events run over ordinary HTTP and keep standard request controls. Choose WebSockets when you need low-latency two-way messaging.
- Cookie or token authentication: same-origin web apps can keep cookies with strict Origin checks and SameSite. Cross-origin clients, mobile apps and third-party integrations should use tokens in the first message or tickets.
- Raw WebSocket or a framework: frameworks such as STOMP or Socket.IO provide message routing and sometimes authorisation hooks, but their defaults differ. Review allowed origins, authentication interceptors and subscription authorisation explicitly.
- Small teams: use a managed gateway or framework defaults, enforce wss://, Origin and size limits, and authorise every message. Large organisations should publish a WebSocket standard and enforce it in a shared gateway.
Detection and measurement
Log connection open and close events with user identity, source IP, Origin and close code; authentication and authorisation failures; and rate-limit and validation violations. Do not log full message contents, tokens or personal data.
- Origin rejections: handshakes rejected for disallowed Origin, by origin. A spike can indicate a CSWSH attempt or a misconfigured new front end.
- Endpoint coverage: percentage of WebSocket endpoints using wss://, Origin allowlists and per-message authorisation (target 100%).
- Authorisation denials per message type: repeated denials from one user suggest object enumeration over the socket.
- Policy closes: 1008 and 1009 closes per hour, baselined so floods and oversized-message attacks stand out.
- Stale connections: sockets still open after their session was revoked (target zero).
Framework mapping
| Framework | Reference | Relevance |
|---|---|---|
| OWASP ASVS 5.0 | V4.4.1, V4.4.2, V4.4.3, V4.4.4; V2.2.1 | WSS, Origin checks, dedicated socket tokens, input validation |
| OWASP WSTG | WSTG-CLNT-10 | Testing WebSockets |
| NIST SP 800-53 Rev. 5 | SC-8, SC-23, AC-3, SI-10, SC-5 | Transmission protection, session authenticity, access enforcement, input validation, denial-of-service protection |
| NIST CSF 2.0 | PR.DS-02, PR.AA-05 | Data in transit; access permissions enforced |
| MITRE ATT&CK | T1190, T1185 | Exploit Public-Facing Application; Browser Session Hijacking |
| MITRE CAPEC | CAPEC-62, CAPEC-115 | Cross Site Request Forgery; Authentication Bypass |
Common failure patterns
- No Origin check with cookie authentication: the textbook CSWSH condition, giving any website read and write access to a logged-in user's socket.
- Authorising only at connect time: any authenticated user can subscribe to any channel or send admin actions because the server trusts the connection.
- Substring Origin matching:
endsWith("example.com")acceptsattackerexample.com. - Tokens in the URL: access tokens copied into load balancer and analytics logs where far more people can read them.
- Sockets outliving sessions: a revoked or logged-out user keeps receiving data for hours.
- Exposed WebSocket admin paths: as in CVE-2024-55591, an alternate channel to a management interface skips the authentication the main login enforces.
Practitioner checklist
- Inventory every WebSocket endpoint, including local listeners, and record an owner.
- Enforce wss:// for all connections and remove ws:// and pre-RFC 6455 fallbacks.
- Validate Origin against an exact allowlist on every handshake, including HTTP/2 extended CONNECT.
- Authenticate before sending data; never put tokens in the query string.
- Close sockets on logout, session expiry and token revocation, and re-validate long-lived connections.
- Authorise every message against the action and the object it names.
- Validate every message against a schema and encode output for its sink.
- Set limits for message size, message rate, connections per user and idle time.
- Keep WebSocket management interfaces off the internet and patch WebSocket libraries.
- Test sockets with WSTG-CLNT-10 in every penetration test.
Standards and references
- IETF: RFC 6455 The WebSocket Protocol
- IETF: RFC 8441 Bootstrapping WebSockets with HTTP/2
- OWASP: Application Security Verification Standard 5.0
- OWASP: WebSocket Security Cheat Sheet
- OWASP: Web Security Testing Guide, Testing WebSockets
- PortSwigger: Cross-site WebSocket hijacking
- Fortinet: PSIRT Advisory FG-IR-24-535
- CISA: Known Exploited Vulnerabilities Catalog
Related NHI Mgmt Group resources: API Security Guide · CSRF Prevention Guide · Session Hijacking and Cookie Theft Guide · Rate Limiting and API Abuse Guide · Web Application Attacks Guide