diff --git a/package-lock.json b/package-lock.json index 3487256bf..c1ded4a32 100644 --- a/package-lock.json +++ b/package-lock.json @@ -26,6 +26,7 @@ "dompurify": "^3.1.7", "dotenv": "^16.4.7", "express": "^4.21.1", + "express-rate-limit": "^7.5.0", "google-auth-library": "^9.14.0", "googleapis": "^143.0.0", "hammerjs": "^2.0.8", @@ -8241,6 +8242,21 @@ "node": ">= 0.10.0" } }, + "node_modules/express-rate-limit": { + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-7.5.0.tgz", + "integrity": "sha512-eB5zbQh5h+VenMPM3fh+nw1YExi5nMr6HUCR62ELSP11huvxm/Uir1H1QEyTkk5QX6A58pX6NmaTMceKZ0Eodg==", + "license": "MIT", + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://github.com/sponsors/express-rate-limit" + }, + "peerDependencies": { + "express": "^4.11 || 5 || ^5.0.0-beta.1" + } + }, "node_modules/express/node_modules/debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", diff --git a/package.json b/package.json index d47e714dc..04c758f88 100644 --- a/package.json +++ b/package.json @@ -89,6 +89,7 @@ "dompurify": "^3.1.7", "dotenv": "^16.4.7", "express": "^4.21.1", + "express-rate-limit": "^7.5.0", "google-auth-library": "^9.14.0", "googleapis": "^143.0.0", "hammerjs": "^2.0.8", diff --git a/src/server/Server.ts b/src/server/Server.ts index f1df564bf..ed94ad9cb 100644 --- a/src/server/Server.ts +++ b/src/server/Server.ts @@ -22,6 +22,7 @@ import { validateUsername, } from "../core/validations/username"; import { Request, Response } from "express"; +import rateLimit from "express-rate-limit"; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); @@ -34,6 +35,14 @@ const wss = new WebSocketServer({ server }); app.use(express.static(path.join(__dirname, "../../out"))); app.use(express.json()); +app.set("trust proxy", 2); +app.use( + rateLimit({ + windowMs: 1000, // 1 second + max: 20, // 20 requests per IP per second + }), +); + const gm = new GameManager(getServerConfig()); const bot = new DiscordBot(); @@ -130,6 +139,14 @@ app.get("/private_lobby/:id", (req, res) => { }); }); +app.get("/debug-ip", (req, res) => { + res.send({ + "x-forwarded-for": req.headers["x-forwarded-for"], + "real-ip": req.ip, + "raw-headers": req.rawHeaders, + }); +}); + app.get("*", function (req, res) { // SPA routing res.sendFile(path.join(__dirname, "../../out/index.html")); diff --git a/webpack.config.js b/webpack.config.js index c6e879fae..180481d1d 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -138,6 +138,7 @@ export default (env, argv) => { "/lobby", "/archive_singleplayer_game", "/validate-username", + "/debug-ip", ], target: "http://localhost:3000", secure: false,