Dieser Commit ist enthalten in:
Claude Project Manager
2025-12-28 21:36:45 +00:00
Commit ab1e5be9a9
146 geänderte Dateien mit 65525 neuen und 0 gelöschten Zeilen

125
backend/middleware/csrf.js Normale Datei
Datei anzeigen

@ -0,0 +1,125 @@
/**
* TASKMATE - CSRF Schutz
* ======================
* Cross-Site Request Forgery Schutz
*
* Vereinfachtes System: Token wird beim Login generiert und bleibt
* für die gesamte Sitzung gültig (24 Stunden).
*/
const crypto = require('crypto');
const logger = require('../utils/logger');
// CSRF-Tokens speichern (in-memory, pro User)
const csrfTokens = new Map();
// Token-Gültigkeit: 24 Stunden
const TOKEN_VALIDITY = 24 * 60 * 60 * 1000;
/**
* CSRF-Token generieren
*/
function generateToken(userId) {
const token = crypto.randomBytes(32).toString('hex');
const expires = Date.now() + TOKEN_VALIDITY;
csrfTokens.set(userId, { token, expires });
cleanupExpiredTokens();
return token;
}
/**
* CSRF-Token validieren
*/
function validateToken(userId, token) {
const stored = csrfTokens.get(userId);
if (!stored) {
return false;
}
if (Date.now() > stored.expires) {
csrfTokens.delete(userId);
return false;
}
return stored.token === token;
}
/**
* Abgelaufene Tokens aufräumen
*/
function cleanupExpiredTokens() {
const now = Date.now();
for (const [userId, data] of csrfTokens.entries()) {
if (now > data.expires) {
csrfTokens.delete(userId);
}
}
}
// Regelmäßig aufräumen (alle 30 Minuten)
setInterval(cleanupExpiredTokens, 30 * 60 * 1000);
/**
* Express Middleware
*/
function csrfProtection(req, res, next) {
// GET-Anfragen sind sicher (lesen nur)
if (req.method === 'GET') {
return next();
}
// User muss authentifiziert sein
if (!req.user) {
return next();
}
const userId = req.user.id;
// Token aus Header oder Body
const token = req.headers['x-csrf-token'] || req.body?._csrf;
// Wenn kein Token vom Client gesendet oder kein Token auf Server gespeichert
if (!token || !csrfTokens.has(userId)) {
const newToken = generateToken(userId);
logger.info(`CSRF: Token missing or not stored for user ${userId}, generated new token`);
return res.status(403).json({
error: 'CSRF-Token fehlt oder abgelaufen',
csrfToken: newToken,
code: 'CSRF_ERROR'
});
}
// Token validieren
if (!validateToken(userId, token)) {
const newToken = generateToken(userId);
logger.info(`CSRF: Token mismatch for user ${userId}`);
return res.status(403).json({
error: 'Ungültiges CSRF-Token',
csrfToken: newToken,
code: 'CSRF_ERROR'
});
}
// Token ist gültig - KEIN neuer Token generiert (bleibt für die Sitzung gleich)
next();
}
/**
* Aktuellen Token für User abrufen (oder neuen generieren)
*/
function getTokenForUser(userId) {
const stored = csrfTokens.get(userId);
if (stored && Date.now() < stored.expires) {
return stored.token;
}
return generateToken(userId);
}
module.exports = csrfProtection;
module.exports.generateToken = generateToken;
module.exports.getTokenForUser = getTokenForUser;