feat: rate limit file uploads

This commit is contained in:
psychobunny
2021-04-18 19:03:35 -04:00
committed by Andrew Rodrigues
parent 0a3a22db9d
commit a9978fcfd2
9 changed files with 115 additions and 14 deletions

View File

@@ -66,6 +66,7 @@ Object.assign(middleware, {
require('./render')(middleware);
require('./maintenance')(middleware);
require('./user')(middleware);
require('./uploads')(middleware);
require('./headers')(middleware);
require('./expose')(middleware);
middleware.assert = require('./assert');

32
src/middleware/uploads.js Normal file
View File

@@ -0,0 +1,32 @@
'use strict';
const LRU = require('lru-cache');
const meta = require('../meta');
const helpers = require('./helpers');
const user = require('../user');
const controllerHelpers = require('../controllers/helpers');
const cache = new LRU({
maxAge: meta.config.uploadRateLimitThreshold * 1000,
});
module.exports = function (middleware) {
middleware.ratelimitUploads = helpers.try(async (req, res, next) => {
const { uid } = req;
if (!uid) {
return controllerHelpers.notAllowed(req, res);
}
if (!meta.config.uploadRateLimitThreshold || await user.isAdminOrGlobalMod(req.uid)) {
return next();
}
const count = (cache.peek(`${uid}:uploaded_file_count`) || 0) + req.files.files.length;
if (count > meta.config.uploadRateLimitThreshold) {
return next(new Error(['[[error:upload-ratelimit-reached]]']));
}
cache.set(`${uid}:uploaded_file_count`, count);
next();
});
};

View File

@@ -27,8 +27,14 @@ module.exports = function (app, middleware, controllers) {
const multipart = require('connect-multiparty');
const multipartMiddleware = multipart();
const middlewares = [middleware.maintenanceMode, multipartMiddleware, middleware.validateFiles, middleware.applyCSRF];
router.post('/post/upload', middlewares, uploadsController.uploadPost);
const middlewares = [
middleware.maintenanceMode,
multipartMiddleware,
middleware.validateFiles,
middleware.ratelimitUploads,
middleware.applyCSRF,
];
router.post('/post/upload', middlewares, uploadsController.uploadPost);
router.post('/user/:userslug/uploadpicture', middlewares.concat([middleware.exposeUid, middleware.authenticateRequest, middleware.ensureLoggedIn, middleware.canViewUsers, middleware.checkAccountPermissions]), controllers.accounts.edit.uploadPicture);
};

View File

@@ -36,7 +36,7 @@ module.exports = function () {
setupApiRoute(router, 'delete', '/:tid/tags', [...middlewares, middleware.assert.topic], controllers.write.topics.deleteTags);
setupApiRoute(router, 'get', '/:tid/thumbs', [], controllers.write.topics.getThumbs);
setupApiRoute(router, 'post', '/:tid/thumbs', [multipartMiddleware, middleware.validateFiles, ...middlewares], controllers.write.topics.addThumb);
setupApiRoute(router, 'post', '/:tid/thumbs', [multipartMiddleware, middleware.validateFiles, middleware.ratelimitUploads, ...middlewares], controllers.write.topics.addThumb);
setupApiRoute(router, 'put', '/:tid/thumbs', [...middlewares, middleware.checkRequired.bind(null, ['tid'])], controllers.write.topics.migrateThumbs);
setupApiRoute(router, 'delete', '/:tid/thumbs', [...middlewares, middleware.checkRequired.bind(null, ['path'])], controllers.write.topics.deleteThumb);
setupApiRoute(router, 'put', '/:tid/thumbs/order', [...middlewares, middleware.checkRequired.bind(null, ['path', 'order'])], controllers.write.topics.reorderThumbs);

View File

@@ -101,6 +101,26 @@
[[admin/settings/uploads:allowed-file-extensions-help]]
</p>
</div>
<div class="form-group">
<label for="uploadRateLimitThreshold">[[admin/settings/uploads:upload-limit-threshold]]</label>
<div class="row">
<div class="col-xs-2">
<input type="text" class="form-control" data-field="uploadRateLimitThreshold" />
</div>
<div class="col-xs-1">
<label style="vertical-align: -5px;">[[admin/settings/uploads:upload-limit-threshold-per]]</label>
</div>
<div class="col-xs-2">
<select class="form-control" data-field="uploadRateLimitCooldown">
<option value="60">1 [[admin/settings/uploads:upload-limit-threshold-per-minute]]</option>
<option value="300">5 [[admin/settings/uploads:upload-limit-threshold-per-minutes]]</option>
<option value="900">15 [[admin/settings/uploads:upload-limit-threshold-per-minutes]]</option>
<option value="3600">60 [[admin/settings/uploads:upload-limit-threshold-per-minutes]]</option>
</select>
</div>
</div>
</div>
</form>
</div>