Security Logging: Auditing and Monitoring for Security Events
In this tutorial, you will learn about Security Logging: Auditing and Monitoring for Security Events. We cover key concepts, practical examples, and best practices to help you master this topic.
Security logging records events relevant to the security of your application: authentication attempts, authorization failures, input validation errors, rate limit triggers, and suspicious activity patterns. These logs feed into monitoring systems that detect and respond to security incidents.
flowchart TB
subgraph Events
Login[Login Success/Failure]
AuthZ[Authorization Denied]
Rate[Rate Limit Triggered]
Input[Validation Error]
Admin[Admin Action]
API[API Key Usage]
end
Events --> Logger[Security Logger]
Logger -->|Structured JSON| LogAggregator[Log Aggregator]
LogAggregator --> Storage[(Elasticsearch / Loki)]
Storage --> Detection[Detection Rules]
Detection -->|Alert| SIEM[SIEM / AlertManager]
Detection -->|Dashboard| Grafana
SIEM --> Response[Incident Response]
What You'll Learn
- Security event types and what to log
- Structured logging with correlation IDs
- Log integrity and tamper prevention
- Detection rules for common attack patterns
Why It Matters
Without security logging, you cannot detect breaches in progress, investigate incidents after the fact, or meet Compliance requirements (SOC2, PCI-DSS, HIPAA). Security logs are often the only record of an attacker's actions.
Real-World Use
A SaaS platform logs every authentication event with user ID, IP, user agent, and result. Anomaly detection identifies when a user logs in from a new country within 5 minutes of a previous login from another country. This pattern detected a credential stuffing attack affecting 50 accounts.
Security Logging Implementation
Structured Security Logger
const { createLogger, format, transports } = require('winston');
const securityLogger = createLogger({
level: 'info',
format: format.combine(
format.timestamp(),
format.json()
),
defaultMeta: { service: 'api', type: 'security' },
transports: [
new transports.File({ filename: 'logs/security.log', maxSize: '100m' }),
new transports.Console({ format: format.json() })
]
});
function logSecurityEvent(eventType, details, severity = 'info') {
securityLogger.log(severity, eventType, {
eventType,
timestamp: new Date().toISOString(),
correlationId: details.correlationId,
userId: details.userId,
ip: details.ip,
userAgent: details.userAgent,
resource: details.resource,
action: details.action,
result: details.result,
reason: details.reason,
metadata: details.metadata
});
}
// Usage
app.post('/api/login', async (req, res) => {
const authResult = await authenticate(req.body);
logSecurityEvent('LOGIN_ATTEMPT', {
correlationId: req.correlationId,
userId: req.body.username,
ip: req.ip,
userAgent: req.headers['user-agent'],
action: 'login',
result: authResult.success ? 'success' : 'failure',
reason: authResult.success ? null : 'invalid_password'
});
if (!authResult.success) {
return res.status(401).json({ error: 'Invalid credentials' });
}
// Proceed with login
});
Expected output:
{"level":"info","eventType":"LOGIN_ATTEMPT","userId":"john","result":"failure","timestamp":"2026-06-28T10:30:00Z","correlationId":"abc-123"}
Security Detection Rules
class SecurityDetector {
constructor() {
this.events = [];
}
addEvent(event) {
this.events.push(event);
this.runDetectionRules(event);
}
runDetectionRules(event) {
// Brute force detection: 5+ failed logins in 60 seconds
if (event.eventType === 'LOGIN_ATTEMPT' && event.result === 'failure') {
const recentFailures = this.events.filter(e =>
e.eventType === 'LOGIN_ATTEMPT' &&
e.result === 'failure' &&
(e.userId === event.userId || e.ip === event.ip) &&
Date.now() - new Date(e.timestamp).getTime() < 60000
);
if (recentFailures.length >= 5) {
this.triggerAlert('BRUTE_FORCE_DETECTED', {
target: event.userId,
ip: event.ip,
attempts: recentFailures.length,
window: '60s'
});
}
}
// Impossible travel: same user from different countries within 5 minutes
if (event.eventType === 'LOGIN_ATTEMPT' && event.result === 'success') {
const previous = this.events.filter(e =>
e.eventType === 'LOGIN_ATTEMPT' &&
e.result === 'success' &&
e.userId === event.userId &&
Date.now() - new Date(e.timestamp).getTime() < 300000
);
if (previous.length > 0 && previous[previous.length - 1].ip !== event.ip) {
this.triggerAlert('IMPOSSIBLE_TRAVEL', {
userId: event.userId,
previousIp: previous[previous.length - 1].ip,
newIp: event.ip,
timeDiff: Date.now() - new Date(previous[previous.length - 1].timestamp).getTime()
});
}
}
}
triggerAlert(alertType, details) {
console.error(`SECURITY ALERT: ${alertType}`, JSON.stringify(details));
// Send to SIEM / PagerDuty / Slack
}
}
Expected output:
SECURITY ALERT: BRUTE_FORCE_DETECTED {"target":"john","ip":"192.168.1.100","attempts":5}
SECURITY ALERT: IMPOSSIBLE_TRAVEL {"userId":"jane","previousIp":"US IP","newIp":"CN IP"}
Audit Trail Middleware
function auditTrail(action) {
return async (req, res, next) => {
const originalSend = res.json.bind(res);
const startTime = Date.now();
res.json = function(body) {
logSecurityEvent('AUDIT_' + action.toUpperCase(), {
correlationId: req.correlationId,
userId: req.user?.sub,
ip: req.ip,
action,
resource: req.originalUrl,
method: req.method,
statusCode: res.statusCode,
responseTime: Date.now() - startTime,
changes: body
});
return originalSend(body);
};
next();
};
}
// Usage
app.post('/api/users', auditTrail('create_user'), async (req, res) => {
const user = await createUser(req.body);
res.status(201).json(user);
});
Expected output:
On user creation, audit log entry includes who created the user, when, from which IP, and the created user data.
Common Mistakes
- Logging sensitive data (passwords, tokens, PII) in plaintext — mask or exclude sensitive fields.
- Not including correlation IDs — without correlation, you cannot trace a request across services.
- Using unstructured (plain text) logs instead of structured (JSON) logs — structured logs are searchable and parseable.
- Not monitoring logs in real-time — logs stored but never reviewed provide no security value.
- Allowing log tampering — logs should be append-only and shipped to a separate, immutable storage.
Practice Questions
- What security events should be logged?
- Why is structured logging important for security?
- What is a correlation ID and why is it useful?
- How do you detect a brute force attack from logs?
- What is the difference between logging and monitoring?
Challenge
Design a security logging system for a banking API. Define 10 security event types. Implement detection rules for: brute force login, impossible travel, unusual API key usage, and admin action auditing. Ship logs to a central location and create a Grafana dashboard.
FAQ
Mini Project
Build a security logging module for your API. Log all authentication attempts, authorization failures, and admin actions. Implement detection rules for brute force and impossible travel. Ship logs to a local ELK Stack. Create a dashboard showing login failure rates and top blocked IPs.
What's Next
Continue to DDoS Protection to learn about protecting your backend from distributed denial-of-service attacks.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro