THE SECURITY PS BLOG:
Observations and insights from the Security PS Team.

Websockets: Trust No One, Not Even Your Own Application

Websockets were designed to solve the need for fast and bi-directional communication in client/server applications. They are an ideal choice for chat programs, collaborative document editors, and browser-based games. Overall, they offer many advantages over traditional HTTP in terms of speed and ease of use. However, like any other technology, there are fundamental security issues that need to be considered when using websockets. In this article, we will review several security concerns that are often overlooked when implementing websockets.

Input

To ease into the topic, let’s begin with a well-known security concern and see how it applies to websockets. As many developers know, applications should always treat user input as possibly malicious. One example of attack that can occur if this assumption is not enforced is a cross-site scripting (XSS) attack. In a XSS attack, an attacker places malicious JavaScript code within a parameter. This malicious code is eventually viewed and executed by a victim’s browser. Many developers are aware of XSS attacks against typical form elements (such as a name or address field) and most web frameworks include libraries to protect against these attacks automatically. However, these libraries often do not automatically protect against malicious code in websocket messages. If this avenue is not secured, attackers can use a man-in-the-middle proxy, such as Burp Suite or OWASP Zap, to modify websocket traffic and conduct XSS attacks.

For example, we recently analyzed a chat application that used websockets to send and receive chat messages. When a user sent a chat message, the following websocket message would be sent from the client (browser) to the server:
{“user”:”user1”, "text":"hello", "type":"message", "id":1562869260905}

By intercepting their own requests to the server, an attacker could modify the message sent to the server to include JavaScript code. This would allow them to bypass client-side sanitation controls. This attack would then execute identically to a traditional XSS attack. An example of this attack is shown below:
{“user”:”user1”, "text":"Nothing malicious here<script>alert(1)</script>","type":"message","id":1562869260905}

When viewed by another user, the JavaScript code executed on their machine:

While this particular application was vulnerable to XSS attacks, the same root cause of trusting websocket input can be used to exploit other areas, such as authorization controls. Like regular input, every application should assume all websocket traffic from users is malicious.

Server Output

In contrast to dealing with user input, many applications are developed to intrinsically trust information that originates from server responses. However, developers should be careful when it comes to trusting websocket responses from the server, as it is possible for attackers to modify these responses before the client receives them. In addition, these modified responses will appear legitimate to the client. If the application trusts these responses, it can lead to serious vulnerabilities.

One browser-based game we analyzed controlled player movement through a combination of websocket requests and responses. When moving in the game, players would send a websocket message to the server that contained their new position:
{“player”:140234, “newposition_x”: 155, “newposition_y”: 20}

The server would respond back to the player with a confirmation message that contained their new position. This response would look identical to the request above.

Attempting to send a request from the client with modified newposition_x or newposition_y values would cause the entire request to be ignored by the server. However, by intercepting the response from the server and modifying these positions before they reach the browser, the game would assume that the player desynced from the server. The game would then invoke a special function to fix this apparent desync. This desync routine would place the player wherever was specified in the modified server response. Since the traffic from the client and server appeared legitimate, this vulnerability allowed attackers to teleport around the game world.

Message Forgery

Both the vulnerabilities discussed above occurred due to attackers modifying messages. However, attackers are not limited to only modifying messages - they can also create them. This can cause two potential issues for applications:
  1. Denial of Service (DoS) attacks
  2. Authorization issues
Applications will often have certain operations that take a long time to finish. An example may be parsing a large file for certain information or querying a database. If these large operations are not properly restricted and queued, it may be possible for an attacker to create thousands of requests and overload the application. Similarly, if the application allows for file uploads, an attacker could upload enormous amounts of data. Neither of these vulnerabilities are novel and often applications will have mechanisms in place to handle them. However, many developers treat websocket traffic differently than regular HTTP requests and do not apply these security mechanisms consistently to websockets use. When using websockets, it’s important to remember that attackers can easily create a large volume of requests to any endpoint and that some sort of protection needs to be applied to prevent denial of service attacks.

In a similar manner, if applications are designed to assume that websocket traffic cannot be created by attackers, they may allow access to unauthorized features. For example, if an application assumes that only administrators can create certain messages, it may not properly verify the requesting user. This would allow an attacker to perform unauthorized actions by only sending websocket requests.

Conclusion

So, while websockets offer many advantages over traditional HTTP, they require the same security considerations that must be applied to all traditional traffic. However, because many security libraries do not account for them automatically, developers must ensure that common controls are also applied to any websocket functionality. In particular, developers should take the following steps to secure their websocket implementations:
  1. Always assume user input is malicious and protect against it, even in websocket messages to the server
  2. Clients that use websockets shouldn’t trust server responses to execute sensitive functionality and servers receiving websocket messages should enforce controls
  3. Always verify that websocket traffic is validated on the server-side for authorization controls, rate limiting, business logic, and other traditional server-side responsibilities
In the next post in this series, we will cover some additional unique vulnerabilities found specifically within websocket implementations.
    Blogger Comment
    Facebook Comment

0 comments:

Post a Comment