审计策略设计:到底该盯住哪些操作
我做了这么多年的数据库安全项目,最常被问到的问题就是:「审计到底要开哪些?」
很多团队一上来就全量审计,结果业务没跑几天,磁盘直接报警,DBA 差点被运维同事追着打。另一种极端是什么都不开,出了事才发现连谁删的表都查不到。
说白了,审计策略设计的核心就一句话:在安全需求和性能开销之间找到那个平衡点。
本章目标:帮你理清「该盯哪些操作、怎么配置更省资源」,我给出一套我在生产环境摸爬滚打后沉淀下来的策略框架。
4.1 审计维度:你得从哪几个角度去盯
设计审计策略之前,我先给你一个思考框架。你想想看,一次数据库操作可以拆成五个维度:
| 维度 | 审计什么 | 典型例子 |
|---|---|---|
| 用户 | 谁在操作 | DBA、应用账号、report_user |
| 对象 | 操作了什么 | user表、salary列、存储过程p_export |
| 操作 | 干了什么 | SELECT、DELETE、GRANT、ALTER |
| 结果 | 成功还是失败 | 0 rows、Error 1142、Timeout |
| 时间 | 什么时候干的 | 凌晨2点、下班后、节假日 |
我个人的习惯是:你用这五个维度画一张表,再决定要不要针对某个「维度组合」开审计。举个例子:
- 敏感操作 = 用户
DBA+ 操作DROP+ 对象payments→ 必须审计 - 常规查询 = 用户
app_user+ 操作SELECT+ 结果成功→ 可以不审
4.2 审计哪些操作:清单来了
下面这份清单是我在几个金融项目里反复磨合出来的,拿给你参考。记住,这是「建议必审项」,不是「全量审计项」。
4.2.1 DDL —— 结构变更必须盯死
为什么要审 DDL?
我记得有一次,开发同事在生产库上直接跑了个 ALTER TABLE 改字段类型,结果业务中断了半小时。如果不是我们提前开了 DDL 审计,连谁改的都不知道。
-- MySQL 配置示例:审计所有 DDL
SET GLOBAL audit_log_policy = 'ALL';
-- 更细粒度的做法:
SET GLOBAL audit_log_include_accounts = 'root@localhost,dev@%';
| 操作类型 | 审计必要性 | 为什么 |
|---|---|---|
| CREATE / ALTER / DROP | ★★★★★ | 改结构可能导致服务异常或数据丢失 |
| TRUNCATE | ★★★★★ | 删表内容还留表结构,容易引起混淆 |
| RENAME | ★★★★☆ | 表名一改,业务代码可能直接报错 |
4.2.2 DML —— 权限越高,审计越严
这里要分人看。普通用户的 SELECT 你审不过来,也没必要。但高权限账号的 DML 操作你必须盯住。
我自己的经验是:凡是拥有 SUPER 或 DBA 权限的账号,它的 DELETE、UPDATE、INSERT 全量审计。普通应用账号只审失败的操作,因为失败往往意味着越权尝试。
-- MariaDB 审计插件:只审计高权限用户
INSTALL SONAME 'server_audit';
SET GLOBAL server_audit_events = 'CONNECT,QUERY_DDL,QUERY_DML';
SET GLOBAL server_audit_incl_users = 'dba_admin,ops_root';
4.2.3 DCL —— 权限变更零容忍
这一块没什么好商量的:所有 GRANT、REVOKE、CREATE USER、ALTER USER 全部审计。
为什么?权限变更意味着安全的边界在动。稍微改错一个 GRANT,等于给攻击者开了扇门。我曾经在一个政府项目里排查过一起「某账号突然多出来 DELETE 权限」的事故,要不是有审计日志,根本还原不出是谁半夜两点执行了 GRANT DELETE ON *.* TO 'backup'。
重要提醒: 审计本身的配置操作也要审计。也就是说,SET GLOBAL audit_log_xxx 这类语句你得想办法记录。否则攻击者先关审计再搞事,日志就断了。
4.3 配置策略的三步法
好,现在你知道该审哪些操作了。下一步是怎么配置。我习惯分成三步走:
- 选工具:根据你的数据库类型选择审计方案。MySQL 官方有 audit_log 插件,MariaDB 有 server_audit,PostgreSQL 用 pgaudit。别自己造轮子。
- 分层设策略:把表按照敏感度分三层:
- 核心层(金融、用户信息、订单):所有 DDL + 所有 DML 失败 + 所有 DCL → 全量审计
- 业务层(商品、库存、日志表):DDL + DCL + 高权限用户的 DML → 半量审计
- 临时层(缓存、临时表):只审 DDL 和 DCL → 按需审计
- 定存储和轮转:审计日志不能一直堆积。设置每天或每 6 小时轮转一次,自动归档到专用审计服务器或对象存储。
我的小技巧: 把审计日志写到独立的磁盘或文件系统上。一方面避免日志挤占数据盘的 IO,另一方面防止攻击者删了日志的同时也影响到数据库。物理隔离就是多一份安全感。
4.4 完整策略示例(拿 MySQL 说事)
下面是一套我在某互联网公司落地的策略:
-- 1. 开启审计,只记录连接和查询
SET GLOBAL audit_log_policy = 'ALL';
-- 2. 指定审计账号
SET GLOBAL audit_log_include_accounts = 'dba@%,ops@%,dev_lead@%';
-- 3. 排除某些账号的常规操作(减少日志量)
SET GLOBAL audit_log_exclude_accounts = 'report_read@%,app_read@%';
-- 4. 记录 SQL 文本(方便事后复盘)
SET GLOBAL audit_log_format = 'NEW';
SET GLOBAL audit_log_statement = 1;
-- 5. 日志输出到 JSON 文件,方便解析
SET GLOBAL audit_log_file = '/var/log/mysql/audit/audit.log';
SET GLOBAL audit_log_rotate_on_size = 1073741824; -- 1GB 轮转一次
嗯,这里要注意:在生产环境改这些配置最好加一句 PERSIST,否则重启后策略就丢了。
-- MySQL 8.0 持久化配置
SET PERSIST audit_log_policy = 'ALL';
SET PERSIST audit_log_rotate_on_size = 1073741824;
4.5 避坑指南(都是真金白银换来的)
我做过的项目多了,积累了一些教训。现在分享给你:
- 千万别审计所有 SELECT。一个日活百万的应用,每秒可能产生几千条 SELECT,你全记下来,磁盘扛不了一天。
- 审计失败操作比审计成功操作更有价值。我曾经发现一个内部员工不断尝试
SELECT password_hash FROM users的用户权限错误,查日志才发现是越权扫描。如果当时只盯成功操作,这人就漏过去了。 - 日志存储要预留 Buffer。审计日志写满磁盘 → 数据库挂掉 → 业务停机。这个连锁反应我见过不止一次。建议给审计日志独立分区,并设置 80% 的磁盘告警。
好,现在你应该对整个审计策略的「该盯哪些操作」和「怎么配」有了清晰的思路。做到心中有数,动手的时候就不会慌。
嗯,记住这张图的逻辑:先定目标,再按五个维度去拆操作,然后基于配置策略落成日志输出。每一步都不难,但缺一个环节,审计就兜不住底。