Tuesday, May 13, 2025

Securing WebSockets: Identifying and Remediating Common Vulnerabilities

Best practice fixes for GraphQL security risks
Navigation
    Run Express App Security Scan - It's Free. How Many Stars Does Your App Get?

    How WebSockets Work

    WebSockets provide a full-duplex communication protocol that enables real-time data exchange between a client and a server over a single, persistent connection. Unlike traditional HTTP requests, which require a separate connection for each interaction, WebSockets allow arbitrary data to be sent and received continuously, improving performance and responsiveness in web applications.

    WebSocket Protocol and Handshake Process

    The WebSocket protocol starts with an initial handshake, which is an HTTP request upgraded to a WebSocket connection. The process follows these steps:

    1. You initiate a connection to the WebSocket server using an HTTP request with the Upgrade and Connection headers.

    2. The server responds with an HTTP response containing the Upgrade: WebSocket header and a Sec-WebSocket-Accept value derived from the Sec-WebSocket-Key.

    3. Once the WebSocket handshake is complete, a communication channel is established, allowing both the client and the server to send messages asynchronously.

    A typical WebSocket handshake request looks like this:

    GET /chat HTTP/1.1
    Host: example.com
    Upgrade: websocket
    Connection: Upgrade
    Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
    Sec-WebSocket-Protocol: chat, superchat
    Origin: https://example.com

    The server returns a response like this:

    HTTP/1.1 101 Switching Protocols
    Upgrade: websocket
    Connection: Upgrade
    Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
    Sec-WebSocket-Protocol: chat

    Websocket vs HTTP

    Both WebSockets and HTTP are widely used communication protocols, but they serve different purposes. HTTP is a request-response protocol, while WebSockets enable full-duplex, real-time communication between a client and a server. Understanding their differences is important for optimizing web applications that require efficient data transmission and secure authentication.

    Feature HTTP WebSockets
    Communication Type Request-Response Full-Duplex
    Connection Handling New connection per request Persistent connection
    Performance Inefficient for real-time communication Efficient for continuous data streaming
    Security Concerns CSRF protection, SQL Injection, XSS Cross-Site WebSocket Hijacking, Man-in-the-Middle
    Supported by Proxy Servers Yes Limited support
    Data Exchange Requires repeated requests Uses WebSocket frames to transmit arbitrary data
    Use Cases Websites, API calls, static content Chat apps, gaming, real-time analytics

    WebSocket Security Considerations

    Secure WebSockets provide real-time bidirectional communication between a client and a server. Unlike traditional HTTP requests, WebSockets establish a persistent WebSocket connection using the WebSocket protocol. However, WebSocket security must be carefully managed to prevent vulnerabilities such as cross-site WebSocket hijacking, man-in-the-middle (MITM) attacks, and SQL injection.

    Authentication and Authorization

    • Authentication Mechanism

    A secure authentication process must be in place to verify the identity of clients connecting to a WebSocket server. Unlike traditional HTTP protocol, WebSockets do not natively support authentication. Therefore, authentication should be handled during the initial handshake using a token-based authentication mechanism such as JWT or OAuth.

    • Authorization Controls

    Even if authentication is implemented, weak authorization can allow other users to access sensitive data. Proper authorization information must be validated on every WebSocket request to prevent unauthorized access. Role-based access control (RBAC) and handle authorization mechanisms should be enforced at the ws server level.

    Secure WebSocket Handshake and Headers

    • Validating the Origin Header

    Unlike standard HTTP headers, WebSockets do not enforce the same origin policy by default. Attackers can exploit this by initiating unauthorized WebSocket connections from malicious domains. To prevent cross-site WebSocket hijacking, the origin header field must be validated during the WebSocket handshake:

    const allowedOrigins = ["https://trusted-domain.com"];
    wss.on("connection", (ws, req) => {
        if (!allowedOrigins.includes(req.headers.origin)) {
            ws.close();
            console.log("Connection rejected due to invalid origin");
        }
    });
    • Customizing WebSocket Headers

    Use customize WebSocket headers such as the Sec-WebSocket-Protocol Header to enforce security policies and restrict access. Additionally, ensure that shared authorization headers are not leaked to unauthorized clients.

    Data Encryption and Secure Communication

    • Using Encrypted WebSocket Connections

    All WebSocket traffic must be encrypted using WebSocket secure (wss://). This prevents man-in-the-middle attacks where attackers intercept WebSocket frames and modify arbitrary data in transit.

    Example of a Secure WebSocket Connection:

    const wss = new WebSocket.Server({ server, secure: true });
    wss.on("connection", function connection(ws) {
        ws.on("message", function incoming(message) {
            console.log("Received: %s", message);
        });
    });

    Preventing Injection Attacks

    • SQL Injection Prevention

    If WebSocket requests interact with a database connection, user inputs must be validated to prevent SQL injection. Instead of concatenating SQL queries, use prepared statements:

    const sql = "INSERT INTO users (username, email) VALUES (?, ?)";
    db.run(sql, [username, email], function (err) {
        if (err) throw err;
    });
    • Cross-Site Scripting (XSS) Mitigation

    If client-side code renders WebSocket traffic, it must be protected from cross-site scripting (XSS). Ensure that WebSocket application messages are sanitized before being displayed to prevent execution of malicious scripts.

    Common WebSocket Vulnerabilities

    If you’re using secure WebSockets in your application, you need to be aware of the security risks they pose. Common WebSocket vulnerabilities can be exploited if proper WebSocket security measures are not in place. Let’s go over the most critical ones and why you should care.

    Lack of Authentication and Authorization

    A WebSocket connection must be properly secured using a strong authentication mechanism and authorization controls. Without these, any client could establish a connection with the WebSocket server, allowing unauthorized users to send data or access restricted resources. Even if authentication is implemented, weak authorization information can enable other users to access sensitive data.

    For example, in a web application, an unauthorized user could intercept chat messages or control real-time financial transactions due to poor handle authorization mechanisms. To mitigate this WebSocket authentication problem, enforce secure authentication using token-based authorization and validate user privileges before processing WebSocket requests.

    Cross-Site WebSocket Hijacking (CSWSH)

    Attackers can exploit cross-site WebSocket hijacking by tricking users into visiting a malicious website that attempts to initiate a WebSocket connection to a trusted server. If the WebSocket application relies on shared authorization headers or cookies, an attacker can establish a WebSocket connection using the victim’s credentials.

    This allows the attacker to steal or manipulate arbitrary data from the application without the user’s knowledge. To prevent this, always validate the origin header field in WebSocket handshake requests and reject connections from untrusted sources.

    Example of a secure WebSocket handshake validation:

    const allowedOrigins = ["https://trusted-domain.com"];
    wsServer.on("connection", (ws, request) => {
        const origin = request.headers["origin"];
        if (!allowedOrigins.includes(origin)) {
            ws.close();
            console.log("Connection rejected due to invalid origin");
        }
    });

    Man-in-the-Middle (MITM) Attacks

    Without proper encryption, WebSocket traffic can be intercepted and modified by an attacker. A man-in-the-middle can eavesdrop on WebSocket frames, manipulate transactions, or inject malicious data into an active session.

    If your application transmits sensitive data over an unencrypted WebSocket protocol (ws:// instead of wss://), you're exposing authentication information, financial transactions, or private messages to attackers.

    Insecure Data Transmission

    Sending sensitive data over an unencrypted WebSocket connection exposes it to data loss, leaks, or unauthorized access. Confidential data like authentication tokens, passwords, and payment details must never be transmitted in plaintext. If an attacker intercepts this data, they can easily hijack accounts or perform identity theft.

    Using encrypted traffic ensures that data remains secure while in transit. This can be achieved through:

    • Enforcing WebSocket secure (wss://)

    • Using strong encryption mechanisms for authentication information

    • Avoiding sending authentication tokens in WebSocket headers

    Denial-of-Service (DoS) Attacks

    Attackers can overload your ws server by opening an excessive number of WebSocket connections or flooding it with large WebSocket frames. This can lead to excessive protocol processing, consuming CPU and memory resources, ultimately causing data loss.

    If your server lacks rate limiting or connection throttling, it becomes an easy target for DoS attacks.

    Code Injection Attacks (XSS and SQL Injection)

    WebSockets handle user input, and if that input isn’t sanitized, attackers can inject malicious scripts or database queries:

    • Cross-Site Scripting (XSS): If client-side code does not properly sanitize user input, attackers can inject JavaScript into WebSocket messages. This could lead to session hijacking, unauthorized data access, or malware execution on modern browsers.

    • SQL Injection: If WebSocket requests interact with a database connection directly without validation, attackers can execute injection attacks such as SQL queries to modify or extract internal record keeping data.

    Unrestricted WebSocket Origin Access

    By default, WebSockets don’t enforce the same-origin policy like traditional HTTP requests.  If the WebSocket server does not validate the origin header, malicious websites can establish unauthorized WebSocket connections. This means an attacker could establish connections from untrusted sources, leading to unauthorized data access or malicious activity on your platform. To prevent this, always restrict WebSocket connections to trusted origins.

    Example of enforcing a strict origin header policy:

    wss.on("connection", function connection(ws, req) {
        if (req.headers.origin !== "https://trusted-site.com") {
            ws.terminate();
        }
    });

    Securing WebSocket Connections

    If you're using WebSocket in your application, security isn't optional—it’s a necessity. WebSockets enable real-time communication but can also expose your system to serious vulnerabilities if not secured properly. Here’s what you need to do to keep your WebSocket connections safe:

    WSS (WebSocket Secure)

    WSS (WebSocket Secure) is the encrypted version of the WebSocket protocol, similar to how HTTPS secures HTTP. It ensures that WebSocket traffic is encrypted, preventing attackers from intercepting or modifying WebSocket frames.

    When a WebSocket connection is established using wss://, the handshake occurs over an encrypted communication channel, protecting against injection attacks, unauthorized access, and data loss.

    To ensure security:

    • Always use wss:// instead of ws:// to prevent man-in-the-middle attacks.

    • Implement TLS encryption to safeguard sensitive data transmitted between the WebSocket client and server.

    • Use the secure wss:// protocol instead of the insecure ws:// transport.

    If an attacker intercepts unencrypted traffic, they can steal authentication information or inject arbitrary data into a session. A secure version of WebSocket connection ensures that sensitive data remains encrypted during transmission.

    • How to Implement Secure WebSockets

      This code block is meant to demonstrate how to implement a secure WebSocket server using Node.js. Specifically, it shows how to configure a WebSocket server to use TLS/SSL encryption, which is essential for secure communication over the wss:// protocol (WebSocket Secure).

      const fs = require("fs");
      const https = require("https");
      const WebSocket = require("ws");
      
      // Creating an encrypted HTTP server
      const server = https.createServer({
          cert: fs.readFileSync("cert.pem"),
          key: fs.readFileSync("key.pem"),
      });
      
      // Setting up a secure WebSocket server
      const wss = new WebSocket.Server({ server });
      
      server.listen(8080, () => {
          console.log("Secure WebSocket server is running on wss://localhost:8080");
      });

    Key Components Explained

    1. fs module:
      Used to read SSL certificate and private key files (cert.pem and key.pem). These are required for setting up a TLS-secured server.

    2. https.createServer():
      Creates an HTTPS server (instead of a plain HTTP server) by providing the certificate and key. This allows for encrypted communication.

    3. WebSocket.Server({ server }):
      Binds the WebSocket server to the secure HTTPS server. This enables support for the wss:// protocol instead of the unencrypted ws://.

    4. server.listen(8080):
      Starts the server on port 8080 and logs a message indicating that the secure WebSocket server is running.

    Preventing Injection Attacks

    Injection attacks occur when an attacker exploits vulnerabilities in the WebSocket connection to send arbitrary data, manipulate WebSocket requests, or compromise sensitive data. Web applications using WebSocket can be susceptible to SQL injection, cross-site scripting (XSS), and other common WebSocket vulnerabilities.

    SQL Injection over WebSockets

    Attackers can exploit WebSocket connections to send arbitrary data and manipulate database queries, potentially gaining unauthorized access to sensitive information. To prevent SQL injection:

    • Always use prepared statements when handling database queries.

    • Validate and sanitize all user inputs before processing them.

    • Validate client input before processing it to prevent SQL injection attacks.

    Secure SQL Query Handling

    const sql = "SELECT * FROM users WHERE username = ?";
    db.get(sql, [username], (err, row) => {
        if (err) throw err;
    });

    Using prepared statements ensures that WebSocket application queries cannot be manipulated by arbitrary data from a client.

    XSS Attacks over WebSockets

    Cross-site scripting (XSS) exploits vulnerabilities in client-side code, allowing attackers to execute malicious scripts on behalf of other users. To mitigate XSS risks:

    • Use JSON.parse() to safely process incoming JSON data.

    • Sanitize all user inputs to prevent script injection.

    Validating Client and Server Data

    Ensuring that data exchanged between the client and the server is valid is crucial for maintaining websocket security.

    Ensuring Data Integrity

    • Use strong authentication mechanisms to verify websocket requests before processing them.

    • Encrypt websocket frames to prevent unauthorized data modification.

    • Implement internal record keeping to log and monitor suspicious activity.

    Origin Header Validation

    • The Origin header field should be validated in the websocket handshake to determine the host of the websocket connection.

    • Enforce a secure version of the WebSocket protocol (Sec-WebSocket-Protocol) to ensure strict origin validation.

    • Implement a whitelist of authorized domains to prevent cross-site WebSocket hijacking.

    • Secure authentication should be performed using a combination of authentication information and ticket-based authentication mechanisms.

    Best Practices for Data Validation

    • Enforce input validation on the server before processing WebSocket frames.

    • Strip out HTML or JavaScript code to prevent cross-site scripting.

    • Ensure all client-side code is properly sanitized before rendering messages.

    Example of Secure Data Handling:

    function sanitizeInput(input) {
        return input.replace(/<[^>]+>/g, ""); // Removes HTML tags
    }
    
    wss.on("message", (msg) => {
        const safeMessage = sanitizeInput(msg);
        console.log("Received sanitized message:", safeMessage);
    });

    Explanation and Context:

    1. Function: sanitizeInput(input)

    • Purpose: It removes any HTML tags from the user input.

    • How: It uses a regular expression / <[^>]+> /g to match and strip out any HTML-like content.

    • Why: To prevent users from injecting potentially malicious scripts (e.g., <script>alert('XSS')</script>) into the application.

    2. WebSocket Server (wss.on("message", ...))

    • This is part of a WebSocket server implementation, listening for incoming messages from clients.

    • The msg parameter represents the raw message sent by a connected user/client.

    • The input is passed through sanitizeInput() before any further processing.

    Authentication and Authorization

    WebSocket authentication and authorization mechanisms are crucial to restricting access to legitimate users only.

    Ticket-Based Authentication

    • Implement a ticket-based authentication mechanism where a secure token is issued after a successful http request authentication process.

    • The authentication token should be sent as part of the WebSocket headers in the initial handshake to verify the user's identity.

    • Avoid using shared authorization headers across multiple WebSocket connections to prevent unauthorized access.

    • Secure authentication should be managed at both the http server and ws server level to prevent websocket authentication problems.

    • Use proxy servers and caching intermediaries to inspect WebSocket traffic and detect anomalies in real-time.

    Best Practices for Secure Authentication

    • Use token-based authentication instead of relying on shared authorization headers.

    • Avoid storing authentication information in WebSocket headers.

    • Implement secure authentication using JWT tokens or OAuth.

    Example of Token Authentication:

    wss.on("connection", (ws, req) => {
        const token = req.headers["sec-websocket-key"];
        if (!validateToken(token)) {
            ws.close();
            console.log("Unauthorized WebSocket connection attempt");
        }
    });
    • wss.on("connection", ...): Listens for incoming WebSocket connections.

    • req.headers["sec-websocket-key"]: Extracts the sec-websocket-key header from the WebSocket upgrade request.

    • validateToken(token): Calls a function (not shown) to check whether the token is valid.

    • If invalid: Closes the WebSocket connection and logs an unauthorized access attempt.

    By securing WebSocket requests with token authentication, unauthorized access can be prevented.

    Testing and Monitoring

    Securing WebSocket connections requires continuous testing and real-time monitoring to prevent common WebSocket vulnerabilities like cross-site scripting (XSS), SQL injection, and man-in-the-middle attacks.

    Key Security Measures

    1. Use WebSocket Security Testing Tools

      Use tools like Wireshark to detect and monitor authentication flaws, injection vulnerabilities, and insecure WebSocket traffic. Intercept and modify WebSocket headers to test authentication mechanisms.

    2. Monitor WebSocket Traffic for Anomalies

      Log WebSocket requests, detect unusual activity, and implement rate-limiting to prevent DoS attacks. Log and analyze WebSocket frames to identify unauthorized access attempts.

    3. Validate WebSocket Authentication

      Ensure authentication tokens are not exposed in WebSocket headers and that sessions expire correctly. Reject WebSocket handshake attempts without valid authentication information.

    4. Prevent Injection Attacks (SQL Injection & XSS)

      Use prepared statements and data sanitization to protect database connections from malicious queries. Prevent SQL injection by validating input before executing queries.

    5. Enforce CSRF Protection and Origin Restrictions

      Require CSRF tokens, validate Origin headers, and restrict WebSocket connections to trusted domains. Close WebSocket connections from untrusted origins.

      How to secure WebSockets in your app?

      Watch the Cyber Chief on-demand demo to see not only how it can help to keep attackers out, but also to see how you can ensure that you ship every release with zero known vulnerabilities. 

      Cyber Chief has been built to integrate with the workflows of high-growth SaaS teams and that's why it offers:

      • Results from scanning your application for the presence of OWASP Top 10 + SANS CWE 25 + thousands of other vulnerabilities.

      • A detailed description of the vulnerabilities found.

      • A risk level for each vulnerability, so you know which ones to fix first.

      • Best-practice fixes for each vulnerability, including code snippets where relevant.

      • On-Demand Security Coaching from our application security experts to help you patch vulnerabilities in hours, not days.

      Click the green button below to see how Cyber Chief works.