security hardening

This commit is contained in:
Philipp committed 2026-05-21 14:09:08 +02:00
1 parent 3103aba972
commit 0046156e58
16 files changed
+205 -43

No files matched your search

+47 -1
View File
@@ -4,17 +4,43 @@ import { createAuditEvent } from '../store.js';
export const authRouter = Router();
const loginAttempts = new Map();
const maxAttempts = 5;
const windowMs = 15 * 60 * 1000;
authRouter.get('/session', (req, res) => {
const session = currentSession(req);
res.json({ authenticated: Boolean(session), user: session?.user || null });
});
authRouter.post('/login', (req, res) => {
const result = login(req.body?.username, req.body?.password);
const username = String(req.body?.username || '');
const key = loginAttemptKey(req, username);
const attempt = currentAttempt(key);
if (attempt.count >= maxAttempts) {
createAuditEvent({
action: 'login_blocked',
targetType: 'session',
targetId: username || null,
details: { ip: clientIp(req), reason: 'rate_limit' },
});
res.status(429).json({ error: 'Too many login attempts. Try again later.' });
return;
}
const result = login(username, req.body?.password);
if (!result) {
recordFailedAttempt(key);
createAuditEvent({
action: 'login_failed',
targetType: 'session',
targetId: username || null,
details: { ip: clientIp(req) },
});
res.status(401).json({ error: 'Invalid username or password.' });
return;
}
loginAttempts.delete(key);
setSessionCookie(res, result.session);
createAuditEvent({
user: result.user,
@@ -39,3 +65,23 @@ authRouter.post('/logout', (req, res) => {
clearSessionCookie(res);
res.json({ ok: true });
});
function loginAttemptKey(req, username) {
return `${clientIp(req)}:${String(username || '').toLowerCase()}`;
}
function clientIp(req) {
return String(req.ip || req.socket?.remoteAddress || '').replace(/^::ffff:/, '');
}
function currentAttempt(key) {
const now = Date.now();
const current = loginAttempts.get(key);
if (!current || current.resetAt <= now) return { count: 0, resetAt: now + windowMs };
return current;
}
function recordFailedAttempt(key) {
const attempt = currentAttempt(key);
loginAttempts.set(key, { count: attempt.count + 1, resetAt: attempt.resetAt });
}