diff --git a/settings.json.docker b/settings.json.docker index c4dcc78ea..56bc69649 100644 --- a/settings.json.docker +++ b/settings.json.docker @@ -408,6 +408,23 @@ "indentationOnNewLine": false, */ + /* + * From Etherpad 1.8.3 onwards, import and export of pads is always rate + * limited. + * + * The default is to allow at most 10 requests per IP in a 90 seconds window. + * After that the import/export request is rejected. + * + * See https://github.com/nfriedly/express-rate-limit for more options + */ + "importExportRateLimiting": { + // duration of the rate limit window (milliseconds) + "windowMs": 90000, + + // maximum number of requests per IP to allow during the rate limit window + "max": 10 + }, + /* * From Etherpad 1.8.3 onwards, the maximum allowed size for a single imported * file is always bounded. diff --git a/settings.json.template b/settings.json.template index 97ed7844b..0e61c7c67 100644 --- a/settings.json.template +++ b/settings.json.template @@ -413,6 +413,23 @@ "indentationOnNewLine": false, */ + /* + * From Etherpad 1.8.3 onwards, import and export of pads is always rate + * limited. + * + * The default is to allow at most 10 requests per IP in a 90 seconds window. + * After that the import/export request is rejected. + * + * See https://github.com/nfriedly/express-rate-limit for more options + */ + "importExportRateLimiting": { + // duration of the rate limit window (milliseconds) + "windowMs": 90000, + + // maximum number of requests per IP to allow during the rate limit window + "max": 10 + }, + /* * From Etherpad 1.8.3 onwards, the maximum allowed size for a single imported * file is always bounded. diff --git a/src/node/hooks/express/importexport.js b/src/node/hooks/express/importexport.js index 5e27e940e..95d02775d 100644 --- a/src/node/hooks/express/importexport.js +++ b/src/node/hooks/express/importexport.js @@ -4,10 +4,19 @@ var exportHandler = require('../../handler/ExportHandler'); var importHandler = require('../../handler/ImportHandler'); var padManager = require("../../db/PadManager"); var authorManager = require("../../db/AuthorManager"); +const rateLimit = require("express-rate-limit"); + +settings.importExportRateLimiting.onLimitReached = function(req, res, options) { + // when the rate limiter triggers, write a warning in the logs + console.warn(`Import/Export rate limiter triggered on "${req.originalUrl}" for IP address ${req.ip}`); +} + +var limiter = rateLimit(settings.importExportRateLimiting); exports.expressCreateServer = function (hook_name, args, cb) { // handle export requests + args.app.use('/p/:pad/:rev?/export/:type', limiter); args.app.get('/p/:pad/:rev?/export/:type', async function(req, res, next) { var types = ["pdf", "doc", "txt", "html", "odt", "etherpad"]; //send a 404 if we don't support this filetype @@ -40,6 +49,7 @@ exports.expressCreateServer = function (hook_name, args, cb) { }); // handle import requests + args.app.use('/p/:pad/import', limiter); args.app.post('/p/:pad/import', async function(req, res, next) { if (await hasPadAccess(req, res)) { let exists = await padManager.doesPadExists(req.params.pad); diff --git a/src/node/utils/Settings.js b/src/node/utils/Settings.js index 4c808620b..91fe5d7c2 100644 --- a/src/node/utils/Settings.js +++ b/src/node/utils/Settings.js @@ -305,6 +305,23 @@ exports.scrollWhenFocusLineIsOutOfViewport = { */ exports.exposeVersion = false; +/* + * From Etherpad 1.8.3 onwards, import and export of pads is always rate + * limited. + * + * The default is to allow at most 10 requests per IP in a 90 seconds window. + * After that the import/export request is rejected. + * + * See https://github.com/nfriedly/express-rate-limit for more options + */ +exports.importExportRateLimiting = { + // duration of the rate limit window (milliseconds) + "windowMs": 90000, + + // maximum number of requests per IP to allow during the rate limit window + "max": 10 +}; + /* * From Etherpad 1.8.3 onwards, the maximum allowed size for a single imported * file is always bounded. diff --git a/src/package-lock.json b/src/package-lock.json index 48ad650d6..ebd75d4bd 100644 --- a/src/package-lock.json +++ b/src/package-lock.json @@ -1633,6 +1633,11 @@ } } }, + "express-rate-limit": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-5.1.1.tgz", + "integrity": "sha512-puA1zcCx/quwWUOU6pT6daCt6t7SweD9wKChKhb+KSgFMKRwS81C224hiSAUANw/gnSHiwEhgozM/2ezEBZPeA==" + }, "express-session": { "version": "1.17.0", "resolved": "https://registry.npmjs.org/express-session/-/express-session-1.17.0.tgz", diff --git a/src/package.json b/src/package.json index 29a7cbec9..9cd9abd44 100644 --- a/src/package.json +++ b/src/package.json @@ -40,6 +40,7 @@ "etherpad-require-kernel": "1.0.9", "etherpad-yajsml": "0.0.2", "express": "4.17.1", + "express-rate-limit": "5.1.1", "express-session": "1.17.0", "find-root": "1.1.0", "formidable": "1.2.1",