From bf9e076f3e0c149f81b1f07ba78fae82a035be09 Mon Sep 17 00:00:00 2001 From: Christian Rute Date: Mon, 18 Nov 2024 17:37:57 +0100 Subject: [PATCH] add little more security - against brute force attacs --- client/css/styles.css | 3 --- server/controllers/authController.js | 38 +++++++++++++++++++++++++++- server/models/User.js | 10 +++++++- 3 files changed, 46 insertions(+), 5 deletions(-) diff --git a/client/css/styles.css b/client/css/styles.css index 0a8ce6b..91c8250 100644 --- a/client/css/styles.css +++ b/client/css/styles.css @@ -426,9 +426,6 @@ main { color: white; } - - - /* General alert box styling */ #custom-alert-confirm { display: none; diff --git a/server/controllers/authController.js b/server/controllers/authController.js index 7431df8..622f3b9 100644 --- a/server/controllers/authController.js +++ b/server/controllers/authController.js @@ -47,12 +47,48 @@ const login = async (req, res) => { return res.status(400).json({ message: 'Ungültige Anmeldedaten' }); } + // Check for too many failed login attempts (e.g., more than 5 in 15 minutes) + const maxAttempts = 5; + const lockTime = Math.pow(2, user.failedLoginAttempts) * 15 * 1000; // Exponentially increasing delay + const now = new Date(); + + if (user.failedLoginAttempts >= maxAttempts && user.lastFailedLogin) { + const timeDifference = now - new Date(user.lastFailedLogin); + if (timeDifference < lockTime) { + return res.status(429).json({ + message: 'Zu viele fehlgeschlagene Versuche. Bitte versuchen Sie es später erneut.', + }); + } else { + // Reset failed attempts if the lock time has passed + user.failedLoginAttempts = 0; + user.lastFailedLogin = null; + await user.save(); + } + } + // Passwort überprüfen const isMatch = await bcrypt.compare(password, user.password); if (!isMatch) { - return res.status(400).json({ message: 'Ungültige Anmeldedaten' }); + // Increment failed login attempts + user.failedLoginAttempts += 1; + user.lastFailedLogin = now; // Update the timestamp of the last failed attempt + await user.save(); + + // Calculate delay (using the exponential backoff strategy) + const delay = Math.pow(2, user.failedLoginAttempts) * 15 * 1000; // Delay in milliseconds + const delayInSeconds = delay / 1000; + + // Return the delay time to the client + return res.status(400).json({ + message: `Invalid credentials. Please wait for ${delayInSeconds} seconds before trying again.`, + }); } + // Reset failed login attempts on successful login + user.failedLoginAttempts = 0; + user.lastFailedLogin = null; + await user.save(); + // JWT-Token erstellen mit benutzerdefinierten Daten const token = jwt.sign({ id: user._id, username: user.username, isAdmin: user.isAdmin }, process.env.JWT_SECRET, { expiresIn: '1h' }); diff --git a/server/models/User.js b/server/models/User.js index 19b3e97..b19461e 100644 --- a/server/models/User.js +++ b/server/models/User.js @@ -24,7 +24,15 @@ const userSchema = new mongoose.Schema({ isAdmin: { type: Boolean, default: false // Standardwert ist false - } + }, + failedLoginAttempts: { // Track failed login attempts + type: Number, + default: 0 + }, + lastFailedLogin: { // Track the last failed login attempt time + type: Date, + default: null + }, }); module.exports = mongoose.model('User', userSchema);