fix(security #6): add rate limiting to POST /api/auth/login
Uses express-rate-limit@6 (pinned for Node 12 dev compat; Node 18 in prod container is unaffected). Limits each IP to 10 attempts per 15-minute window. Returns 429 with a safe error message on breach.
This commit is contained in:
26
package-lock.json
generated
26
package-lock.json
generated
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "media-download-dashboard",
|
"name": "sofarr",
|
||||||
"version": "1.0.0",
|
"version": "0.1.4",
|
||||||
"lockfileVersion": 2,
|
"lockfileVersion": 2,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "media-download-dashboard",
|
"name": "sofarr",
|
||||||
"version": "1.0.0",
|
"version": "0.1.4",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"axios": "^1.6.0",
|
"axios": "^1.6.0",
|
||||||
@@ -14,6 +14,7 @@
|
|||||||
"cors": "^2.8.5",
|
"cors": "^2.8.5",
|
||||||
"dotenv": "^16.3.1",
|
"dotenv": "^16.3.1",
|
||||||
"express": "^4.18.2",
|
"express": "^4.18.2",
|
||||||
|
"express-rate-limit": "^6.7.0",
|
||||||
"node-cron": "^3.0.3"
|
"node-cron": "^3.0.3"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
@@ -623,6 +624,17 @@
|
|||||||
"url": "https://opencollective.com/express"
|
"url": "https://opencollective.com/express"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/express-rate-limit": {
|
||||||
|
"version": "6.7.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-6.7.0.tgz",
|
||||||
|
"integrity": "sha512-vhwIdRoqcYB/72TK3tRZI+0ttS8Ytrk24GfmsxDXK9o9IhHNO5bXRiXQSExPQ4GbaE5tvIS7j1SGrxsuWs+sGA==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 12.9.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"express": "^4 || ^5"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/fill-range": {
|
"node_modules/fill-range": {
|
||||||
"version": "7.1.1",
|
"version": "7.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
|
||||||
@@ -2124,6 +2136,12 @@
|
|||||||
"vary": "~1.1.2"
|
"vary": "~1.1.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"express-rate-limit": {
|
||||||
|
"version": "6.7.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-6.7.0.tgz",
|
||||||
|
"integrity": "sha512-vhwIdRoqcYB/72TK3tRZI+0ttS8Ytrk24GfmsxDXK9o9IhHNO5bXRiXQSExPQ4GbaE5tvIS7j1SGrxsuWs+sGA==",
|
||||||
|
"requires": {}
|
||||||
|
},
|
||||||
"fill-range": {
|
"fill-range": {
|
||||||
"version": "7.1.1",
|
"version": "7.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
|
||||||
|
|||||||
13
package.json
13
package.json
@@ -9,16 +9,17 @@
|
|||||||
"install:all": "npm install"
|
"install:all": "npm install"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"express": "^4.18.2",
|
"axios": "^1.6.0",
|
||||||
|
"cookie-parser": "^1.4.6",
|
||||||
"cors": "^2.8.5",
|
"cors": "^2.8.5",
|
||||||
"dotenv": "^16.3.1",
|
"dotenv": "^16.3.1",
|
||||||
"axios": "^1.6.0",
|
"express": "^4.18.2",
|
||||||
"node-cron": "^3.0.3",
|
"express-rate-limit": "^6.7.0",
|
||||||
"cookie-parser": "^1.4.6"
|
"node-cron": "^3.0.3"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"nodemon": "^2.0.22",
|
"concurrently": "^7.6.0",
|
||||||
"concurrently": "^7.6.0"
|
"nodemon": "^2.0.22"
|
||||||
},
|
},
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"sabnzbd",
|
"sabnzbd",
|
||||||
|
|||||||
@@ -1,12 +1,18 @@
|
|||||||
const express = require('express');
|
const express = require('express');
|
||||||
const axios = require('axios');
|
const axios = require('axios');
|
||||||
|
const rateLimit = require('express-rate-limit');
|
||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
|
|
||||||
const EMBY_URL = process.env.EMBY_URL;
|
const loginLimiter = rateLimit({
|
||||||
const EMBY_API_KEY = process.env.EMBY_API_KEY;
|
windowMs: 15 * 60 * 1000, // 15 minutes
|
||||||
|
max: 10,
|
||||||
|
standardHeaders: true,
|
||||||
|
legacyHeaders: false,
|
||||||
|
message: { success: false, error: 'Too many login attempts, please try again later' }
|
||||||
|
});
|
||||||
|
|
||||||
// Authenticate user with Emby
|
// Authenticate user with Emby
|
||||||
router.post('/login', async (req, res) => {
|
router.post('/login', loginLimiter, async (req, res) => {
|
||||||
try {
|
try {
|
||||||
const { username, password } = req.body;
|
const { username, password } = req.body;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user