Просмотр исходного кода

Merge pull request #4154 from weseek/imprv/gw6965-rate-limit

apply rate limit
cao 4 лет назад
Родитель
Сommit
2ca5334e79

+ 2 - 1
packages/app/package.json

@@ -56,8 +56,8 @@
     "@browser-bunyan/console-formatted-stream": "^1.6.2",
     "@google-cloud/storage": "^5.8.5",
     "@growi/plugin-attachment-refs": "^4.3.3-RC",
-    "@growi/plugin-pukiwiki-like-linker": "^4.3.3-RC",
     "@growi/plugin-lsx": "^4.3.3-RC",
+    "@growi/plugin-pukiwiki-like-linker": "^4.3.3-RC",
     "@growi/slack": "^4.3.3-RC",
     "@kobalab/socket.io-session": "^1.0.3",
     "@promster/express": "^5.0.1",
@@ -91,6 +91,7 @@
     "express-bunyan-logger": "^1.3.3",
     "express-form": "~0.12.0",
     "express-mongo-sanitize": "^2.1.0",
+    "express-rate-limit": "^5.3.0",
     "express-session": "^1.16.1",
     "express-validator": "^6.1.1",
     "express-webpack-assets": "^0.1.0",

+ 9 - 1
packages/app/src/server/routes/apiv3/forgot-password.js

@@ -1,3 +1,4 @@
+import rateLimit from 'express-rate-limit';
 import loggerFactory from '~/utils/logger';
 
 const logger = loggerFactory('growi:routes:apiv3:forgotPassword'); // eslint-disable-line no-unused-vars
@@ -29,6 +30,13 @@ module.exports = (crowi) => {
     ],
   };
 
+  const apiLimiter = rateLimit({
+    windowMs: 15 * 60 * 1000, // 15 minutes
+    max: 5, // limit each IP to 5 requests per windowMs
+    message:
+      'Too many requests were sent from this IP. Please try a password reset request again on the password reset request form',
+  });
+
   async function sendPasswordResetEmail(email, url, i18n) {
     return mailService.send({
       to: email,
@@ -69,7 +77,7 @@ module.exports = (crowi) => {
     }
   });
 
-  router.put('/', csrf, validator.password, apiV3FormValidator, async(req, res) => {
+  router.put('/', apiLimiter, csrf, validator.password, apiV3FormValidator, async(req, res) => {
     const { token, newPassword } = req.body;
 
     const passwordResetOrder = await PasswordResetOrder.findOne({ token });

+ 9 - 1
packages/app/src/server/routes/index.js

@@ -1,5 +1,13 @@
 const multer = require('multer');
 const autoReap = require('multer-autoreap');
+const rateLimit = require('express-rate-limit');
+
+const apiLimiter = rateLimit({
+  windowMs: 15 * 60 * 1000, // 15 minutes
+  max: 5, // limit each IP to 5 requests per windowMs
+  message:
+    'Too many requests sent from this IP, please try again after 15 minutes',
+});
 
 autoReap.options.reapOnError = true; // continue reaping the file even if an error occurs
 
@@ -178,7 +186,7 @@ module.exports = function(crowi, app) {
   app.post('/_api/hackmd.saveOnHackmd'   , accessTokenParser , loginRequiredStrictly , csrf, hackmd.validateForApi, hackmd.saveOnHackmd);
 
   app.get('/forgot-password', forgotPassword.forgotPassword);
-  app.get('/forgot-password/:token'      , passwordReset, forgotPassword.resetPassword);
+  app.get('/forgot-password/:token'      ,apiLimiter, passwordReset, forgotPassword.resetPassword);
   app.get('/forgot-password/error/:reason'      , applicationInstalled, forgotPassword.error);
 
   app.get('/share/:linkId', page.showSharedPage);

+ 5 - 0
yarn.lock

@@ -7991,6 +7991,11 @@ express-mongo-sanitize@^2.1.0:
   resolved "https://registry.yarnpkg.com/express-mongo-sanitize/-/express-mongo-sanitize-2.1.0.tgz#a8c647787c25ded6e97b5e864d113e7687c5d471"
   integrity sha512-ELGeH/Tx+kJGn3klCzSmOewfN1ezJQrkqzq83dl/K3xhd5PUbvLtiD5CiuYRmQfoZPL4rUEVjANf/YjE2BpTWQ==
 
+express-rate-limit@^5.3.0:
+  version "5.3.0"
+  resolved "https://registry.yarnpkg.com/express-rate-limit/-/express-rate-limit-5.3.0.tgz#e7b9d3c2e09ece6e0406a869b2ce00d03fe48aea"
+  integrity sha512-qJhfEgCnmteSeZAeuOKQ2WEIFTX5ajrzE0xS6gCOBCoRQcU+xEzQmgYQQTpzCcqUAAzTEtu4YEih4pnLfvNtew==
+
 express-session@^1.16.1:
   version "1.16.1"
   resolved "https://registry.yarnpkg.com/express-session/-/express-session-1.16.1.tgz#251ff9776c59382301de6c8c33411af357ed439c"