| name | security-audit-logging |
| description | Implement comprehensive security audit logging for compliance, forensics, and SIEM integration. Use when building audit trails, compliance logging, or security monitoring systems. |
Security Audit Logging
Overview
Implement comprehensive audit logging for security events, user actions, and system changes with structured logging, retention policies, and SIEM integration.
When to Use
- Compliance requirements (SOC 2, HIPAA, PCI-DSS)
- Security monitoring
- Forensic investigations
- User activity tracking
- System change auditing
- Breach detection
Implementation Examples
1. Node.js Audit Logger
const winston = require('winston');
const { ElasticsearchTransport } = require('winston-elasticsearch');
class AuditLogger {
constructor() {
this.logger = winston.createLogger({
level: 'info',
format: winston.format.combine(
winston.format.timestamp(),
winston.format.json()
),
transports: [
new winston.transports.File({
filename: 'logs/audit.log',
maxsize: 10485760,
maxFiles: 30,
tailable: true
}),
new ElasticsearchTransport({
level: 'info',
clientOpts: {
node: process.env.ELASTICSEARCH_URL
},
index: 'security-audit'
})
]
});
}
logAuth(userId, action, success, metadata = {}) {
this.logger.info({
category: 'authentication',
userId,
action,
success,
timestamp: new Date().toISOString(),
ip: metadata.ip,
userAgent: metadata.userAgent,
location: metadata.location,
mfaUsed: metadata.mfaUsed
});
}
logAuthorization(userId, resource, action, granted, metadata = {}) {
this.logger.info({
category: 'authorization',
userId,
resource,
action,
granted,
timestamp: new Date().toISOString(),
ip: metadata.ip,
reason: metadata.reason
});
}
logDataAccess(userId, dataType, recordId, action, metadata = {}) {
this.logger.info({
category: 'data_access',
userId,
dataType,
recordId,
action,
timestamp: new Date().toISOString(),
ip: metadata.ip,
query: metadata.query,
resultCount: metadata.resultCount
});
}
logConfigChange(userId, setting, oldValue, newValue, metadata = {}) {
this.logger.info({
category: 'configuration_change',
userId,
setting,
oldValue,
newValue,
timestamp: new Date().toISOString(),
ip: metadata.ip
});
}
logSecurityEvent(eventType, severity, description, metadata = {}) {
this.logger.warn({
category: 'security_event',
eventType,
severity,
description,
timestamp: new Date().toISOString(),
...metadata
});
}
logAdminAction(adminId, action, targetUserId, metadata = {}) {
this.logger.info({
category: 'admin_action',
adminId,
action,
targetUserId,
timestamp: new Date().toISOString(),
changes: metadata.changes,
reason: metadata.reason
});
}
logAPIRequest(userId, method, endpoint, statusCode, duration, metadata = {}) {
this.logger.info({
category: 'api_request',
userId,
method,
endpoint,
statusCode,
duration,
timestamp: new Date().toISOString(),
ip: metadata.ip,
userAgent: metadata.userAgent,
requestId: metadata.requestId
});
}
}
function auditMiddleware(auditLogger) {
return (req, res, next) => {
const startTime = Date.now();
const originalSend = res.send;
res.send = function(data) {
res.send = originalSend;
const duration = Date.now() - startTime;
auditLogger.logAPIRequest(
req.user?.id || 'anonymous',
req.method,
req.path,
res.statusCode,
duration,
{
ip: req.ip,
userAgent: req.get('user-agent'),
requestId: req.id
}
);
return res.send(data);
};
next();
};
}
const auditLogger = new AuditLogger();
app.post('/api/login', async (req, res) => {
const { email, password } = req.body;
try {
const user = await authenticateUser(email, password);
auditLogger.logAuth(
user.id,
'login',
true,
{
ip: req.ip,
userAgent: req.get('user-agent'),
mfaUsed: user.mfaEnabled
}
);
res.json({ token: generateToken(user) });
} catch (error) {
auditLogger.logAuth(
email,
'login',
false,
{
ip: req.ip,
userAgent: req.get('user-agent')
}
);
res.status(401).json({ error: 'Invalid credentials' });
}
});
app.get('/api/users/:id', authorize, async (req, res) => {
const userId = req.params.id;
if (req.user.id !== userId && !req.user.isAdmin) {
auditLogger.logAuthorization(
req.user.id,
`user:${userId}`,
'read',
false,
{
ip: req.ip,
reason: 'Insufficient permissions'
}
);
return res.status(403).json({ error: 'Forbidden' });
}
const user = await getUser(userId);
auditLogger.logDataAccess(
req.user.id,
'user',
userId,
'read',
{
ip: req.ip
}
);
res.json(user);
});
module.exports = { AuditLogger, auditMiddleware };
2. Python Audit Logging System
import logging
import json
from datetime import datetime
from typing import Dict, Any, Optional
import structlog
from elasticsearch import Elasticsearch
class AuditLogger:
def __init__(self):
structlog.configure(
processors=[
structlog.processors.TimeStamper(fmt="iso"),
structlog.processors.JSONRenderer()
]
)
self.logger = structlog.get_logger()
file_handler = logging.FileHandler('logs/audit.log')
file_handler.setLevel(logging.INFO)
self.es = Elasticsearch([{'host': 'localhost', 'port': 9200}])
def _log(self, category: str, event_data: Dict[str, Any]):
"""Internal logging method"""
log_entry = {
'category': category,
'timestamp': datetime.utcnow().isoformat(),
**event_data
}
self.logger.info(json.dumps(log_entry))
try:
.es.index(
index=,
document=log_entry
)
Exception e:
()
():
._log(, {
: user_id,
: action,
: success,
: ip,
: user_agent,
**kwargs
})
():
._log(, {
: user_id,
: resource,
: action,
: granted,
: reason,
**kwargs
})
():
._log(, {
: user_id,
: data_type,
: record_id,
: action,
**kwargs
})
():
._log(, {
: event_type,
: severity,
: description,
**kwargs
})
():
._log(, {
: user_id,
: setting,
: (old_value),
: (new_value),
**kwargs
})
flask Flask, request, g
functools wraps
app = Flask(__name__)
audit_logger = AuditLogger()
():
g.request_start_time = datetime.now()
():
(g, ):
duration = (datetime.now() - g.request_start_time).total_seconds() *
audit_logger._log(, {
: (g, , ),
: request.method,
: request.path,
: response.status_code,
: duration,
: request.remote_addr,
: request.user_agent.string
})
response
():
():
():
result = f(*args, **kwargs)
audit_logger.log_data_access(
user_id=g.user_id,
data_type=data_type,
record_id=kwargs.get(, ),
action=request.method.lower(),
ip=request.remote_addr
)
result
decorated_function
decorator
():
jsonify({: user_id})
__name__ == :
app.run()
3. Java Audit Logging
package com.example.security;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.time.Instant;
import java.util.HashMap;
import java.util.Map;
public class AuditLogger {
private static final Logger logger = LoggerFactory.getLogger("AUDIT");
private static final ObjectMapper objectMapper = new ObjectMapper();
public enum Category {
AUTHENTICATION,
AUTHORIZATION,
DATA_ACCESS,
CONFIGURATION_CHANGE,
SECURITY_EVENT,
ADMIN_ACTION
}
public enum Severity {
LOW, MEDIUM, HIGH, CRITICAL
}
public void logAuth(String userId, String action, boolean success,
String ip, Map<String, Object> metadata) {
Map<String, Object> logEntry = new HashMap<>();
logEntry.put("category", Category.AUTHENTICATION);
logEntry.put("userId", userId);
logEntry.put("action", action);
logEntry.put("success", success);
logEntry.put("ip", ip);
logEntry.put(, Instant.now().toString());
logEntry.putAll(metadata);
log(logEntry);
}
{
Map<String, Object> logEntry = <>();
logEntry.put(, Category.DATA_ACCESS);
logEntry.put(, userId);
logEntry.put(, dataType);
logEntry.put(, recordId);
logEntry.put(, action);
logEntry.put(, ip);
logEntry.put(, Instant.now().toString());
log(logEntry);
}
{
Map<String, Object> logEntry = <>();
logEntry.put(, Category.SECURITY_EVENT);
logEntry.put(, eventType);
logEntry.put(, severity);
logEntry.put(, description);
logEntry.put(, Instant.now().toString());
logEntry.putAll(metadata);
log(logEntry);
}
{
{
objectMapper.writeValueAsString(logEntry);
logger.info(json);
} (Exception e) {
logger.error(, e);
}
}
}
{
AuditLogger auditLogger;
ServletException, IOException {
System.currentTimeMillis();
filterChain.doFilter(request, response);
System.currentTimeMillis() - startTime;
SecurityContextHolder.getContext()
.getAuthentication()
.getName();
Map<String, Object> metadata = <>();
metadata.put(, request.getMethod());
metadata.put(, request.getRequestURI());
metadata.put(, response.getStatus());
metadata.put(, duration);
metadata.put(, request.getHeader());
auditLogger.logAuth(userId, , ,
request.getRemoteAddr(), metadata);
}
}
Best Practices
✅ DO
- Log all security events
- Use structured logging
- Include timestamps (UTC)
- Log user context
- Implement log retention
- Encrypt sensitive logs
- Monitor log integrity
- Send to SIEM
- Include request IDs
❌ DON'T
- Log passwords/secrets
- Log sensitive PII unnecessarily
- Skip failed attempts
- Allow log tampering
- Store logs insecurely
- Ignore log analysis
Events to Log
- Authentication: Login, logout, password changes
- Authorization: Access granted/denied
- Data Access: CRUD operations
- Configuration: System changes
- Security Events: Suspicious activity
- Admin Actions: Privileged operations
Log Retention
- SOC 2: 1 year minimum
- HIPAA: 6 years
- PCI-DSS: 1 year online, 3 years archive
- GDPR: As needed for purpose
Resources