触发器审计日志必须脱敏敏感字段、禁用current_user()、严格管控日志表权限与加密,并仅作兜底审计而非替代应用层脱敏。

触发器日志里别存原始敏感字段
触发器写入审计日志时,直接用 NEW.id_card 或 OLD.phone 插入 audit_log 表,等于把明文身份证、手机号全记进日志文件——这比数据库泄露还危险,因为日志常被多个角色访问,且备份更频繁。
- 必须在触发器内做脱敏:用
SUBSTRING()、CONCAT()或AES_DECRYPT()(如果原字段已加密)处理后再插入,例如:CONCAT(LEFT(NEW.id_card, 3), '***', RIGHT(NEW.id_card, 4)) - 禁止把
NEW.*或OLD.*整行序列化成 JSON 写入old_value/new_value字段——JSON 里会包含所有字段,包括你没意识到的password_hash或api_key - 如果业务要求记录完整变更,只对非敏感字段做全量快照;敏感字段单独列、固定脱敏格式,如
id_card_masked、phone_masked,不复用原始字段名
别让 CURRENT_USER() 暴露真实操作人
CURRENT_USER() 返回的是认证账号(如 app_rw@'10.20.%'),不是发起请求的人。所有通过连接池来的请求都显示同一个值,审计日志失去追溯价值,还可能误导调查方向。
- 应用层必须显式传入上下文:执行 SQL 前先运行
SET @app_user_id = 'u12345'; SET @app_trace_id = 'req-abc123'; - 触发器中读取
@app_user_id而非CURRENT_USER(),并写入日志表的app_user_id字段 - MySQL 不支持
SESSION_USER(),调用会报错ERROR 1305 (42000): FUNCTION xxx.SESSION_USER does not exist,别试
日志表本身要设访问控制和加密
审计日志表不是“只读保险箱”,它和普通表一样可被 SELECT、DUMP、甚至 DROP——如果权限没收紧,攻击者拿到一个低权限账号就能拖走全部历史操作记录。
- 创建专用日志账号,仅授予
INSERT权限(触发器用),严禁SELECT、UPDATE、DELETE;查日志由独立审计服务账号操作 - 对
audit_log表启用ENCRYPTION = 'Y'(MySQL 8.0+ InnoDB 表空间加密),防止磁盘被物理窃取后直接读取 - 禁用
LOAD DATA INFILE和SELECT ... INTO OUTFILE,避免通过这些语句导出日志内容;执行SET GLOBAL local_infile = OFF;
触发器不能替代应用层脱敏逻辑
有人想靠触发器统一拦截所有 INSERT/UPDATE 并自动脱敏字段,但这是高风险做法:一旦触发器异常或被绕过(比如用 REPLACE INTO 或批量 INSERT ... ON DUPLICATE KEY UPDATE),敏感数据就直写原表。
- 脱敏必须在应用写入前完成:DTO 构造时掩码、ORM 事件监听器预处理、或存储过程入口校验
- 触发器只做“兜底审计”——记录谁改了什么、改前改后什么样,而不是“代替业务逻辑做数据清洗”
- 性能上,每个 DML 都触发一次额外 INSERT,高并发下容易成为瓶颈;日志表建议用
COMPRESSED行格式 + 独立innodb_buffer_pool配置缓解











