DEV Community

Cover image for Building a Secure WebSocket-Based Customer Support Chat: Chat Policies, Rate Limiting, and BOLA Prevention
Hassam Fathe Muhammad
Hassam Fathe Muhammad

Posted on

Building a Secure WebSocket-Based Customer Support Chat: Chat Policies, Rate Limiting, and BOLA Prevention

Being a full-stack developer for quite some time, I have been consistently upgrading my full-stack knowledge by learning new technologies and features, along with how to implement them in a secure way.

What are WebSockets?

WebSockets provide a full-duplex, persistent communication channel between the client and the server, enabling real-time data exchange without repeatedly creating new HTTP requests. They are widely used in applications such as chat systems, live notifications, collaborative platforms, and online gaming where low-latency communication is essential.

Implementing a Customer Support Chat System

This time, I learned and implemented WebSockets for a customer service chat system, and I used the concept of a Chat Policy for blocking customer-to-customer chats, which is against the policy of a customer service chat system where only admins and agents are allowed to be texted.

On top of this, I learned how to implement a Chat Policy Service, a completely distinct service with its own database queries and validations, and then integrate it into the message-handling events. You can create your own chat policy based on a specific database schema according to your application's requirements.

Project Architecture

The overall folder structure of the chat system was organized by separating the socket initialization, socket events, services, middleware, authentication, and utilities, making the architecture modular and easier to maintain.

The message handling logic remained inside dedicated event handlers, while business logic such as Chat Policy validation was delegated to its own service, following the principle of Separation of Concerns (SoC).

Example Folder Structure

src/
├── socket/
│   ├── initSocket.ts
│   ├── onlineUsers.ts
│   ├── events/
│   │   ├── messageHandler.ts
│   │   └── disconnectHandler.ts
│   └── middleware/
│       └── socketAuth.ts
├── services/
│   ├── chatPolicy.service.ts
│   └── message.service.ts
├── models/
├── routes/
├── types/
├── utils/
└── server.ts
Enter fullscreen mode Exit fullscreen mode

Implementing Rate Limiting

Along with this, I also came to understand the importance of rate limiting while sending text messages, so I learned to implement rate limiting using JavaScript's Map.

One thing that could be useful for your knowledge is creating a single instance of the service and exporting it. This allows all event handlers to use the same Map across requests; otherwise, the counts and user information would not remain synchronized because each new instance would maintain its own separate memory.

This approach works well when running a single server instance and is unlikely to restart or crash frequently. However, because Map stores data only in memory, it is not persistent and does not work across multiple server instances. A more professional and production-standard solution is Redis, which provides centralized and persistent storage shared among all application instances.

Map-Based Rate Limiter

import { UserLimit } from "../types/rateLimit.types.ts";

class RateLimiter {

    private users = new Map<string, UserLimit>();

    private readonly MAX_MESSAGES = Number(process.env.RATE_LIMIT_MESSAGES) || 5;
    private readonly WINDOW_MS = Number(process.env.RATE_LIMIT_WINDOW) || 1000;

    public canSend(username: string): boolean {
        const now = Date.now();
        const user = this.users.get(username);

        if (!user) {
            this.users.set(username, {
                count: 1,
                windowStart: now
            });
            return true;
        }

        if (now - user.windowStart > this.WINDOW_MS) {
            user.count = 1;
            user.windowStart = now;
            return true;
        }

        if (user.count > this.MAX_MESSAGES) {
            return false;
        }

        user.count++;
        return true;
    }
}

export default new RateLimiter();
Enter fullscreen mode Exit fullscreen mode

Preventing Broken Object Level Authorization (BOLA)

In addition to these security standards, one more important thing that I implemented was preventing the flaw of Broken Object Level Authorization (BOLA), which is one of the most critical API security vulnerabilities.

BOLA occurs when an application trusts object identifiers or user information received from the client without verifying whether the authenticated user is actually authorized to access or modify those resources. A secure implementation should never trust identifiers such as usernames or user IDs coming directly from the frontend.

While implementing the chat system, I learned to extract the authenticated user's ID and username directly from the verified authentication token instead of accepting them from the client payload. This ensures that every authorization decision is based on the identity established by the server after token verification, preventing malicious users from impersonating other users simply by modifying request data.

By performing authorization checks on the server and validating permissions before processing chat events, the system effectively mitigates the risk of BOLA and enforces secure access control throughout the messaging workflow.

Key Takeaways

  • Learned to build real-time communication using WebSockets.
  • Implemented a dedicated Chat Policy Service for authorization rules.
  • Structured the application using Separation of Concerns (SoC).
  • Implemented Map-based rate limiting and understood its limitations.
  • Learned why Redis is the production-standard solution for distributed rate limiting.
  • Prevented Broken Object Level Authorization (BOLA) by extracting authenticated user information from verified tokens instead of trusting client-provided data.
  • Improved my understanding of secure backend architecture and real-time application development.

Top comments (0)