fix: rate limit fights!
authorxangelo <me@xangelo.ca>
Fri, 25 Aug 2023 14:39:45 +0000 (10:39 -0400)
committerxangelo <me@xangelo.ca>
Fri, 25 Aug 2023 14:39:45 +0000 (10:39 -0400)
You can only start 1 fight every 2 seconds.

package-lock.json
package.json
src/server/api.ts

index 88e09fe9bd9578fb74863c1de172c63d3f737223..a371720616fb990a22ceac75b0464d1dd172105a 100644 (file)
@@ -18,6 +18,7 @@
         "csv-parse": "^5.4.0",
         "dotenv": "^16.0.3",
         "express": "^4.18.2",
+        "express-rate-limit": "^6.9.0",
         "ioredis": "^5.3.2",
         "knex": "^2.4.2",
         "lodash": "^4.17.21",
         "node": ">= 0.10.0"
       }
     },
+    "node_modules/express-rate-limit": {
+      "version": "6.9.0",
+      "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-6.9.0.tgz",
+      "integrity": "sha512-AnISR3V8qy4gpKM62/TzYdoFO9NV84fBx0POXzTryHU/qGUJBWuVGd+JhbvtVmKBv37t8/afmqdnv16xWoQxag==",
+      "engines": {
+        "node": ">= 14.0.0"
+      },
+      "peerDependencies": {
+        "express": "^4 || ^5"
+      }
+    },
     "node_modules/express/node_modules/body-parser": {
       "version": "1.20.1",
       "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz",
         }
       }
     },
+    "express-rate-limit": {
+      "version": "6.9.0",
+      "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-6.9.0.tgz",
+      "integrity": "sha512-AnISR3V8qy4gpKM62/TzYdoFO9NV84fBx0POXzTryHU/qGUJBWuVGd+JhbvtVmKBv37t8/afmqdnv16xWoQxag==",
+      "requires": {}
+    },
     "extend": {
       "version": "3.0.2",
       "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
index cfc0057c6fa8da59cc92feb39aac73e3fb7a0cc2..1f3d2aa20ce65f943ea91a5e4d1564dc546943b1 100644 (file)
@@ -52,6 +52,7 @@
     "csv-parse": "^5.4.0",
     "dotenv": "^16.0.3",
     "express": "^4.18.2",
+    "express-rate-limit": "^6.9.0",
     "ioredis": "^5.3.2",
     "knex": "^2.4.2",
     "lodash": "^4.17.21",
index 4a176dd3d2d4004d0284c8a754fecb096f24913e..60c2b5c6b019ab40bc99c1b172e05bd1bcb3922a 100644 (file)
@@ -5,6 +5,7 @@ import { join } from 'path';
 import express, {Request, Response} from 'express';
 import bodyParser from 'body-parser';
 import xss from 'xss';
+import { rateLimit } from 'express-rate-limit';
 
 import http from 'http';
 import { Server, Socket } from 'socket.io';
@@ -71,6 +72,17 @@ app.use((req, res, next) => {
   next();
 });
 
+const fightRateLimiter = rateLimit({
+  windowMs: parseInt(process.env.RATE_LIMIT_WINDOW || '30000'),
+  max: parseInt(process.env.RATE_LIMIT_MAX_REQUESTS || '20'),
+  standardHeaders: true,
+  legacyHeaders: false,
+  handler: (req, res, next, options) => {
+    logger.log(`Blocked request: [${req.headers['x-authtoken']}: ${req.method} ${req.path}]`);
+    res.status(options.statusCode).send(options.message);
+  }
+});
+
 async function bootstrapSocket(socket: Socket, player: Player) {
   // ref to get the socket id for a particular player
   cache.set(`socket:${player.id}`, socket.id);
@@ -615,7 +627,7 @@ app.post('/fight/turn', authEndpoint, async (req: AuthRequest, res: Response) =>
   res.send(html + travelSection + playerBar);
 });
 
-app.post('/fight', authEndpoint, async (req: AuthRequest, res: Response) => {
+app.post('/fight', fightRateLimiter, authEndpoint, async (req: AuthRequest, res: Response) => {
   if(req.player.hp <= 0) {
     logger.log(`Player didn\'t have enough hp`);
     return res.sendStatus(400);