3、日志标准化:日志格式规范(CEF、LEEF、JSON)、字段映射与清洗、时间戳归一化处理
好,咱们进入第三个话题。日志标准化。
说实话,我见过太多安全团队,日志收了一堆,结果告警全是垃圾。为什么?因为日志格式不统一。你想想看,防火墙用 Syslog,Windows 用 EventLog,Web 服务器用 JSON,数据库又用 CSV……这些日志堆在一起,就像把中文、英文、阿拉伯文混在一起写报告,根本没法看。
所以,标准化是日志分析的第一步,也是最重要的一步。这一步做不好,后面所有分析都是空中楼阁。
3.1 日志格式规范:CEF、LEEF、JSON
目前业界主流的日志格式有三种:CEF、LEEF 和 JSON。我一个个说。
3.1.1 CEF(Common Event Format)
CEF 是 ArcSight 搞出来的格式。说白了,它就是一种键值对格式,但结构固定。长这样:
CEF:0|Security|ThreatManager|1.0|100|Detected malware|10|src=10.0.0.1 dst=10.0.0.2 msg=Malware found
看到没?竖线分隔的是固定字段,后面是扩展字段。固定字段包括设备厂商、设备产品、版本、事件ID、事件名称、严重级别。扩展字段就是自定义的键值对。
我个人习惯用 CEF 来处理老牌安全设备,比如防火墙、IDS/IPS。因为这些设备厂商基本都支持 CEF,兼容性最好。
3.1.2 LEEF(Log Event Extended Format)
LEEF 是 IBM 的 QRadar 推的格式。它跟 CEF 很像,但结构稍微不同:
LEEF:1.0|IBM|QRadar|7.0|100|devTime=2024-01-15T10:30:00Z src=10.0.0.1 dst=10.0.0.2
LEEF 的固定字段更少,主要靠扩展字段。它的优势在于时间戳处理更灵活,支持多种时间格式。
我记得有一次做项目,客户用的是 QRadar,但日志源是自家开发的系统。我建议他们直接输出 LEEF 格式,省去了大量解析工作。嗯,这就是选对格式的好处。
3.1.3 JSON
JSON 是目前最流行的格式。为什么?因为灵活、可读性强、解析方便。几乎所有现代应用都支持 JSON。
{
"event_id": "100",
"event_name": "Detected malware",
"severity": 10,
"src_ip": "10.0.0.1",
"dst_ip": "10.0.0.2",
"timestamp": "2024-01-15T10:30:00Z"
}
JSON 的好处是嵌套结构。你可以把关联信息放在一起,比如把用户信息和操作信息放在同一个对象里。这在做关联分析时特别方便。
3.2 字段映射与清洗
日志格式统一了,接下来就是字段映射。说白了,就是把不同来源的日志字段,映射到统一的字段名上。
举个例子:
- 防火墙的源IP字段叫
src_ip - Windows 事件日志的源IP字段叫
IpAddress - Web 服务器的源IP字段叫
remote_addr
如果不做映射,你写查询的时候就得记一堆字段名,累不累?
我建议的做法是:
- 定义统一字段模型:比如所有源IP都叫
src_ip,所有目标IP都叫dst_ip,所有时间戳都叫timestamp。 - 写解析规则:用 Grok、正则或者 Logstash filter 把原始字段映射到统一字段。
- 清洗脏数据:比如去掉空值、修正格式错误、过滤掉无意义的数据。
这里我分享一个我常用的 Logstash 配置片段:
filter {
if [type] == "firewall" {
mutate {
rename => { "source_ip" => "src_ip" }
rename => { "destination_ip" => "dst_ip" }
}
}
if [type] == "windows" {
mutate {
rename => { "IpAddress" => "src_ip" }
}
}
# 清洗:去掉空值
if [src_ip] == "" or [src_ip] == "-" {
drop {}
}
}
你看,逻辑很简单。但实际项目中,字段映射往往是最耗时的环节。我曾经花了两周时间,就为了把 30 多种日志源的字段统一起来。嗯,那段时间真是噩梦。
3.3 时间戳归一化处理
时间戳,这是日志标准化里最坑的地方。没有之一。
你想想看:
- 防火墙用
Jan 15 10:30:00 - Windows 用
2024-01-15T10:30:00.123Z - Web 服务器用
1705300200(Unix 时间戳) - 数据库用
2024-01-15 10:30:00
这些时间格式混在一起,你怎么做时间排序?怎么做关联分析?
所以,时间戳归一化是必须的。我推荐的做法是:
- 统一转为 ISO 8601 格式:比如
2024-01-15T10:30:00Z。这个格式可读性好,而且支持时区。 - 保留原始时间戳:不要直接覆盖原始字段,而是新建一个
@timestamp字段。 - 处理时区问题:所有时间统一转为 UTC,避免时区混乱。
这里我给出一个 Logstash 的时间戳处理示例:
filter {
date {
match => ["original_timestamp", "ISO8601", "UNIX", "MMM dd HH:mm:ss"]
target => "@timestamp"
timezone => "UTC"
}
}
你看,用 date 插件,指定多种匹配格式,Logstash 会自动尝试解析。解析成功后,会把结果写入 @timestamp 字段。
2024-01-15 10:30:00,但没带时区。结果 Logstash 默认当成 UTC,而实际是东八区。所有日志都差了 8 小时。后来我加了一行 timezone => "Asia/Shanghai" 才解决。所以,时间戳一定要明确时区,否则后患无穷。
另外,还有一个常见问题:日志延迟。比如事件发生在 10:00,但日志在 10:05 才到达。这时候,@timestamp 应该用事件时间,而不是接收时间。否则你分析的时候会发现,10:05 的日志里混着 10:00 的事件,乱套了。
嗯,时间戳归一化,说白了就是「统一格式、统一时区、统一字段」。这三统一做到了,后面的分析才能靠谱。
小结
日志标准化,听起来简单,做起来全是坑。但只要你把格式规范、字段映射、时间戳归一化这三件事做好,后面的异常行为挖掘就会顺畅很多。
我个人经验是:标准化工作至少占整个日志分析项目的 40% 时间。别嫌多,这一步省了,后面十倍百倍地还回来。
好,这一节就到这里。下一节我们聊聊日志采集架构,看看怎么把标准化后的日志高效地收上来。