Protection - how not to become a victim
Protection is not a“do it” list. This is an understanding of principles.
It's not a hole in theory, it's a door to reality
WebSocket is anexcellent technology that erases the boundaries between the clientand the server. But it is this “erasherness” that is its maindanger.
Let's dwell on the word "wash". It's nota beautiful metaphor from the booklet. This is a technical fact thatchanges everything. In the HTTP/1.1 model, which we all (do not)love, the boundaries were clear as lines in the console. There isRequest. From the client. With headlines, body, method. And there isResponse. From the server. The end of history. Each such exchange isan atomic event. It can be logged, it can be assigned a unique ID, itcan be interrupted without touching others. The Firewalls and WAFSare sharpened for this discreteness. They know how to “understand”where the beginning is and where the end is. They live in a statelessworld, even on top of keep-alive.
WebSocket this modeldoesn’t just break this model. He takes her out of brackets. Afterthe handshake, which is only formally an HTTP package, is installedconstant, bidirectional channel. It's no longer an exchange ofpackages. It's a tunnel. A network socket raised to the applicationlevel and wrapped in the browser API.
Here’s what“eratin eras” really means:
It creates theillusion of “its” channel, trusting, almost internal.
Thisillusion is the most insidious enemy. After installing a connection,especially inside one domain (the same Origin), the developer has afeeling that he communicates not with an external, potentiallyhostile world, but with his trusted client. In his head, it lookslike a straight line between two modules of the same system. Almostlike IPC (Inter-Process Communication),but through the network.
How does this manifest itself inthe code? Let's look at the real, sewn into meat, antipatterns:
And this illusionfits both developers who forget about security, and administratorswho copy HTTP tips for the WS.
Developer who forgets aboutsecurity, - it's not necessarily a lazy jung. This is a person whofocuses on functionality. He reads the tutorial: "How to make achat on Socket.io in 10 minutes.” In the tutorial after the connection is installedimmediately
socket.on('chat message',...). Not a word about socket.on('connect',() => { /* а тут проверим, кто это? */ }).The mental model is: “If you connected, it means his own.” Itforgets not because it is stupid, but because the abstractionprovided by the library (socket is like a user), directly contributesto this forgetfulness. The library takes over the transport, whichmeans, in its opinion, should take safety. But no.
Administratorthat copies HTTP configi, - this is a system hero who keeps tenservices. He sees in the configence of the application line:websocket: true. His job is to skiptraffic. It opens the Nginx config for this application, seeslocation / c proxy_pass.He copies it, creates location /ws/ or/socket.io/, adds magic lines
proxy_http_version 1.1;, proxy_set_headerUpgrade $http_upgrade;, proxy_set_header Connection "upgrade";.
He's good. He did what he asked for. The traffic's gone.
Butwhat he not Copied because it didn't happen in HTTP?
It's not his fault.It thinks in terms of HTTP proxy. WebSocket for him is simply “onemore protocol to be missed.” It does not go further, because theapp documentation rarely contains the section "Requirements forproxying WebSocket for operation in the production". The result:the app seems to work, but the connections themselves break in 60seconds, or the balancer falls under load due to millions of“hanging” WS connections, or, worse, the backend does not receivecookies and counts all anonymous users, opening the door for anyoneelse.
We went from broken frames to CSRF, from parsing toinjections. We saw as quietly, without noise, listen to privatemessages or replace transactions.
Let's make it not alist, but a narrative. The story of one possible attack that usesthis entire chain of failures.
Act 1: Reconnaissance. Theattacker (say, we) looks at the target-trade.com application. In thesource code of the page we find:
const ws= new WebSocket('wss://api.target-trade.com/ws/v1');. Allright, there's an endpoint.
Act 2: An analysis of a handshake.Our ws-harness Checking the Origin. Sendwith Origin: httrs://evil.com. The connection is established. Thefirst victory: The server does not check the Origin. This means thatany site that our victim (trader) will be able to open a hidden WSconnection to the trading server from his face.
Act 3: Analysisof the protocol. Through DevTools, we look at the traffic of ourlegitimate user. See the sequence: after connecting the client sends{"auth": "BearereyJhbGciOiJIUzI1NiIs..."} - JWT tone. Subscriptions arefurther: {"sub": "orders:user_123"},{"sub": "quotes:AAPL"}.
Then theteams: {"order": "buy","qty": 10, "symbol": "AAPL"}.The protocol is clear.
Act 4: Search for a lain. We note thatjust the user ID is used to subscribe to the gallery:user_123channel. We try to subscribe from your authorized user toorders:user_456. The server allows and begins to send other people'sorders. Second victory: Lack of channel isolation.
Act 5:Creating an Operational Chain. We don't know the victim's JWT tone.But we don't need that. We use the first vulnerability (OriginHijacking + CSRF). Write a page on evil.com:
Act 6: Attack. Slipthe victim this page (phishing, advertising, XSS on another site).The victim visits her. In the background, without any visibleactions, its browser opens the WS to the trading server, using itsown session cookies (which the browser will set up automatically).Our script gets full access to its stream of orders and real-timebalance. Silence. No pop-ups or suspicious passages. The WS protocoldoes not have a built-in mechanism to notify the user about newconnections. This is not OAuth, where they are asked to confirmaccess.
And we can go further. If the protocol has a"cancelOrder" command and it does not check that thecanceled order belongs to the current user (and trusts the channelfrom which the message came), then we can send {"cancelOrder":"order_id_789"} and cancel someone else'sapplication, causing financial damage. It's not just aneavesdropping, it's no longer Substitution of Transactions.
Thisis not a script from a fantastic movie. This is a consistentapplication of the found configuration vulnerabilities and logic.
Itis important to remember: every new technology in the stack is notonly fiction, but also new risks. Especially when this technologybreaks the old paradigms.
An important clarification mustbe made here. The risks arise not because of the novelty, but becauseof Template Break Break. The human brain, and in particular the brainof the developer and admin, is looking for analogies. “Oh,WebSocket is like a long HTTP request” or “It’s like a socket,but in a browser.” These analogies are time-action mines.
When technologybreaks the old paradigm, all years-round practices, tutorials andconfigies become partially or completely wrong. The period comesChaos of Implementation, when vulnerabilities are brewed not becausethe technology is bad, but because it is applied to old,inappropriate solutions. This was the case with AJAX (hello, CSRF),with SPA (roating, authentication), with microservices (security ofinterservice communication). Now with WebSocket and its brethren(SSE, WebRTC data channels).
Now you have a map of thisterritory. Tools you can finalize for your needs. And, moreimportantly, understanding the logic of attacks.
The mapis not just a list of “what to check” items. This understandingLayers:
Tools (ws-harness,ws_fuzzer, scripts for mitmproxy)is a picket and a shovel for the study of this map. Their value isnot to start and get red or green light. Their value is to Ask aquestion to the system and understand by her answer (or lack ofanswer)How it works inside. Silence in response to the broken frame?So the parser fell. Is the connection accepted with any Origin? Sothe door is open to hijacking. Message {"cmd":"debug"} Reverse the spectrum? So there's a hiddenundocumented protocol.
Understanding the Logic of Attacks- that's the main thing. This is the transition from tactics (“checkOrigin”) to strategy (“understand how an attacker can turn atrust channel into a cover-by-wheel-control tool”). This allows youto think like an attacker, even when a specific script for an attackdoesn’t exist yet. You begin to see Potential to attack in thearchitecture of the solution itself.
Use it to protectyour systems. And if you're doing a pentest, look for those holes.They are. Trust me.
There is no place for indulgence. Thephrase “they are” is not a figure of speech. It is a statisticaland empirical given. According to security reports (for example, fromBugcrowd or HackerOne), vulnerabilities associated with insufficientOrigin check and improper authorization in WebSocket are in the topFinds on modern web applications. They are found not because they arecomplex, but because their not searched purposefully. Standardpentest techniques are often limited to a superficial check: “Isthere a WSS? Okay, let's move on."
The protection ofyour systems begins with Recognition of the. With the fact that you,as an architect or developer, you are reading: “This beautiful,living channel is not a pipe between two trusted rooms. It's a tunneldug from a hostile Internet right into the heart of my businesslogic. And at the entrance to this tunnel should be not just abarrier (handshake), but a full-scale checkpoint with a lie detector,x-ray and armed guards.
And if you're a pentruster, lookfor these holes Persistently. Do not pass the WebSockets tab inDevTools. Use tools for phase-aging not only HTTP, but also WSprotocol. Introduce Origin checks and authorization in the list ofmandatory tests. Your task is not to go through the tick point"checked WebSocket", and Consciously Test All FourLayerswhich we described. You know the logic. You know where theweakness is hidden. Look for her.
Protection is not a“do it” list. This is an understanding of principles.
- Always use WSS (TLS). None ws://in the production. The right ciphers are comfortable, disable old protocols.
- A hard Origin test. On the server. Compare the complete match to the white list. Do not trust the header if he came from an unverified client (but in the browser it is reliable).
- Authentication and authorization before the opening of the WS-connection. The ideal option: a session cookie, which is automatically sent by the browser, is checked on a backend when riding hands, and only then the connection object is created, tied to the user object. Alternative - disposable tokens in query stringgenerated by the page after reviewing the session.
- Validation of input data. All messages that have come via WS should be checked as carefully as the HTTP request settings. Schemes, types, ranges.
- Isolation. Check the user's rights for each action (subscription to the channel, sending a message to the channel). Channel identifiers should be crypto-resistant (UUID, not incremental ID) or checked through access.
- Limits. Restrict:
- Size of the frame.
- Frequency of messages from one connection.
- Number of connections from one IP/user.
- The total number of connections on the server.
- Size of the frame.
- Embarrassion of infrastructure. Increase timeouts for WSS in balancers. Make sure that all the necessary headlines (especially cookies) are predicted. Consider using specialized proxies for WS (e.g., a specialized Socket.io server with a clustering adapter).
- Ping-pong. Implement the mechanism ping/pongto detect "humming" connections and reset the unresponsive customers.
- Login and monitoring. Login to install and break the connection (with the user identifier). Monitor anomalous activity: bursts of messages, attempts to connect with left-wing Origin.
It's not a hole in theory, it's a door to reality
WebSocket is anexcellent technology that erases the boundaries between the clientand the server. But it is this “erasherness” that is its maindanger.
Let's dwell on the word "wash". It's nota beautiful metaphor from the booklet. This is a technical fact thatchanges everything. In the HTTP/1.1 model, which we all (do not)love, the boundaries were clear as lines in the console. There isRequest. From the client. With headlines, body, method. And there isResponse. From the server. The end of history. Each such exchange isan atomic event. It can be logged, it can be assigned a unique ID, itcan be interrupted without touching others. The Firewalls and WAFSare sharpened for this discreteness. They know how to “understand”where the beginning is and where the end is. They live in a statelessworld, even on top of keep-alive.
WebSocket this modeldoesn’t just break this model. He takes her out of brackets. Afterthe handshake, which is only formally an HTTP package, is installedconstant, bidirectional channel. It's no longer an exchange ofpackages. It's a tunnel. A network socket raised to the applicationlevel and wrapped in the browser API.
Here’s what“eratin eras” really means:
- The boundary between the “request” and “session” is erased. All data exchange now takes place within the framework of one long-lived session-scope. WAF, which is accustomed to inspect each individual HTTP request, often simply passes the entire data stream after the 101st code, trusting that once the handshake has passed, then further - valid WS-traffic. But inside this traffic can flow anything: both SQL injections, and teams for degeneration, and attempts to subscribe to other people’s channels. Firewall is blind because his HTTP parser stops on the first double line transfer after 101 Switching Protocols.
- The boundary between the client and the server is erased as the initiator. In HTTP, a server is always the answering party. In WebSocket, the server can send data whenever you want. This breaks the mental security models built around the processing of incoming requests. Now “inbound” can come at any time, and the logic of authorization should be ready for this. Weak, disposable checking at the connecting no longer rolls.
- The boundary between the transport and the application protocol is blurred. The developer thinks, “I use WebSocket.” In fact, it uses two layers: 1) the transport protocol WS (frames, ping/pong), and 2) its own, custom application protocol that runs on top of these frames (JSON-RPC, STOMP, just a stream of JSON objects). Safety of the first layer (validity of frames, masks, Origin) - the responsibility of the library. Safety of the second layer (validation of JSON-fields, checking the rights to the action {"action": "deleteUser"}) - completely on the developer. And here there is a fatal substitution: protecting the transport (say, putting the WSS), he believes that he also protected the application. It's like putting an armored door to the house with the walls of cardboard.
It creates theillusion of “its” channel, trusting, almost internal.
Thisillusion is the most insidious enemy. After installing a connection,especially inside one domain (the same Origin), the developer has afeeling that he communicates not with an external, potentiallyhostile world, but with his trusted client. In his head, it lookslike a straight line between two modules of the same system. Almostlike IPC (Inter-Process Communication),but through the network.
How does this manifest itself inthe code? Let's look at the real, sewn into meat, antipatterns:
- Passing authorization for “official” messages. An example from the life of microservice architecture:
if (message.type === 'healthcheck') { ws.send('ok'); return; }. That's great, isn't it? Quick internal ping. And if the attacker just begins to send {"type": "healthcheck"}Every millisecond? This is a DoS that bypasses all heavy middleware authentication.
- Confidence in the data of the “internal” format. “We’re inside our protocol, everything is structured.” And the handler comes JSON: {"cmd": "update", "settings": {"theme": "dark"}}. What if you send
{"cmd": "update", "settings": {"theme": "dark"}, "__proto__": {"isAdmin": true}}? Or if the parser on Node.js uses JSON.parse without checks, and then somewhere deep in logic there is a measure of objects Object.assign(target, receivedData.settings). The illusion of the “internal” format lulls vigilance to the classic attacks on serialization and prototypes.
- No rate limiting and monitoring. On HTTP route /api/loginusually put limits of 10 queries per minute with IP. And on the WS-connection, which is once installed and can send 1000 messages per second - put? Most often not. Because it’s “alive connection,” “teat has to be fast.” The illusion of a trust channel turns off the idea of abuse.
And this illusionfits both developers who forget about security, and administratorswho copy HTTP tips for the WS.
Developer who forgets aboutsecurity, - it's not necessarily a lazy jung. This is a person whofocuses on functionality. He reads the tutorial: "How to make achat on Socket.io in 10 minutes.” In the tutorial after the connection is installedimmediately
socket.on('chat message',...). Not a word about socket.on('connect',() => { /* а тут проверим, кто это? */ }).The mental model is: “If you connected, it means his own.” Itforgets not because it is stupid, but because the abstractionprovided by the library (socket is like a user), directly contributesto this forgetfulness. The library takes over the transport, whichmeans, in its opinion, should take safety. But no.
Administratorthat copies HTTP configi, - this is a system hero who keeps tenservices. He sees in the configence of the application line:websocket: true. His job is to skiptraffic. It opens the Nginx config for this application, seeslocation / c proxy_pass.He copies it, creates location /ws/ or/socket.io/, adds magic lines
proxy_http_version 1.1;, proxy_set_headerUpgrade $http_upgrade;, proxy_set_header Connection "upgrade";.
He's good. He did what he asked for. The traffic's gone.
Butwhat he not Copied because it didn't happen in HTTP?
- proxy_read_timeout 3600s;(So that the connection does not break through the taymoute of inactivity).
- proxy_set_header Cookie $http_cookie;(If this is not inheld by default, and the backend checks cookies).
- Buffer Settings for Big Frames (proxy_buffer_size, proxy_buffers)
- Speed limits (limit_rate after 10m), if they are needed.
- Perhaps a separate limit_conn_zone to control the number of simultaneous WS-connections with one IP.
It's not his fault.It thinks in terms of HTTP proxy. WebSocket for him is simply “onemore protocol to be missed.” It does not go further, because theapp documentation rarely contains the section "Requirements forproxying WebSocket for operation in the production". The result:the app seems to work, but the connections themselves break in 60seconds, or the balancer falls under load due to millions of“hanging” WS connections, or, worse, the backend does not receivecookies and counts all anonymous users, opening the door for anyoneelse.
We went from broken frames to CSRF, from parsing toinjections. We saw as quietly, without noise, listen to privatemessages or replace transactions.
Let's make it not alist, but a narrative. The story of one possible attack that usesthis entire chain of failures.
Act 1: Reconnaissance. Theattacker (say, we) looks at the target-trade.com application. In thesource code of the page we find:
const ws= new WebSocket('wss://api.target-trade.com/ws/v1');. Allright, there's an endpoint.
Act 2: An analysis of a handshake.Our ws-harness Checking the Origin. Sendwith Origin: httrs://evil.com. The connection is established. Thefirst victory: The server does not check the Origin. This means thatany site that our victim (trader) will be able to open a hidden WSconnection to the trading server from his face.
Act 3: Analysisof the protocol. Through DevTools, we look at the traffic of ourlegitimate user. See the sequence: after connecting the client sends{"auth": "BearereyJhbGciOiJIUzI1NiIs..."} - JWT tone. Subscriptions arefurther: {"sub": "orders:user_123"},{"sub": "quotes:AAPL"}.
Then theteams: {"order": "buy","qty": 10, "symbol": "AAPL"}.The protocol is clear.
Act 4: Search for a lain. We note thatjust the user ID is used to subscribe to the gallery:user_123channel. We try to subscribe from your authorized user toorders:user_456. The server allows and begins to send other people'sorders. Second victory: Lack of channel isolation.
Act 5:Creating an Operational Chain. We don't know the victim's JWT tone.But we don't need that. We use the first vulnerability (OriginHijacking + CSRF). Write a page on evil.com:
const targetWs = newWebSocket(‘wss://api.target-trade.com/ws/v1’);
targetWs.onopen = ()=> {
// 1. Subscribe toall of the victim's private channels (we know their ID from theirpublic profile).
targetWs.send(JSON.stringify({“sub”:“orders:user_456”}));
targetWs.send(JSON.stringify({“sub”:“balance:user_456”}));
// 2. Wait for thedata to arrive, and quietly send it to our server.
};
// ... code forsending data
Act 6: Attack. Slipthe victim this page (phishing, advertising, XSS on another site).The victim visits her. In the background, without any visibleactions, its browser opens the WS to the trading server, using itsown session cookies (which the browser will set up automatically).Our script gets full access to its stream of orders and real-timebalance. Silence. No pop-ups or suspicious passages. The WS protocoldoes not have a built-in mechanism to notify the user about newconnections. This is not OAuth, where they are asked to confirmaccess.
And we can go further. If the protocol has a"cancelOrder" command and it does not check that thecanceled order belongs to the current user (and trusts the channelfrom which the message came), then we can send {"cancelOrder":"order_id_789"} and cancel someone else'sapplication, causing financial damage. It's not just aneavesdropping, it's no longer Substitution of Transactions.
Thisis not a script from a fantastic movie. This is a consistentapplication of the found configuration vulnerabilities and logic.
Itis important to remember: every new technology in the stack is notonly fiction, but also new risks. Especially when this technologybreaks the old paradigms.
An important clarification mustbe made here. The risks arise not because of the novelty, but becauseof Template Break Break. The human brain, and in particular the brainof the developer and admin, is looking for analogies. “Oh,WebSocket is like a long HTTP request” or “It’s like a socket,but in a browser.” These analogies are time-action mines.
- HTTP paradigm: “request-response”. Protect every request. The session is a cookie. Security is the validation of input parameters and cookie check.
- WebSocket Paradigm: “Continuous connection-session”. We protect the connection and each message inside it. A session is a WebSocketClient object in a server memory linked to a user. Security is a) checking at the connector (Origin, authentication), b) stateful-validation of each incoming frame/message, c) control the state of this object (whether the limits have exhausted, whether it is not trying to perform incompatible actions).
When technologybreaks the old paradigm, all years-round practices, tutorials andconfigies become partially or completely wrong. The period comesChaos of Implementation, when vulnerabilities are brewed not becausethe technology is bad, but because it is applied to old,inappropriate solutions. This was the case with AJAX (hello, CSRF),with SPA (roating, authentication), with microservices (security ofinterservice communication). Now with WebSocket and its brethren(SSE, WebRTC data channels).
Now you have a map of thisterritory. Tools you can finalize for your needs. And, moreimportantly, understanding the logic of attacks.
The mapis not just a list of “what to check” items. This understandingLayers:
- Layer of transport (frames): Where they hit the parser, where on timeauts, where on masks.
- Handshake Layer (HTTP-upgrade): Where the Origin is counterfeited, where they play with subprotocols, where authentication tokens are intercepted/guide.
- Session layer (connection): How the server stores the state, how it binds it to the user, how it isolates, how it controls (limits).
- Application Protocol layer (messages): Where injections, where there is insufficient authorization, where race conditions, where are logical errors.
Tools (ws-harness,ws_fuzzer, scripts for mitmproxy)is a picket and a shovel for the study of this map. Their value isnot to start and get red or green light. Their value is to Ask aquestion to the system and understand by her answer (or lack ofanswer)How it works inside. Silence in response to the broken frame?So the parser fell. Is the connection accepted with any Origin? Sothe door is open to hijacking. Message {"cmd":"debug"} Reverse the spectrum? So there's a hiddenundocumented protocol.
Understanding the Logic of Attacks- that's the main thing. This is the transition from tactics (“checkOrigin”) to strategy (“understand how an attacker can turn atrust channel into a cover-by-wheel-control tool”). This allows youto think like an attacker, even when a specific script for an attackdoesn’t exist yet. You begin to see Potential to attack in thearchitecture of the solution itself.
Use it to protectyour systems. And if you're doing a pentest, look for those holes.They are. Trust me.
There is no place for indulgence. Thephrase “they are” is not a figure of speech. It is a statisticaland empirical given. According to security reports (for example, fromBugcrowd or HackerOne), vulnerabilities associated with insufficientOrigin check and improper authorization in WebSocket are in the topFinds on modern web applications. They are found not because they arecomplex, but because their not searched purposefully. Standardpentest techniques are often limited to a superficial check: “Isthere a WSS? Okay, let's move on."
The protection ofyour systems begins with Recognition of the. With the fact that you,as an architect or developer, you are reading: “This beautiful,living channel is not a pipe between two trusted rooms. It's a tunneldug from a hostile Internet right into the heart of my businesslogic. And at the entrance to this tunnel should be not just abarrier (handshake), but a full-scale checkpoint with a lie detector,x-ray and armed guards.
And if you're a pentruster, lookfor these holes Persistently. Do not pass the WebSockets tab inDevTools. Use tools for phase-aging not only HTTP, but also WSprotocol. Introduce Origin checks and authorization in the list ofmandatory tests. Your task is not to go through the tick point"checked WebSocket", and Consciously Test All FourLayerswhich we described. You know the logic. You know where theweakness is hidden. Look for her.