diff --git a/backend/src/routes/auth.js b/backend/src/routes/auth.js index 6bb8714..8c08fbc 100644 --- a/backend/src/routes/auth.js +++ b/backend/src/routes/auth.js @@ -52,9 +52,36 @@ const validateLogin = (req, res, next) => { } }; +// Sicherheitsfunktionen +const generateSecureToken = (userId, email) => { + // Verwende eine stärkere JWT-Konfiguration + return jwt.sign( + { userId, email }, + process.env.JWT_SECRET || 'fallback_secret_key_for_dev', + { + expiresIn: '7d', + issuer: 'helpyourneighbour-backend', + audience: 'helpyourneighbour-users' + } + ); +}; + +// Rate limiting für Auth-Endpunkte (simuliert) +let loginAttempts = new Map(); +const MAX_ATTEMPTS = 5; +const LOCKOUT_TIME = 15 * 60 * 1000; // 15 Minuten + router.post('/register', validateRegister, async (req, res) => { try { const { email, password, displayName } = req.validatedData; + + // Überprüfe, ob das Passwort sicher ist + if (!/(?=.*[a-z])(?=.*[A-Z])(?=.*\d)/.test(password)) { + return res.status(400).json({ + error: 'Password must contain at least one lowercase letter, one uppercase letter, and one digit' + }); + } + const passwordHash = await bcrypt.hash(password, 12); const [result] = await pool.query( @@ -62,8 +89,12 @@ router.post('/register', validateRegister, async (req, res) => { [email, passwordHash, displayName] ); - const token = jwt.sign({ userId: result.insertId, email }, process.env.JWT_SECRET, { expiresIn: '7d' }); - return res.status(201).json({ token }); + const token = generateSecureToken(result.insertId, email); + return res.status(201).json({ + token, + userId: result.insertId, + email + }); } catch (err) { console.error('Registration error:', err); if (err.code === 'ER_DUP_ENTRY') { @@ -76,20 +107,38 @@ router.post('/register', validateRegister, async (req, res) => { router.post('/login', validateLogin, async (req, res) => { try { const { email, password } = req.validatedData; + + // Rate limiting check + const attempts = loginAttempts.get(email) || 0; + if (attempts >= MAX_ATTEMPTS) { + return res.status(429).json({ error: 'Too many login attempts. Please try again later.' }); + } + const [rows] = await pool.query('SELECT id, email, password_hash FROM users WHERE email = ? LIMIT 1', [email]); const user = rows[0]; if (!user) { + // Erhöhe den Versuchscounter für nicht-existente E-Mail + loginAttempts.set(email, (attempts + 1)); return res.status(401).json({ error: 'Invalid credentials' }); } const ok = await bcrypt.compare(password, user.password_hash); if (!ok) { + // Erhöhe den Versuchscounter für falsches Passwort + loginAttempts.set(email, (attempts + 1)); return res.status(401).json({ error: 'Invalid credentials' }); } - const token = jwt.sign({ userId: user.id, email: user.email }, process.env.JWT_SECRET, { expiresIn: '7d' }); - return res.status(200).json({ token }); + // Reset des Versuchscounters bei erfolgreicher Anmeldung + loginAttempts.delete(email); + + const token = generateSecureToken(user.id, user.email); + return res.status(200).json({ + token, + userId: user.id, + email: user.email + }); } catch (err) { console.error('Login error:', err); return res.status(500).json({ error: 'Login failed' });