3. 审计日志格式与常见类型(syslog、应用日志等)
做审计日志巡检,第一件事是什么?不是写脚本,是搞清楚日志长什么样。你想想,如果连字段都认不全,那脚本跑出来的结果你敢信吗?
我个人习惯拿到一台机器 先看 `/var/log/` 下有什么文件,然后 `head -5` 瞄一眼结构。日志格式说白了就几大类,但每一类里的坑都不少。
3.1 Syslog:老而弥坚的系统日志标准
Syslog 是从 Unix 时代就有的协议,至今仍是系统日志的基石。大部分 Linux 发行版里,`/var/log/syslog` 或 `/var/log/messages` 用的就是它。
标准 syslog 格式(RFC 3164)长这样:
<34>Oct 11 22:14:15 mymachine su: 'su root' failed for lonvick on /dev/pts/8
拆开看:尖括号里的数字是优先级编码(设施×8 + 级别),后面是时间戳、主机名、进程名和消息。我刚开始接触时总搞不清那个数字怎么算,后来写了个小函数才记住。
新版的 RFC 5424 增加了结构化数据,格式更规整:
<14>1 2024-10-11T22:14:15.003Z mymachine.example.com su 1234 ID47 [exampleSDID@32473 iut="3" eventSource="Application"] BOM'su root' failed for lonvick
嗯,这里要注意:很多老设备还走旧版格式。你的巡检脚本如果只按新版解析,遇到旧版就全废了。
| 特性 | RFC 3164(旧版) | RFC 5424(新版) |
|---|---|---|
| 时间戳 | Oct 11 22:14:15(无年份) | 2024-10-11T22:14:15.003Z(含时区) |
| 优先级 | 尖括号内数字 | 同上,但可省略 |
| 结构化数据 | 无 | [exampleSDID@32473 …] 键值对 |
| 设备兼容 | 几乎所有网络设备 | 较新设备及软件 |
3.2 应用日志:百花齐放,但总有规律
应用日志就没那么统一了。每个中间件、每个框架都有自己的脾气。不过万变不离其宗,常见格式也就那么几种。
- Web 服务器(Apache / Nginx):最流行的组合日志(Combined Log Format),一行一个请求。
- Java 应用(Log4j / Logback):常用 PatternLayout,可自定义,但多数包含时间、级别、线程、Logger、消息。
- JSON 日志:近年趋势,结构化程度最高。每条日志用大括号包裹,键值对清晰。
- 自定义文本日志:嗯,你说它乱吧,它又有固定的分隔符;你说它规范吧,字段排列经常变。我在一个运维项目里见过用 `||` 分隔的、用 `^A`(Control A)分隔的……
拿最常见的 Nginx 日志举例:
192.168.1.1 - - [11/Oct/2024:22:14:15 +0800] "GET /api/health HTTP/1.1" 200 1234 "-" "curl/7.68.0"
字段依次是:客户端IP、标识符(通常 `-`)、用户、时间、请求行、状态码、字节数、Referer、User-Agent。这个格式我敢打赌你见过,因为它太普遍了。
3.3 常见字段与它们的“性格”
不管日志长什么样,总有一些核心字段会反复出现。我把它们归纳成三组:
| 字段组 | 包含字段 | 在审计中的意义 |
|---|---|---|
| 时间相关 | 时间戳(精确到秒或毫秒)、时区 | 排序、频率分析、异常检测的时间基线 |
| 身份相关 | IP、用户名、进程名、PID | 追溯责任人、定位异常源 |
| 行为相关 | 操作、结果码、消息 | 判断成功/失败、识别违规操作 |
例如,你在 SSH 登录日志里看到 `Failed password for root from 10.0.0.5 port 22`,就可以提取 `root`、`10.0.0.5`、`Failed` 三个关键信息。
3.4 解析策略:面对混乱,我怎么做?
日志格式再怎么变,解析思路是死的。我总结了三个步骤:
- 观察样本 —— 取 10~20 条日志,肉眼确认分隔符、固定位置、可变部分。
- 选择解析器 —— 结构化(JSON/XML)用 `jq`;带时间戳的文本用 `awk` + `date`;复杂混合型写 Python/Ruby 小脚本。
- 处理异常 —— 比如某行被截断了,或字段里出现了意料之外的分隔符。我总会加一个“错误行计数”和“样本存储”,便于事后排查。
# 示例:用 awk 提取 syslog 中的时间、主机和消息
awk '{
# syslog 典型:月 日 时:分:秒 主机 进程[PID]: 消息
# 我们取前三个字段作为时间,第四字段主机,从第五字段开始为消息
timestamp = $1 " " $2 " " $3
hostname = $4
message = substr($0, index($0, $5))
print timestamp, hostname, message
}' /var/log/syslog | head -5
当然,这只是一个极简示范。真实场景里我会用 `grep -E` 先过滤,再用 `sed` 或 `perl` 做精细切割。
3.5 一张图看懂审计日志格式分类
下面这张图我把常见审计日志格式做了一个体系梳理。你可以用它作为自己巡检脚本的“格式速查”:
3.6 解析时的几个注意事项
- 时区统一:各日志可能用 UTC、本地时间、甚至不带时区。我建议巡检脚本强制将所有时间转为 UTC 存储,避免排序和比较出幺蛾子。
- 跨行日志:比如 Java 的堆栈信息占了多行。你用 `awk` 按行处理就会断掉。我一般先用 `sed` 把换行逻辑合并——例如遇到不以时间戳开头的行,就追加到前一行末尾。
- 编码问题:有些老应用输出 GBK 或 Latin-1。我的做法是在脚本头统一检测 BOM 或 `file -i`,发现非 UTF‑8 就 `iconv -f gbk -t utf8` 转一下。
搞清楚了日志格式,下一步就是把这些规则编进巡检脚本里了。格式越清晰,你的脚本越强健。别怕一开始多花时间观察样本——我见过太多人上来就写逻辑,结果被一行格式异常打回原形。你想想,是不是这个理?