【问题标题】:How can I rate limit the /socket.io route on my http server?如何对我的 http 服务器上的 /socket.io 路由进行速率限制?
【发布时间】:2020-05-18 20:26:22
【问题描述】:

我正在使用 socket.io 并使用 rate-limiter-flexiblelimiter 来限制请求率,但我注意到路由 /socket.io 没有收到来自任何一个的 429,事实上,我无法记录任何来自该路由的请求使用app.use('/socket.io')..

我认为 socket.io 正在对这条路线进行一些处理,对吗?如果是这样,我如何确保在达到限制后对 /socket.io 的请求也收到 429?

Connect 上的速率限制器:

this.io.on(this.SocketConstants.CONNECTION, async (client) => {
      this.client = client;
      try {
        await this.rateLimiter.consume(client.handshake.address);
      } catch (rejRes) {
        // On flood
        client.error('Too many requests');
        client.disconnect(true);
      }

http 服务器上的速率限制器:

class RateLimiting {
    constructor() {
        this.limiter = new RateLimiter(2, 'minute', true);
        this.index = this.index.bind(this);
    }

    index(req,res,next){
        try {
            this.limiter.removeTokens(1, function(err, remainingRequests) {
                if (remainingRequests < 1) {
                    res.writeHead(429, {'Content-Type': 'text/plain;charset=UTF-8'});
                    res.end('429 Too Many Requests - your IP is being rate limited');
                    // res.status(429).json({ message: 'Too many requests' });
                } else {
                    next();
                }
            });
        }
        catch(err){
            console.log('Error', err);
        }
    }
}

编辑

对于遇到类似问题的任何人,我最终尝试了几种不同的方法来完成此任务,其中最简单的是:

  1. 编写自己的allowRequest

AllowRequest 是一个通过/失败函数,您可以使用它来覆盖默认的“checkRequest”函数 (reference here)

checkBucket(err, remainingRequests) {
    remainingRequests < 1 ? false : true;
  }

allowRequest(req, callback) {
    const limit = this.limiter.removeTokens(1, this.checkBucket);
    callback(limit ? null : 'Too many requests', limit);
  }

并且服务器启动为

this.io = this.socketServer(server, 
        { 
          allowRequest: this.allowRequest
        });
  1. 选择不同的路由,使用 express 添加任何需要的中间件并禁用默认路由

您可以通过将 serveClient 设置为 false 并将 path 设置为您需要的任何值来完成此操作。

this.io = this.socketServer(服务器, { 路径:'/newSocketRoute', 服务客户端:假 });

这对我来说不太奏效,所以我提供套接字文件的方式可能有问题,但这就是它的样子:

app.get("/socket", this.limiter.initialize(), function(req, res) {
        if (0 === req.url.indexOf('/newSocketRoute/socket.io.js.map')) {
          res.sendFile(join(__dirname, "../../node_modules/socket.io-client/dist/socket.io.js.map"));
        } else if (0 === req.url.indexOf('/newSocketRoute/socket.io.js')) {
          res.sendFile(join(__dirname, "../../node_modules/socket.io-client/dist/socket.io.js"));
        }
      });

【问题讨论】:

  • 顺便说一下,rate-limiter-flexible 有RateLimiterQueue 与限制器包的功能相同。

标签: node.js socket.io rate-limiting


【解决方案1】:

Socket.io 通过将自己插入为request 事件的第一个侦听器(socket.io 代码reference here)将自己附加到您的 http 服务器,这意味着它完全绕过了任何 Express 中间件。

如果您尝试对/socket.io/socket.io.js(客户端 socket.io 代码)的请求进行速率限制,那么您可以使用您自己的自定义路由器在 Express 中为该文件创建自己的路由,并使用不同的路径并拥有您的客户端只需使用 Express 版本的路径,然后您可以禁用通过 socket.io 提供该文件(有一个选项可以禁用它)。

如果您尝试对传入的 socket.io 连接进行速率限制,那么您可能需要修改您的速率限制器,以便它可以参与 socket.io 连接事件。


现在我想到了,您可以像 socket.io 一样破解request 事件侦听器列表(您可以查看上面引用的代码链接了解它是如何做到的)并且您可以在之前插入自己的速率限制它可以看到请求。 socket.io 正在做的是实现一个穷人的中间件并切到行的最前面,这样他们就可以首先破解任何传入的 http 请求,如果他们处理了它,则将其隐藏起来。你可以用你的速率限制器做同样的事情。然后,您将参加“军备竞赛”,看看谁先得手。就个人而言,如果违反了速率限制规则,我可能只是挂钩connect 事件并终止那里的连接。但是,您可以破解并进入 socket.io 代码的前面。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-08-18
    • 1970-01-01
    • 2018-01-28
    • 1970-01-01
    • 1970-01-01
    • 2020-03-25
    • 1970-01-01
    相关资源
    最近更新 更多