Financial regulators, PCI-DSS Req 10.2, and NIST 800-63B (AU-2/AU-9) require that session lifecycle events — logins, logouts, timeouts, and extensions — be recorded in a tamper-resistant audit trail. Without immutable session logs, incident response is blind: there is no way to determine whether a specific session was active during a disputed transaction, whether a concurrent session was established from an unusual IP, or whether a session was extended unexpectedly. ISO 27001:2022 A.8.15 requires log integrity. Mutable logs stored in a writable database table are trivially altered to conceal unauthorized access.
Info severity because the absence of session logging does not directly enable attack, but it removes the forensic record needed to detect, investigate, and prove unauthorized session activity after the fact.
Wire session event logging via middleware in src/middleware/sessionLogger.ts using an append-only transport:
import pino from 'pino';
const auditLog = pino({
transport: {
target: 'pino-http-send',
options: { url: 'https://log-service.example.com/audit' }
}
});
export function logSessionEvent(userId: string, event: string, details: object) {
auditLog.info({
timestamp: new Date().toISOString(),
userId,
event, // 'login' | 'logout' | 'timeout' | 'extend' | 'concurrent_warning'
...details
});
}
Capture all four required event types: login, logout, timeout, and extension. Use an append-only destination — a WORM-compliant log service, an S3 bucket with object lock, or a Postgres table with no UPDATE/DELETE grants on the audit role.
finserv-session-security.session-lifecycle.session-operations-loggedinfo"0 session event types logged — no session logging found" or "2 of 4 required event types logged (login, logout only — timeout and extension missing)"src/middleware/sessionLogger.ts):
// middleware/sessionLogger.ts
import pino from 'pino';
const auditLog = pino({
transport: {
target: 'pino-http-send',
options: { url: 'https://log-service.example.com/audit' } // Or append-only file
}
});
export function logSessionEvent(userId: string, event: string, details: any) {
auditLog.info({
timestamp: new Date().toISOString(),
userId,
event, // 'login', 'logout', 'timeout', 'extend'
...details
});
}