diff --git a/package-lock.json b/package-lock.json index 521cbbfb0..88f878267 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", @@ -8254,6 +8255,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 e20bfd831..16ec49ac7 100644 --- a/package.json +++ b/package.json @@ -90,6 +90,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 775fad628..219a856ca 100644 --- a/src/server/Server.ts +++ b/src/server/Server.ts @@ -31,6 +31,8 @@ import { SecretManagerServiceClient } from "@google-cloud/secret-manager"; import dotenv from "dotenv"; import crypto from "crypto"; dotenv.config(); +import rateLimit from "express-rate-limit"; + const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); @@ -51,6 +53,22 @@ let DISCORD_CLIENT_SECRET: string; 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 + }), +); + +app.set("trust proxy", 2); +app.use( + rateLimit({ + windowMs: 1000, // 1 second + max: 20, // 20 requests per IP per second + }), +); + const gm = new GameManager(serverConfig); let lobbiesString = ""; @@ -208,6 +226,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 30177ad50..ba9e1c4c0 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", "/auth/callback", "/auth/discord", ],