SQL审计日志的设计需要同时满足安全溯源和合规要求,核心目标是完整记录所有数据库操作行为,确保在出现数据泄露、违规操作时可以快速定位责任人,同时符合监管对日志留存、内容完整性的强制规定。

SQL审计日志的合规要求
不同行业的安全合规标准对SQL审计日志有明确要求,常见的核心要求包括:
- 日志必须包含操作时间、操作账号、操作来源、操作内容、操作结果五个核心要素
- 日志留存时间不低于6个月,重要业务系统要求留存1年以上
- 日志不可篡改,需要保证存储过程的完整性,防止被恶意删除或修改
- 敏感操作(如删除表、修改权限、批量导出数据)需要单独标记,支持快速检索
SQL审计日志核心字段设计
要覆盖合规要求的所有场景,SQL审计日志需要包含以下核心字段,字段设计要兼顾完整性和可查询性:
| 字段名 | 字段说明 | 合规对应要求 |
|---|---|---|
| log_id | 日志唯一ID,自增或UUID生成 | 日志唯一性标识,支持溯源 |
| operate_time | 操作发生的时间戳,精确到毫秒 | 操作时间记录要求 |
| db_user | 执行SQL的数据库账号 | 操作责任人标识 |
| source_ip | 发起操作的客户端IP地址 | 操作来源追溯要求 |
| sql_content | 执行的完整SQL语句 | 操作内容记录要求 |
| sql_type | SQL类型,如SELECT、INSERT、UPDATE、DELETE、DDL等 | 操作类型分类,支持敏感操作识别 |
| operate_result | 操作执行结果,成功或失败,失败需记录错误码 | 操作结果记录要求 |
| effect_rows | SQL执行影响的数据行数 | 批量操作风险识别 |
| is_sensitive | 是否为敏感操作,0否1是 | 敏感操作单独标记要求 |
敏感操作的识别规则设计
合规要求中对敏感操作的审计是重点,需要提前定义敏感操作识别规则,在日志生成时自动标记is_sensitive字段:
- DDL操作:包含CREATE、ALTER、DROP、TRUNCATE等关键词的SQL
- 权限操作:包含GRANT、REVOKE、CREATE USER、DROP USER等关键词的SQL
- 批量数据操作:DELETE、UPDATE操作影响行数超过1000行的场景
- 敏感表操作:对存储用户信息、交易数据、密码等敏感表的操作
- 全量查询:SELECT语句没有WHERE条件或者WHERE条件为恒真的场景
基于MySQL的审计日志实现示例
以MySQL数据库为例,可以通过开启通用日志结合自定义触发器的方式实现SQL审计,以下是核心实现代码:
1. 创建审计日志表
-- 创建SQL审计日志表 CREATE TABLE `sql_audit_log` ( `log_id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '日志ID', `operate_time` datetime(3) NOT NULL COMMENT '操作时间', `db_user` varchar(64) NOT NULL COMMENT '数据库账号', `source_ip` varchar(32) NOT NULL COMMENT '来源IP', `sql_content` text NOT NULL COMMENT 'SQL内容', `sql_type` varchar(16) NOT NULL COMMENT 'SQL类型', `operate_result` tinyint(4) NOT NULL COMMENT '操作结果 0失败 1成功', `effect_rows` int(11) DEFAULT NULL COMMENT '影响行数', `is_sensitive` tinyint(4) NOT NULL DEFAULT '0' COMMENT '是否敏感操作 0否 1是', `error_msg` varchar(512) DEFAULT NULL COMMENT '错误信息', PRIMARY KEY (`log_id`), KEY `idx_operate_time` (`operate_time`), KEY `idx_db_user` (`db_user`), KEY `idx_is_sensitive` (`is_sensitive`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='SQL审计日志表';
2. 敏感操作识别函数
-- 创建敏感操作识别函数
DELIMITER $$
CREATE FUNCTION `check_sensitive_sql`(p_sql_content TEXT, p_sql_type VARCHAR(16), p_effect_rows INT)
RETURNS TINYINT(4)
DETERMINISTIC
BEGIN
DECLARE v_is_sensitive TINYINT(4) DEFAULT 0;
-- DDL操作标记为敏感
IF p_sql_type IN ('CREATE', 'ALTER', 'DROP', 'TRUNCATE') THEN
SET v_is_sensitive = 1;
-- 权限操作标记为敏感
ELSEIF UPPER(p_sql_content) LIKE '%GRANT%' OR UPPER(p_sql_content) LIKE '%REVOKE%'
OR UPPER(p_sql_content) LIKE '%CREATE USER%' OR UPPER(p_sql_content) LIKE '%DROP USER%' THEN
SET v_is_sensitive = 1;
-- 批量操作标记为敏感
ELSEIF p_sql_type IN ('DELETE', 'UPDATE') AND p_effect_rows > 1000 THEN
SET v_is_sensitive = 1;
-- 全量查询标记为敏感
ELSEIF p_sql_type = 'SELECT' AND (UPPER(p_sql_content) NOT LIKE '%WHERE%' OR UPPER(p_sql_content) LIKE '%WHERE 1=1%') THEN
SET v_is_sensitive = 1;
END IF;
RETURN v_is_sensitive;
END$$
DELIMITER ;
3. 通用日志转存审计日志的存储过程
-- 创建存储过程,将通用日志转存到审计日志表
DELIMITER $$
CREATE PROCEDURE `transfer_general_log`()
BEGIN
DECLARE v_sql TEXT;
DECLARE v_user VARCHAR(64);
DECLARE v_time DATETIME(3);
DECLARE v_ip VARCHAR(32);
DECLARE v_done INT DEFAULT 0;
-- 游标读取通用日志
DECLARE cur_log CURSOR FOR SELECT event_time, user_host, argument FROM mysql.general_log WHERE command_type = 'Query' AND event_time > DATE_SUB(NOW(), INTERVAL 1 MINUTE);
DECLARE CONTINUE HANDLER FOR NOT FOUND SET v_done = 1;
OPEN cur_log;
read_loop: LOOP
FETCH cur_log INTO v_time, v_user, v_sql;
IF v_done THEN
LEAVE read_loop;
END IF;
-- 解析用户和IP
SET v_ip = SUBSTRING_INDEX(v_user, '@', -1);
SET v_user = SUBSTRING_INDEX(v_user, '@', 1);
-- 插入审计日志表
INSERT INTO sql_audit_log (
operate_time, db_user, source_ip, sql_content, sql_type, operate_result, effect_rows, is_sensitive
) VALUES (
v_time,
v_user,
v_ip,
v_sql,
UPPER(SUBSTRING_INDEX(v_sql, ' ', 1)),
1,
NULL,
check_sensitive_sql(v_sql, UPPER(SUBSTRING_INDEX(v_sql, ' ', 1)), 0)
);
END LOOP;
CLOSE cur_log;
-- 清理已转存的通用日志
TRUNCATE TABLE mysql.general_log;
END$$
DELIMITER ;
审计日志的存储与合规保障
完成日志生成后,还需要做好存储和防护才能满足合规要求:
- 日志存储采用读写分离架构,审计日志表单独存放在只读从库,避免主库性能受影响
- 定期将日志同步到离线存储系统,比如对象存储,满足长期留存要求
- 对日志表开启防删除权限,只有审计管理员可以查询日志,普通运维人员无删除、修改权限
- 定期对日志完整性进行校验,通过哈希算法生成日志校验值,防止日志被篡改
常见问题说明
很多人在设计SQL审计日志时会遇到性能问题,这里提供两个优化方案:
- 对于高频查询的业务,可以只审计写操作和敏感读操作,减少日志生成量
- 采用异步写入方式,将日志先写入消息队列,再批量同步到数据库,降低对业务SQL执行的影响
另外需要注意,审计日志中如果包含用户敏感信息,比如明文密码、身份证号,需要对这些字段进行脱敏处理,避免日志本身造成数据泄露风险。