From 9dbd8f132fe998288271357e529539928ff320fb Mon Sep 17 00:00:00 2001 From: Duncan Carter Date: Tue, 3 Sep 2024 08:44:33 +0100 Subject: [PATCH 1/4] Update rate limiter and trust proxy --- backend/package-lock.json | 24 +++++++++++++------ backend/package.json | 2 +- backend/server.js | 4 +--- backend/server/routes/nhsBsaApi/apiDocs.js | 4 ++-- .../server/routes/nhsBsaApi/workplaceData.js | 4 ++-- .../server/utils/middleware/rateLimiting.js | 10 ++++---- .../utils/middleware/rateLimitingNHSBSAAPI.js | 18 ++++++-------- 7 files changed, 35 insertions(+), 31 deletions(-) diff --git a/backend/package-lock.json b/backend/package-lock.json index a6414d73a2..d212423180 100644 --- a/backend/package-lock.json +++ b/backend/package-lock.json @@ -33,7 +33,7 @@ "exceljs": "^4.2.0", "express": "^4.19.2", "express-http-proxy": "^1.6.2", - "express-rate-limit": "^5.5.1", + "express-rate-limit": "^7.4.0", "express-sanitizer": "^1.0.6", "helmet": "^4.6.0", "honeycomb-beeline": "^2.7.0", @@ -6424,9 +6424,18 @@ } }, "node_modules/express-rate-limit": { - "version": "5.5.1", - "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-5.5.1.tgz", - "integrity": "sha512-MTjE2eIbHv5DyfuFz4zLYWxpqVhEhkTiwFGuB74Q9CSou2WHO52nlE5y3Zlg6SIsiYUIPj6ifFxnkPz6O3sIUg==" + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-7.4.0.tgz", + "integrity": "sha512-v1204w3cXu5gCDmAvgvzI6qjzZzoMWKnyVDk3ACgfswTQLYiGen+r8w0VnXnGMmzEN/g8fwIQ4JrFFd4ZP6ssg==", + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://github.com/sponsors/express-rate-limit" + }, + "peerDependencies": { + "express": "4 || 5 || ^5.0.0-beta.1" + } }, "node_modules/express-sanitizer": { "version": "1.0.6", @@ -26755,9 +26764,10 @@ } }, "express-rate-limit": { - "version": "5.5.1", - "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-5.5.1.tgz", - "integrity": "sha512-MTjE2eIbHv5DyfuFz4zLYWxpqVhEhkTiwFGuB74Q9CSou2WHO52nlE5y3Zlg6SIsiYUIPj6ifFxnkPz6O3sIUg==" + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-7.4.0.tgz", + "integrity": "sha512-v1204w3cXu5gCDmAvgvzI6qjzZzoMWKnyVDk3ACgfswTQLYiGen+r8w0VnXnGMmzEN/g8fwIQ4JrFFd4ZP6ssg==", + "requires": {} }, "express-sanitizer": { "version": "1.0.6", diff --git a/backend/package.json b/backend/package.json index 72fba337a7..db656fa3cd 100644 --- a/backend/package.json +++ b/backend/package.json @@ -62,7 +62,7 @@ "exceljs": "^4.2.0", "express": "^4.19.2", "express-http-proxy": "^1.6.2", - "express-rate-limit": "^5.5.1", + "express-rate-limit": "^7.4.0", "express-sanitizer": "^1.0.6", "helmet": "^4.6.0", "honeycomb-beeline": "^2.7.0", diff --git a/backend/server.js b/backend/server.js index c7fce4ff12..af8a1bd510 100644 --- a/backend/server.js +++ b/backend/server.js @@ -312,9 +312,7 @@ app.get('/loaderio-63e80cd3c669177f22e9ec997ea2594d.txt', function (req, res) { }); app.use('*', authLimiter); -// app.get('*', function (req, res) { -// return res.sendFile(path.join(__dirname, 'dist/index.html')); -// }); +app.set('trust proxy', 1 /* number of proxies between user and server */); app.all('*', function (req, res) { res.status(404); diff --git a/backend/server/routes/nhsBsaApi/apiDocs.js b/backend/server/routes/nhsBsaApi/apiDocs.js index 50c094b23e..bee971b75c 100644 --- a/backend/server/routes/nhsBsaApi/apiDocs.js +++ b/backend/server/routes/nhsBsaApi/apiDocs.js @@ -2,7 +2,7 @@ const express = require('express'); const router = express.Router(); const fs = require('fs'); const path = require('path'); -const { authLimiter } = require('../../utils/middleware/rateLimitingNHSBSAAPI'); +const { nhsBsaApiLimiter } = require('../../utils/middleware/rateLimitingNHSBSAAPI'); const nhsBsaApiDocumentation = (req, res) => { try { @@ -17,6 +17,6 @@ const nhsBsaApiDocumentation = (req, res) => { } }; -router.route('/').get(authLimiter, nhsBsaApiDocumentation); +router.route('/').get(nhsBsaApiLimiter, nhsBsaApiDocumentation); module.exports = router; module.exports.nhsBsaApiDocumentation = nhsBsaApiDocumentation; diff --git a/backend/server/routes/nhsBsaApi/workplaceData.js b/backend/server/routes/nhsBsaApi/workplaceData.js index ea68ddfc97..15173d634b 100644 --- a/backend/server/routes/nhsBsaApi/workplaceData.js +++ b/backend/server/routes/nhsBsaApi/workplaceData.js @@ -2,7 +2,7 @@ const express = require('express'); const router = express.Router(); const models = require('../../models'); const authorization = require('../../utils/middleware/isNHSBSAAuthenticated'); -const { authLimiter } = require('../../utils/middleware/rateLimitingNHSBSAAPI'); +const { nhsBsaApiLimiter } = require('../../utils/middleware/rateLimitingNHSBSAAPI'); // WDF effective date const WdfCalculator = require('../../models/classes/wdfCalculator').WdfCalculator; @@ -98,7 +98,7 @@ const calculatePercentageOfWorkersEligible = (workers) => { return Math.floor((numberOfEligibleWorkers / numberOfWorkers) * 100); }; -router.route('/:workplaceId').get(authLimiter, authorization.isAuthorised, nhsBsaApi); +router.route('/:workplaceId').get(nhsBsaApiLimiter, authorization.isAuthorised, nhsBsaApi); module.exports = router; module.exports.nhsBsaApi = nhsBsaApi; module.exports.subsidiariesList = subsidiariesList; diff --git a/backend/server/utils/middleware/rateLimiting.js b/backend/server/utils/middleware/rateLimiting.js index c9d74be860..47ada061f4 100644 --- a/backend/server/utils/middleware/rateLimiting.js +++ b/backend/server/utils/middleware/rateLimiting.js @@ -1,5 +1,5 @@ const config = require('../../config/config'); -const RateLimit = require('express-rate-limit'); +const expressRateLimit = require('express-rate-limit'); const RedisStore = require('rate-limit-redis'); const isCI = require('is-ci'); @@ -20,9 +20,9 @@ const authLimiter = isCI ? (req, res, next) => { next(); } - : new RateLimit({ + : expressRateLimit.rateLimit({ ...rateLimiterConfig, - max: 1000, + limit: 1000, prefix: 'auth:', }); @@ -30,9 +30,9 @@ const dbLimiter = isCI ? (req, res, next) => { next(); } - : new RateLimit({ + : expressRateLimit.rateLimit({ ...rateLimiterConfig, - max: 1000, + limit: 1000, prefix: 'db:', }); diff --git a/backend/server/utils/middleware/rateLimitingNHSBSAAPI.js b/backend/server/utils/middleware/rateLimitingNHSBSAAPI.js index b99964765a..cec02edde3 100644 --- a/backend/server/utils/middleware/rateLimitingNHSBSAAPI.js +++ b/backend/server/utils/middleware/rateLimitingNHSBSAAPI.js @@ -1,6 +1,5 @@ - const config = require('../../config/config'); -const RateLimit = require('express-rate-limit'); +const expressRateLimit = require('express-rate-limit'); const RedisStore = require('rate-limit-redis'); const isCI = require('is-ci'); @@ -14,20 +13,17 @@ const rateLimiterConfig = { store, delayMs: 0, // disable delaying - full speed until the max limit is reached passIfNotConnected: true, - windowMs: 60 * 1000, // 1 minute + windowMs: 60 * 1000, // 1 minute }; -const authLimiter = isCI +const nhsBsaApiLimiter = isCI ? (req, res, next) => { next(); } - : new RateLimit({ + : expressRateLimit.rateLimit({ ...rateLimiterConfig, - max: 100, // maximum number of requests allowed in the windowMs - prefix: 'auth:', + limit: 200, // maximum number of requests allowed in the windowMs + prefix: 'nhsBsaApi:', }); - - -module.exports.authLimiter = authLimiter; - +module.exports.nhsBsaApiLimiter = nhsBsaApiLimiter; From cc7040746c4aa47b2808d59edaeecca268d91643 Mon Sep 17 00:00:00 2001 From: Duncan Carter Date: Tue, 3 Sep 2024 12:32:50 +0100 Subject: [PATCH 2/4] Update to trust two proxies for rate limiting --- backend/server.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/server.js b/backend/server.js index af8a1bd510..04f4ffc045 100644 --- a/backend/server.js +++ b/backend/server.js @@ -312,7 +312,7 @@ app.get('/loaderio-63e80cd3c669177f22e9ec997ea2594d.txt', function (req, res) { }); app.use('*', authLimiter); -app.set('trust proxy', 1 /* number of proxies between user and server */); +app.set('trust proxy', 2 /* number of proxies between user and server */); app.all('*', function (req, res) { res.status(404); From 8b08d2fe56bc02edcf49cb49c99b2a2c590b74b9 Mon Sep 17 00:00:00 2001 From: Duncan Carter Date: Tue, 3 Sep 2024 12:49:36 +0100 Subject: [PATCH 3/4] Add limiter to auth token endpoint --- backend/server/routes/nhsBsaApi/index.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/backend/server/routes/nhsBsaApi/index.js b/backend/server/routes/nhsBsaApi/index.js index f41456e576..3e419ccd49 100644 --- a/backend/server/routes/nhsBsaApi/index.js +++ b/backend/server/routes/nhsBsaApi/index.js @@ -2,9 +2,10 @@ const express = require('express'); const router = express.Router(); const generateJWT = require('../../utils/security/NHSBSAgenerateJWT'); const config = require('../../config/config'); +const { nhsBsaApiLimiter } = require('../../utils/middleware/rateLimitingNHSBSAAPI'); +router.use('/', nhsBsaApiLimiter); router.route('/').get(function (req, res) { - const authHeader = req.headers && req.headers.nhsbsaapikey; const nhsBsaApiKey = config.get('nhsBsaApi.apikey'); From 6b12b76db035b47387408108859f4743f1a01011 Mon Sep 17 00:00:00 2001 From: Duncan Carter Date: Tue, 3 Sep 2024 16:14:43 +0100 Subject: [PATCH 4/4] Use latest version of rate-limit-redis and update to use redis client --- backend/package-lock.json | 193 +++++++++--------- backend/package.json | 3 +- .../server/utils/middleware/rateLimiting.js | 17 +- .../utils/middleware/rateLimitingNHSBSAAPI.js | 6 +- 4 files changed, 121 insertions(+), 98 deletions(-) diff --git a/backend/package-lock.json b/backend/package-lock.json index d212423180..ad89496a71 100644 --- a/backend/package-lock.json +++ b/backend/package-lock.json @@ -37,6 +37,7 @@ "express-sanitizer": "^1.0.6", "helmet": "^4.6.0", "honeycomb-beeline": "^2.7.0", + "ioredis": "^5.4.1", "is-ci": "^3.0.0", "js-yaml": "^3.14.1", "jsonwebtoken": "^8.5.1", @@ -55,7 +56,7 @@ "pg": "^8.5.1", "pg-hstore": "^2.3.4", "pug": "^3.0.1", - "rate-limit-redis": "^2.1.0", + "rate-limit-redis": "^4.2.0", "request": "^2.88.2", "sequelize": "^6.29.0", "sequelize-cli": "^6.6.0", @@ -878,6 +879,11 @@ "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", "dev": true }, + "node_modules/@ioredis/commands": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@ioredis/commands/-/commands-1.2.0.tgz", + "integrity": "sha512-Sx1pU8EM64o2BrqNpEO1CNLtKQwyhuXuqyfH7oGKCk+1a33d2r5saW8zNwm3j6BTExtjrv2BxTgzzkMwts6vGg==" + }, "node_modules/@isaacs/cliui": { "version": "8.0.2", "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", @@ -4006,14 +4012,6 @@ "node": ">=8" } }, - "node_modules/clone": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", - "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", - "engines": { - "node": ">=0.8" - } - }, "node_modules/clone-response": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.3.tgz", @@ -4026,6 +4024,14 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/cluster-key-slot": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.2.tgz", + "integrity": "sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/codelyzer": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/codelyzer/-/codelyzer-6.0.2.tgz", @@ -5110,17 +5116,6 @@ "node": ">=8" } }, - "node_modules/defaults": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz", - "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==", - "dependencies": { - "clone": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/defer-to-connect": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-1.1.3.tgz", @@ -5239,9 +5234,9 @@ } }, "node_modules/denque": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/denque/-/denque-1.5.1.tgz", - "integrity": "sha512-XwE+iZ4D6ZUB7mfYRMb5wByE8L74HCn30FBN7sWnXksWc1LO1bPDl67pBR9o/kC4z/xSNAwkMYcGgqDV3BE3Hw==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/denque/-/denque-2.1.0.tgz", + "integrity": "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==", "engines": { "node": ">=0.10" } @@ -8415,6 +8410,29 @@ "integrity": "sha512-sNYPls1Q4fyN0EGLFVJ7TIuaMWln01LupLozfIBt69rHK0DHehghMSz6ejfnSklgRddnyQSEaQPIU6d9TCKH3w==", "dev": true }, + "node_modules/ioredis": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/ioredis/-/ioredis-5.4.1.tgz", + "integrity": "sha512-2YZsvl7jopIa1gaePkeMtd9rAcSjOOjPtpcLlOeusyO+XH2SK5ZcT+UCrElPP+WVIInh2TzeI4XW9ENaSLVVHA==", + "dependencies": { + "@ioredis/commands": "^1.1.1", + "cluster-key-slot": "^1.1.0", + "debug": "^4.3.4", + "denque": "^2.1.0", + "lodash.defaults": "^4.2.0", + "lodash.isarguments": "^3.1.0", + "redis-errors": "^1.2.0", + "redis-parser": "^3.0.0", + "standard-as-callback": "^2.1.0" + }, + "engines": { + "node": ">=12.22.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/ioredis" + } + }, "node_modules/ip": { "version": "1.1.5", "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz", @@ -10691,6 +10709,11 @@ "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==" }, + "node_modules/lodash.isarguments": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz", + "integrity": "sha512-chi4NHZlZqZD18a0imDHnZPrDeBbTtVN7GXMwuGdRH9qotxAjYs3aVLKc7zNOG9eddR5Ksd8rvFEBc9SsggPpg==" + }, "node_modules/lodash.isboolean": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", @@ -16947,12 +16970,14 @@ } }, "node_modules/rate-limit-redis": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/rate-limit-redis/-/rate-limit-redis-2.1.0.tgz", - "integrity": "sha512-6SAsTCzY0v6UCIKLOLLYqR2XzFmgdtF7jWXlSPq2FrNIZk8tZ7xwBvyGW7GFMCe5I4S9lYNdrSJ9E84rz3/CpA==", - "dependencies": { - "defaults": "^1.0.3", - "redis": "^3.0.2" + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/rate-limit-redis/-/rate-limit-redis-4.2.0.tgz", + "integrity": "sha512-wV450NQyKC24NmPosJb2131RoczLdfIJdKCReNwtVpm5998U8SgKrAZrIHaN/NfQgqOHaan8Uq++B4sa5REwjA==", + "engines": { + "node": ">= 16" + }, + "peerDependencies": { + "express-rate-limit": ">= 6" } }, "node_modules/raven": { @@ -17161,29 +17186,6 @@ "node": ">=8.10.0" } }, - "node_modules/redis": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/redis/-/redis-3.1.2.tgz", - "integrity": "sha512-grn5KoZLr/qrRQVwoSkmzdbw6pwF+/rwODtrOr6vuBRiR/f3rjSTGupbF90Zpqm2oenix8Do6RV7pYEkGwlKkw==", - "dependencies": { - "denque": "^1.5.0", - "redis-commands": "^1.7.0", - "redis-errors": "^1.2.0", - "redis-parser": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/node-redis" - } - }, - "node_modules/redis-commands": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/redis-commands/-/redis-commands-1.7.0.tgz", - "integrity": "sha512-nJWqw3bTFy21hX/CPKHth6sfhZbdiHP6bTawSgQBlKOVRG7EZkfHbbHwQJnrE4vsQf0CMNE+3gJ4Fmm16vdVlQ==" - }, "node_modules/redis-errors": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/redis-errors/-/redis-errors-1.2.0.tgz", @@ -18846,6 +18848,11 @@ "node": "*" } }, + "node_modules/standard-as-callback": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/standard-as-callback/-/standard-as-callback-2.1.0.tgz", + "integrity": "sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A==" + }, "node_modules/statuses": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", @@ -22296,6 +22303,11 @@ "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", "dev": true }, + "@ioredis/commands": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@ioredis/commands/-/commands-1.2.0.tgz", + "integrity": "sha512-Sx1pU8EM64o2BrqNpEO1CNLtKQwyhuXuqyfH7oGKCk+1a33d2r5saW8zNwm3j6BTExtjrv2BxTgzzkMwts6vGg==" + }, "@isaacs/cliui": { "version": "8.0.2", "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", @@ -24771,11 +24783,6 @@ } } }, - "clone": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", - "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==" - }, "clone-response": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.3.tgz", @@ -24785,6 +24792,11 @@ "mimic-response": "^1.0.0" } }, + "cluster-key-slot": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.2.tgz", + "integrity": "sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==" + }, "codelyzer": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/codelyzer/-/codelyzer-6.0.2.tgz", @@ -25660,14 +25672,6 @@ } } }, - "defaults": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz", - "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==", - "requires": { - "clone": "^1.0.2" - } - }, "defer-to-connect": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-1.1.3.tgz", @@ -25761,9 +25765,9 @@ "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==" }, "denque": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/denque/-/denque-1.5.1.tgz", - "integrity": "sha512-XwE+iZ4D6ZUB7mfYRMb5wByE8L74HCn30FBN7sWnXksWc1LO1bPDl67pBR9o/kC4z/xSNAwkMYcGgqDV3BE3Hw==" + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/denque/-/denque-2.1.0.tgz", + "integrity": "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==" }, "depd": { "version": "2.0.0", @@ -28276,6 +28280,22 @@ "integrity": "sha512-sNYPls1Q4fyN0EGLFVJ7TIuaMWln01LupLozfIBt69rHK0DHehghMSz6ejfnSklgRddnyQSEaQPIU6d9TCKH3w==", "dev": true }, + "ioredis": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/ioredis/-/ioredis-5.4.1.tgz", + "integrity": "sha512-2YZsvl7jopIa1gaePkeMtd9rAcSjOOjPtpcLlOeusyO+XH2SK5ZcT+UCrElPP+WVIInh2TzeI4XW9ENaSLVVHA==", + "requires": { + "@ioredis/commands": "^1.1.1", + "cluster-key-slot": "^1.1.0", + "debug": "^4.3.4", + "denque": "^2.1.0", + "lodash.defaults": "^4.2.0", + "lodash.isarguments": "^3.1.0", + "redis-errors": "^1.2.0", + "redis-parser": "^3.0.0", + "standard-as-callback": "^2.1.0" + } + }, "ip": { "version": "1.1.5", "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz", @@ -30033,6 +30053,11 @@ "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==" }, + "lodash.isarguments": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz", + "integrity": "sha512-chi4NHZlZqZD18a0imDHnZPrDeBbTtVN7GXMwuGdRH9qotxAjYs3aVLKc7zNOG9eddR5Ksd8rvFEBc9SsggPpg==" + }, "lodash.isboolean": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", @@ -34763,13 +34788,10 @@ "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==" }, "rate-limit-redis": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/rate-limit-redis/-/rate-limit-redis-2.1.0.tgz", - "integrity": "sha512-6SAsTCzY0v6UCIKLOLLYqR2XzFmgdtF7jWXlSPq2FrNIZk8tZ7xwBvyGW7GFMCe5I4S9lYNdrSJ9E84rz3/CpA==", - "requires": { - "defaults": "^1.0.3", - "redis": "^3.0.2" - } + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/rate-limit-redis/-/rate-limit-redis-4.2.0.tgz", + "integrity": "sha512-wV450NQyKC24NmPosJb2131RoczLdfIJdKCReNwtVpm5998U8SgKrAZrIHaN/NfQgqOHaan8Uq++B4sa5REwjA==", + "requires": {} }, "raven": { "version": "2.6.4", @@ -34939,22 +34961,6 @@ "picomatch": "^2.2.1" } }, - "redis": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/redis/-/redis-3.1.2.tgz", - "integrity": "sha512-grn5KoZLr/qrRQVwoSkmzdbw6pwF+/rwODtrOr6vuBRiR/f3rjSTGupbF90Zpqm2oenix8Do6RV7pYEkGwlKkw==", - "requires": { - "denque": "^1.5.0", - "redis-commands": "^1.7.0", - "redis-errors": "^1.2.0", - "redis-parser": "^3.0.0" - } - }, - "redis-commands": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/redis-commands/-/redis-commands-1.7.0.tgz", - "integrity": "sha512-nJWqw3bTFy21hX/CPKHth6sfhZbdiHP6bTawSgQBlKOVRG7EZkfHbbHwQJnrE4vsQf0CMNE+3gJ4Fmm16vdVlQ==" - }, "redis-errors": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/redis-errors/-/redis-errors-1.2.0.tgz", @@ -36256,6 +36262,11 @@ "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", "integrity": "sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==" }, + "standard-as-callback": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/standard-as-callback/-/standard-as-callback-2.1.0.tgz", + "integrity": "sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A==" + }, "statuses": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", diff --git a/backend/package.json b/backend/package.json index db656fa3cd..b3412a4cd9 100644 --- a/backend/package.json +++ b/backend/package.json @@ -66,6 +66,7 @@ "express-sanitizer": "^1.0.6", "helmet": "^4.6.0", "honeycomb-beeline": "^2.7.0", + "ioredis": "^5.4.1", "is-ci": "^3.0.0", "js-yaml": "^3.14.1", "jsonwebtoken": "^8.5.1", @@ -84,7 +85,7 @@ "pg": "^8.5.1", "pg-hstore": "^2.3.4", "pug": "^3.0.1", - "rate-limit-redis": "^2.1.0", + "rate-limit-redis": "^4.2.0", "request": "^2.88.2", "sequelize": "^6.29.0", "sequelize-cli": "^6.6.0", diff --git a/backend/server/utils/middleware/rateLimiting.js b/backend/server/utils/middleware/rateLimiting.js index 47ada061f4..876229ba0a 100644 --- a/backend/server/utils/middleware/rateLimiting.js +++ b/backend/server/utils/middleware/rateLimiting.js @@ -1,16 +1,23 @@ const config = require('../../config/config'); const expressRateLimit = require('express-rate-limit'); -const RedisStore = require('rate-limit-redis'); +const { RedisStore } = require('rate-limit-redis'); +const RedisClient = require('ioredis'); const isCI = require('is-ci'); -const store = isCI +const redisClient = new RedisClient(config.get('redis.url')); +const authStore = isCI ? undefined : new RedisStore({ - redisURL: config.get('redis.url'), + sendCommand: (...args) => redisClient.call(...args), + }); + +const dbStore = isCI + ? undefined + : new RedisStore({ + sendCommand: (...args) => redisClient.call(...args), }); const rateLimiterConfig = { - store, delayMs: 0, // disable delaying - full speed until the max limit is reached passIfNotConnected: true, windowMs: 5000, @@ -22,6 +29,7 @@ const authLimiter = isCI } : expressRateLimit.rateLimit({ ...rateLimiterConfig, + store: authStore, limit: 1000, prefix: 'auth:', }); @@ -32,6 +40,7 @@ const dbLimiter = isCI } : expressRateLimit.rateLimit({ ...rateLimiterConfig, + store: dbStore, limit: 1000, prefix: 'db:', }); diff --git a/backend/server/utils/middleware/rateLimitingNHSBSAAPI.js b/backend/server/utils/middleware/rateLimitingNHSBSAAPI.js index cec02edde3..52c0d783a2 100644 --- a/backend/server/utils/middleware/rateLimitingNHSBSAAPI.js +++ b/backend/server/utils/middleware/rateLimitingNHSBSAAPI.js @@ -1,12 +1,14 @@ const config = require('../../config/config'); const expressRateLimit = require('express-rate-limit'); -const RedisStore = require('rate-limit-redis'); +const { RedisStore } = require('rate-limit-redis'); +const RedisClient = require('ioredis'); const isCI = require('is-ci'); +const redisClient = new RedisClient(config.get('redis.url')); const store = isCI ? undefined : new RedisStore({ - redisURL: config.get('redis.url'), + sendCommand: (...args) => redisClient.call(...args), }); const rateLimiterConfig = {