- Add security headers middleware (CSP, HSTS, X-Frame-Options, etc.) - Add rate limiting (100 req/15min public, 5 req/min forms) - Add input validation and sanitization middleware - Add response sanitization (hide stack traces, remove sensitive fields) - Add centralized security event logging to audit trail - Disable CSRF (deprecated package, will implement modern solution in Phase 3) - Update security logger to use HOME-based log path Implements: inst_041, inst_042, inst_043, inst_044, inst_045, inst_046 Refs: docs/plans/security-implementation-roadmap.md
72 lines
2.3 KiB
JavaScript
72 lines
2.3 KiB
JavaScript
/**
|
|
* Security Event Logger (inst_046 - Quick Win Version)
|
|
* Centralized logging for all security events
|
|
*
|
|
* QUICK WIN: Simple file-based logging with JSON format
|
|
* Full version in Phase 5 will add ProtonMail/Signal alerts
|
|
*/
|
|
|
|
const fs = require('fs').promises;
|
|
const path = require('path');
|
|
|
|
const SECURITY_LOG_PATH = process.env.SECURITY_LOG_PATH ||
|
|
(process.env.HOME ? `${process.env.HOME}/var/log/tractatus/security-audit.log` : '/var/log/tractatus/security-audit.log');
|
|
|
|
/**
|
|
* Log a security event to audit trail
|
|
*
|
|
* @param {Object} event - Security event details
|
|
* @param {string} event.type - Event type (e.g., 'rate_limit_violation')
|
|
* @param {string} event.sourceIp - Source IP address
|
|
* @param {string} event.userId - User ID (if authenticated)
|
|
* @param {string} event.endpoint - Request endpoint
|
|
* @param {string} event.userAgent - User agent string
|
|
* @param {Object} event.details - Additional event details
|
|
* @param {string} event.action - Action taken (e.g., 'blocked', 'logged')
|
|
* @param {string} event.severity - Severity level ('low', 'medium', 'high', 'critical')
|
|
*/
|
|
async function logSecurityEvent(event) {
|
|
const logEntry = {
|
|
timestamp: new Date().toISOString(),
|
|
event_type: event.type || 'unknown',
|
|
source_ip: event.sourceIp || 'unknown',
|
|
user_id: event.userId || 'anonymous',
|
|
endpoint: event.endpoint || 'unknown',
|
|
user_agent: event.userAgent || 'unknown',
|
|
violation_details: event.details || {},
|
|
action_taken: event.action || 'logged',
|
|
severity: event.severity || 'medium'
|
|
};
|
|
|
|
const logLine = JSON.stringify(logEntry) + '\n';
|
|
|
|
try {
|
|
// Ensure log directory exists
|
|
const logDir = path.dirname(SECURITY_LOG_PATH);
|
|
await fs.mkdir(logDir, { recursive: true, mode: 0o750 });
|
|
|
|
// Append to log file
|
|
await fs.appendFile(SECURITY_LOG_PATH, logLine, { encoding: 'utf-8' });
|
|
} catch (error) {
|
|
// Fallback to console if file logging fails
|
|
console.error('[SECURITY LOGGER ERROR]', error.message);
|
|
console.error('[SECURITY EVENT]', logEntry);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Helper: Extract client IP from request (handles proxies)
|
|
*/
|
|
function getClientIp(req) {
|
|
return (
|
|
req.ip ||
|
|
req.headers['x-forwarded-for']?.split(',')[0]?.trim() ||
|
|
req.connection.remoteAddress ||
|
|
'unknown'
|
|
);
|
|
}
|
|
|
|
module.exports = {
|
|
logSecurityEvent,
|
|
getClientIp
|
|
};
|