第二章 审计日志的构成与格式
做数据库审计这么多年,我最深的感受是:看不懂日志,就甭想做好检测。很多新手上来就堆算法,结果连日志里某个字段是啥意思都没搞清,闹出乌龙是常事。
审计日志说白了就是数据库的“黑匣子”。谁、在什么时间、通过哪个IP、干了什么事、对哪些对象动了手——全记下来。咱们这一章就掰开这些字段,看看它们到底长什么样,有什么坑。
2.1 核心字段一览
不同的数据库(MySQL、PostgreSQL、Oracle、SQL Server)审计日志格式略有差异,但核心字段几乎一致。我列了张表,方便你对照。
| 字段 | 含义 | 常见示例 |
|---|---|---|
| 时间戳 | 事件发生的精确时间 | 2025-03-18 14:32:15.732 |
| 用户名 | 发起操作的数据库账号 | app_user, root |
| 操作类型 | 具体执行的SQL命令类别 | SELECT, DELETE, CREATE |
| 对象 | 被操作的库、表、视图、存储过程等 | test_db.orders |
| 来源IP | 客户端IP地址 | 10.0.1.45, 192.168.1.100 |
| 会话ID | 数据库连接的唯一标识 | 1234567 |
| 返回码 | 操作执行结果(成功/失败/错误代码) | 0, 1146 (表不存在) |
| 执行耗时 | SQL执行所花时间 | 0.023s, 12.5s |
2.2 时间戳:时区问题是第一大坑
时间戳看似简单,但我在项目里被它坑过不止一次。有一次一个告警系统凌晨三点狂报警,查了半天才发现是数据库服务器用UTC,而日志采集系统用CST,两边的时区差8小时。那个凌晨,我对着屏幕把头发抓掉不少。
需要注意的点:
- 确认时区:日志中是否明确标注时区?比如
2025-03-18T06:32:15Z中的Z表示UTC。 - 精度:毫秒还是微秒?有些审计日志只精确到秒,这种在短时间内大量操作时根本分不清先后顺序。
- 时钟同步:多节点日志如果存在时钟偏差,关联分析会得出错误结论。建议NTP统一对时。
还有一点很容易被忽略——时间戳的格式。有的数据库用UNIX时间戳(长整数),有的用ISO8601字符串。如果你用正则或者字符串函数解析,一定要注意格式统一。
2.3 用户与身份映射
用户字段记录的是数据库账号名。但你想想看,很多时候应用层用的是同一个数据库连接池,日志里看到的全是webapp这个账号。这时候你就需要额外的信息来映射到具体操作人。
怎么办呢?我建议关注这些:
- 客户端程序名称:有些数据库审计能记录
application_name(PostgreSQL)或program_name(SQL Server)。 - 会话绑定变量:如果应用层在连接时注入了用户ID,可以从
SET session user_id = 'xxx'这类语句中提取。 - 代理用户:部分数据库支持代理认证(如Oracle),这时记录的是代理用户而非最终用户。
2.4 操作类型:别只看“增删改查”
操作类型一般分几大类:DML(SELECT、INSERT、UPDATE、DELETE)、DDL(CREATE、ALTER、DROP)、DCL(GRANT、REVOKE)、TCL(BEGIN、COMMIT、ROLLBACK),还有管理操作(KILL、SHUTDOWN、BACKUP)等。
但这里有个细节:同样的操作在不同的数据库里分类可能不同。比如TRUNCATE在MySQL里是DDL,在Oracle里算DML?反正我们做异常检测时,最好把操作类型归一化成自己的分类体系。
我通常会这样分类:
| 类别 | 包含操作 | 异常关注度 |
|---|---|---|
| 高危险 | DROP, ALTER, GRANT, REVOKE, KILL, SHUTDOWN | 极高 |
| 中危险 | DELETE, UPDATE without WHERE, TRUNCATE | 中 |
| 低危险 | SELECT, INSERT, BEGIN, COMMIT | 按频率与上下文判断 |
2.5 对象:表和字段的完整路径
对象字段通常记录数据库.表或数据库.模式.表的完整路径。有时候还包括字段名、索引名甚至分区名。我见过最长的对象名包含四级:prod.sales_db.dbo.order_items.PK_orders。
做异常检测时,对象的层次信息非常有用。比如你发现某个用户突然访问了一个从没访问过的敏感表,哪怕操作类型只是SELECT,也值得标记。
另外,注意对象别名的解析。有些日志直接记录FROM t这种SQL文本,而没有解析出真实的表名t到底是什么。这时候需要SQL解析器来还原。
2.6 来源IP:网络层溯源
来源IP记录的是客户端的IP地址。看起来直白,但实际场景里常遇到:
- NAT/代理:应用服务器通过中间件连接数据库,日志里全是中间件的IP,看不到真实客户端。
- 容器/Pod:K8s环境下Pod IP会频繁变化,需要结合Pod名称或Service名称做映射。
- VPN:跨地域访问时来源IP可能来自VPN网关,而不是实际用户所在城市。
所以我建议,在采集阶段就做IP富化——把IP转换成内网段标识、地理位置、主机名等信息。这样检测时才能更快定位问题。
2.7 一条完整的审计日志长啥样
下面给一个MySQL审计日志的示例(实际格式可能因插件而异):
2025-03-18T14:32:15.732000Z,
connect,
root@10.0.1.45,
1234567,
0,
0,
0,
0,
0,
0,
'CONNECT',
'root',
'10.0.1.45',
'test_db',
'',
'',
'',
'',
'',
'',
'',
'',
'',
'',
'',
'',
'',
0
嗯,字段确实很多,而且顺序固定。我们做异常检测时一般只取前几个关键字段,剩下的可以按需解析。
2.8 知识体系图
下面的SVG图总结了审计日志的核心字段及它们之间的关系,你可以把它当作“日志结构记忆卡”。