Node.js日志中的安全信息解读
一 日志在安全事件中的价值与解读目标
二 关键字段与判读要点
三 常见安全事件模式与日志特征
四 日志采集与脱敏配置示例
const morgan = require('morgan');
const fs = require('fs');
const path = require('path');
// 1) 自定义脱敏令牌
morgan.token('password', (req) => (req.body && req.body.password ? '******' : '-'));
morgan.token('safe-ip', (req) => {
const ip = req.ip || '';
return ip.replace(/(\d+)\.(\d+)\.(\d+)\.(\d+)/, '$1.$2.*.*');
});
morgan.token('filtered-query', (req) => {
const q = new URLSearchParams(req.query || {});
['id','phone','email','token','ssn'].forEach(k => q.has(k) && q.set(k, '***'));
return q.toString();
});
// 2) 安全日志格式(避免 combined/common)
morgan.format('secure',
':safe-ip - :remote-user [:date[iso]] ":method :url" :status :response-time ms :filtered-query');
// 3) 目录与文件权限(仅所有者读写)
const logDir = path.join(__dirname, 'logs');
fs.mkdirSync(logDir, { mode: 0o700 });
const accessLogStream = fs.createWriteStream(path.join(logDir, 'access.log'), { flags: 'a', mode: 0o600 });
// 4) 生产环境减少噪声:跳过成功 GET
app.use(morgan('secure', {
stream: accessLogStream,
skip: (req, res) => process.env.NODE_ENV === 'production' && res.statusCode < 400 && req.method === 'GET'
}));
const { createLogger, format, transports } = require('winston');
const DailyRotateFile = require('winston-daily-rotate-file');
const logger = createLogger({
level: 'info',
format: format.combine(format.timestamp(), format.json()),
transports: [
new transports.File({ filename: 'error.log', level: 'error' }),
new DailyRotateFile({
filename: 'app-%DATE%.log',
datePattern: 'YYYY-MM-DD',
zippedArchive: true,
maxSize: '20m',
maxFiles: '14d'
})
]
});
if (process.env.NODE_ENV !== 'production') {
logger.add(new transports.Console({ format: format.simple() }));
}
五 处置与加固清单