审计策略设计:到底该盯住哪些操作

我做了这么多年的数据库安全项目,最常被问到的问题就是:「审计到底要开哪些?」

很多团队一上来就全量审计,结果业务没跑几天,磁盘直接报警,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 配置策略的三步法

好,现在你知道该审哪些操作了。下一步是怎么配置。我习惯分成三步走:

  1. 选工具:根据你的数据库类型选择审计方案。MySQL 官方有 audit_log 插件,MariaDB 有 server_audit,PostgreSQL 用 pgaudit。别自己造轮子。
  2. 分层设策略:把表按照敏感度分三层:
    • 核心层(金融、用户信息、订单):所有 DDL + 所有 DML 失败 + 所有 DCL → 全量审计
    • 业务层(商品、库存、日志表):DDL + DCL + 高权限用户的 DML → 半量审计
    • 临时层(缓存、临时表):只审 DDL 和 DCL → 按需审计
  3. 定存储和轮转:审计日志不能一直堆积。设置每天或每 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% 的磁盘告警。

好,现在你应该对整个审计策略的「该盯哪些操作」和「怎么配」有了清晰的思路。做到心中有数,动手的时候就不会慌。

审计策略设计模型 审计策略目标 用户维度 对象维度 操作维度 结果维度 时间维度 配置策略:分层 + 账号 + 操作 审计日志输出与存储 所有配置须持久化 + 独立存储 + 定期审查策略

嗯,记住这张图的逻辑:先定目标,再按五个维度去拆操作,然后基于配置策略落成日志输出。每一步都不难,但缺一个环节,审计就兜不住底。