diff --git a/package-lock.json b/package-lock.json index 3c07216..abd8bff 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3796,6 +3796,24 @@ "url": "https://opencollective.com/express" } }, + "node_modules/express-rate-limit": { + "version": "8.5.2", + "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-8.5.2.tgz", + "integrity": "sha512-5Kb34ipNX694DH48vN9irak1Qx30nb0PLYHXfJgw4YEjiC3ZEmZJhwOp+VfiCYwFzvFTdB9QkArYS5kXa2cx2A==", + "license": "MIT", + "dependencies": { + "ip-address": "^10.2.0" + }, + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://github.com/sponsors/express-rate-limit" + }, + "peerDependencies": { + "express": ">= 4.11" + } + }, "node_modules/express/node_modules/debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", @@ -4309,6 +4327,15 @@ "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", "license": "ISC" }, + "node_modules/ip-address": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.2.0.tgz", + "integrity": "sha512-/+S6j4E9AHvW9SWMSEY9Xfy66O5PWvVEJ08O0y5JGyEKQpojb0K0GKpz/v5HJ/G0vi3D2sjGK78119oXZeE0qA==", + "license": "MIT", + "engines": { + "node": ">= 12" + } + }, "node_modules/ipaddr.js": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", @@ -7907,6 +7934,7 @@ "cookie-parser": "^1.4.7", "drizzle-orm": "^0.33.0", "express": "^4.19.0", + "express-rate-limit": "^8.5.2", "multer": "^1.4.5-lts.1", "nodemailer": "^8.0.7", "xlsx": "^0.18.5", diff --git a/packages/backend/package.json b/packages/backend/package.json index 4fd88eb..141d124 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -22,6 +22,7 @@ "cookie-parser": "^1.4.7", "drizzle-orm": "^0.33.0", "express": "^4.19.0", + "express-rate-limit": "^8.5.2", "multer": "^1.4.5-lts.1", "nodemailer": "^8.0.7", "xlsx": "^0.18.5", diff --git a/packages/backend/src/middleware/rate-limit.ts b/packages/backend/src/middleware/rate-limit.ts new file mode 100644 index 0000000..19c23f6 --- /dev/null +++ b/packages/backend/src/middleware/rate-limit.ts @@ -0,0 +1,21 @@ +import rateLimit from 'express-rate-limit'; + +const fifteenMin = 15 * 60 * 1000; + +function makeLimiter(max: number, codeMessage = 'Too many attempts, please try again later') { + return rateLimit({ + windowMs: fifteenMin, + limit: max, + standardHeaders: 'draft-7', + legacyHeaders: false, + skip: () => process.env.NODE_ENV === 'test', + handler: (_req, res) => { + res.status(429).json({ error: { code: 'RATE_LIMITED', message: codeMessage } }); + }, + }); +} + +export const loginLimiter = makeLimiter(10); +export const registerLimiter = makeLimiter(5); +export const forgotPasswordLimiter = makeLimiter(5); +export const tokenLimiter = makeLimiter(20);