如何在Node.js的Bunyan日志中排除敏感数据(密码、CVV、PIN等)
Hey there! Great question—keeping sensitive data like passwords, CVV codes, or PINs out of your logs is critical for security and compliance. Let’s break down the best ways to handle this with Bunyan in Node.js:
Bunyan relies on serializers to format objects like req, res, or err into log-friendly JSON. You can override the default serializers to redact sensitive fields before they hit the logs.
First, define a list of sensitive fields you want to hide, then create a custom serializer that sanitizes those fields:
const bunyan = require('bunyan'); // List all sensitive fields you need to redact const sensitiveFields = ['password', 'cvv', 'pin', 'creditCardNumber']; // Custom serializers with sanitization const customSerializers = { req: (req) => { if (!req) return req; // Create a copy of the request object to avoid modifying the original const sanitizedReq = { ...req }; // Sanitize request body if (sanitizedReq.body) { sensitiveFields.forEach(field => { if (sanitizedReq.body[field]) { sanitizedReq.body[field] = '[REDACTED]'; } }); } // Sanitize query parameters (if needed) if (sanitizedReq.query) { sensitiveFields.forEach(field => { if (sanitizedReq.query[field]) { sanitizedReq.query[field] = '[REDACTED]'; } }); } // Fall back to Bunyan's default req serializer for other fields return bunyan.stdSerializers.req(sanitizedReq); }, // Keep the default error serializer unless you need to redact sensitive data there too err: bunyan.stdSerializers.err }; // Initialize your logger with the custom serializers const logger = bunyan.createLogger({ name: 'my-secure-app', serializers: customSerializers, level: 'info' }); // Test it out! logger.info({ req: { method: 'POST', url: '/api/payment', body: { userId: '12345', creditCardNumber: '4111-1111-1111-1111', cvv: '123', amount: 99.99 } } }, 'Payment request received');
If you want to redact sensitive fields across all log entries (not just specific objects like req), you can use a transform function on your Bunyan stream. This lets you modify the log record right before it’s written:
const bunyan = require('bunyan'); const sensitiveFields = ['password', 'cvv', 'pin']; // Recursive function to sanitize nested objects function sanitizeData(data) { if (typeof data !== 'object' || data === null) return data; const sanitized = { ...data }; // Redact top-level sensitive fields sensitiveFields.forEach(field => { if (sanitized[field]) { sanitized[field] = '[REDACTED]'; } }); // Recursively sanitize nested objects Object.keys(sanitized).forEach(key => { sanitized[key] = sanitizeData(sanitized[key]); }); return sanitized; } const logger = bunyan.createLogger({ name: 'my-app', streams: [{ level: 'info', stream: process.stdout, // Transform the log record before output transform: (record) => { // Sanitize any data attached to the record if (record.data) { record.data = sanitizeData(record.data); } // You can also sanitize other parts of the record if needed return record; } }] }); // Test nested data logger.info({ data: { user: { name: 'Jane Doe', password: 'mysecretpassword', paymentDetails: { pin: '4567', balance: 500 } } } }, 'User profile updated');
If you’re using a web framework like Express, you can add middleware to sanitize request data before it gets logged. This ensures you never accidentally log sensitive fields in route handlers:
const express = require('express'); const bunyan = require('bunyan'); const app = express(); app.use(express.json()); const sensitiveFields = ['password', 'cvv', 'pin']; const logger = bunyan.createLogger({ name: 'express-app' }); // Middleware to sanitize request body app.use((req, res, next) => { // Create a sanitized copy of the body req.sanitizedBody = { ...req.body }; sensitiveFields.forEach(field => { if (req.sanitizedBody[field]) { req.sanitizedBody[field] = '[REDACTED]'; } }); next(); }); // Example route app.post('/login', (req, res) => { // Use req.sanitizedBody in logs, not the original req.body logger.info({ body: req.sanitizedBody }, 'Login attempt'); // Your authentication logic here (use original req.body for validation) res.send('Login processed'); }); app.listen(3000);
- Never modify the original request/response object: Always work with copies to avoid breaking your application logic.
- Recursively sanitize nested objects: Sensitive data might be hidden in deep structures (like
user.paymentDetails.pin). - Keep your sensitive fields list updated: Add new fields as your application evolves (e.g.,
socialSecurityNumber). - Test thoroughly: Check your logs to confirm no sensitive data is leaking through.
内容的提问来源于stack exchange,提问作者Sabbir Sayed

