Skip to content

数据库审计实践

一、数据库审计概述

1.1 什么是数据库审计

数据库审计是指对数据库系统的访问和操作行为进行记录、监控和分析的过程。通过审计,可以追踪谁在什么时间、从哪个 IP、执行了什么 SQL 语句、影响了哪些数据。

1.2 审计的重要性

维度说明
安全合规满足等保 2.0、GDPR、PCI DSS、SOX 等法规要求
风险追溯发生数据泄露时,可追溯至具体操作人和操作记录
内部威胁检测和防范内部人员的恶意操作或误操作
异常检测发现异常的 SQL 访问模式,如批量导出数据
性能优化分析慢查询,优化数据库性能

1.3 等保 2.0 对数据库审计的要求

根据《信息安全技术 网络安全等级保护基本要求》(GB/T 22239-2019):

  • 三级系统:应对数据库操作进行审计,审计记录保存 >= 180 天。
  • 四级系统:应启用数据库审计功能,对关键操作进行实时告警。

二、MySQL 审计策略配置

2.1 MySQL General Log(通用日志)

General Log 记录所有 SQL 语句,是最基础的审计方式。

bash
# 登录 MySQL
mysql -u root -p
sql
-- 查看当前 General Log 配置
SHOW VARIABLES LIKE '%general%';

-- 启用 General Log(动态生效,无需重启)
SET GLOBAL general_log = ON;

-- 设置日志文件路径
SET GLOBAL general_log_file = '/var/log/mysql/mysql-general.log';

-- 查看日志文件位置
SHOW VARIABLES LIKE 'general_log_file';
bash
# 查看 General Log 内容
tail -f /var/log/mysql/mysql-general.log

# 示例输出
# 2026-07-08T10:30:00.123456Z	  5 Query	SELECT * FROM employees WHERE id = 1001
# 2026-07-08T10:30:05.654321Z	  5 Query	INSERT INTO audit_logs (user, action) VALUES ('admin', 'login')

General Log 的局限性

优点缺点
实现简单,不依赖第三方插件记录所有 SQL,日志量大,影响性能
无额外依赖不支持细粒度过滤(用户、数据库、操作类型)
原生支持生产环境不推荐长期开启

2.2 MySQL Audit Plugin(审计插件)

MySQL 企业版提供了审计日志插件(MySQL Enterprise Audit),社区版可使用 MariaDB Audit Plugin 或第三方开源插件。

2.2.1 安装 MariaDB Audit Plugin(社区版推荐方案)

bash
# 下载 MariaDB Audit Plugin
wget https://downloads.mariadb.com/Connectors/audit-plugin/mariadb-audit-plugin.tar.gz
tar -xzf mariadb-audit-plugin.tar.gz
cp lib/mariadb/plugin/server_audit.so /usr/lib/mysql/plugin/
sql
-- 安装插件
INSTALL PLUGIN server_audit SONAME 'server_audit.so';

-- 验证安装
SHOW PLUGINS;
-- 应看到 server_audit 状态为 ACTIVE

2.2.2 配置审计规则

sql
-- 设置审计日志文件路径
SET GLOBAL server_audit_file_path = '/var/log/mysql/audit.log';

-- 设置审计事件类型(ALL 表示记录所有事件)
SET GLOBAL server_audit_events = 'CONNECT,QUERY,TABLE,QUERY_DDL,QUERY_DML';

-- 排除系统账户的审计(避免过多日志)
SET GLOBAL server_audit_incl_users = '';
SET GLOBAL server_audit_excl_users = 'root,replication';

-- 设置日志轮转(每 10 天自动轮转,保留 30 天)
SET GLOBAL server_audit_file_rotate_size = 104857600;  -- 100MB 轮转
SET GLOBAL server_audit_file_rotations = 9;

-- 启用审计
SET GLOBAL server_audit_logging = ON;

2.2.3 持久化配置(写入 my.cnf)

ini
[mysqld]
# 审计插件配置
plugin-load = server_audit=server_audit.so
server_audit_logging = ON
server_audit_events = CONNECT,QUERY,TABLE,QUERY_DDL,QUERY_DML
server_audit_file_path = /var/log/mysql/audit.log
server_audit_file_rotate_size = 104857600
server_audit_file_rotations = 9
server_audit_excl_users = root,replication

2.3 MySQL Enterprise Audit(企业版)

如果使用 MySQL 企业版,可以直接使用官方审计插件:

sql
-- 安装企业版审计插件
INSTALL PLUGIN audit_log SONAME 'audit_log.so';

-- 审计日志格式(JSON 格式,便于解析)
SET GLOBAL audit_log_format = 'JSON';

-- 审计策略
SET GLOBAL audit_log_policy = 'ALL';
-- 可选值:ALL(所有操作)、LOGINS(登录事件)、QUERIES(查询)、NONE(关闭)

JSON 格式的审计日志示例:

json
{
  "timestamp": "2026-07-08T10:30:00",
  "id": 12345,
  "class": "connection",
  "event": "connect",
  "account": {
    "user": "app_user",
    "host": "192.168.1.100"
  },
  "login": {
    "user": "app_user",
    "os": "linux",
    "thread": 1234
  },
  "connection_type": "ssl",
  "status": 0
}

三、审计策略配置

3.1 审计粒度策略

根据实际业务需求,选择合适的审计粒度:

审计级别记录内容适用场景
全部记录所有 SQL 语句和连接事件合规要求严格的金融行业
仅 DDLCREATE/ALTER/DROP 操作数据库结构变更审计
DDL + DMLDDL + INSERT/UPDATE/DELETE常规审计需求
仅登录连接和断开事件基础安全审计
自定义指定用户/数据库/表精准审计

3.2 生产环境推荐配置

sql
-- 生产环境审计配置(平衡性能与安全)

-- 1. 记录所有 DDL 操作(结构变更)
SET GLOBAL server_audit_events = 'QUERY_DDL,CONNECT';

-- 2. 针对敏感表开启 DML 审计
-- 通过应用程序层或触发器实现
-- 示例:对 employees 表创建审计触发器

-- 3. 排除高频监控连接
SET GLOBAL server_audit_excl_users = 'root,monitor,replication,backup';

-- 4. 设置日志轮转
SET GLOBAL server_audit_file_rotate_size = 1073741824;  -- 1GB
SET GLOBAL server_audit_file_rotations = 9;

3.3 敏感表审计触发器

对于需要精确审计的敏感表,可以使用触发器记录变更:

sql
-- 创建审计日志表
CREATE TABLE audit.employee_changes (
    id INT AUTO_INCREMENT PRIMARY KEY,
    table_name VARCHAR(64),
    operation VARCHAR(16),
    old_data JSON,
    new_data JSON,
    changed_by VARCHAR(128),
    changed_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    client_ip VARCHAR(45)
);

-- 创建审计触发器
DELIMITER //
CREATE TRIGGER trg_employees_update
AFTER UPDATE ON hr.employees
FOR EACH ROW
BEGIN
    INSERT INTO audit.employee_changes (
        table_name, operation, old_data, new_data,
        changed_by, client_ip
    ) VALUES (
        'employees',
        'UPDATE',
        JSON_OBJECT(
            'id', OLD.id,
            'name', OLD.name,
            'salary', OLD.salary
        ),
        JSON_OBJECT(
            'id', NEW.id,
            'name', NEW.name,
            'salary', NEW.salary
        ),
        USER(),
        SUBSTRING(USER(), INSTR(USER(), '@') + 1)
    );
END//
DELIMITER ;

四、日志分析

4.1 审计日志格式解析

MySQL Audit Plugin 的日志格式示例:

20260708 10:30:00,web-01,app_user,192.168.1.100,5,12345,QUERY,,'SELECT * FROM employees WHERE id = 1001',0
字段位置说明
时间戳1事件发生时间
服务器名2数据库服务器主机名
用户名3连接用户
客户端 IP4来源 IP 地址
连接 ID5数据库连接 ID
查询 ID6查询内部 ID
操作类型7CONNECT/QUERY/DISCONNECT
数据库8目标数据库
SQL 语句9执行的 SQL
状态码100 成功,非 0 失败

4.2 日志分析脚本

bash
#!/bin/bash
# 用途:MySQL 审计日志分析脚本
# 保存路径:/usr/local/bin/analyze-mysql-audit.sh

AUDIT_LOG="/var/log/mysql/audit.log"
REPORT_DIR="/var/reports/mysql-audit"
DATE=$(date +%Y%m%d)

mkdir -p "$REPORT_DIR"

echo "========== MySQL 审计日志分析报告 ($DATE) ==========="
echo ""

# 1. 统计操作类型分布
echo "--- 操作类型统计 ---"
awk -F',' '{count[$7]++} END {for (k in count) print k, count[k]}' "$AUDIT_LOG" | sort -k2 -rn

# 2. 统计活跃用户(按操作次数排序)
echo ""
echo "--- 活跃用户排名 ---"
awk -F',' '{users[$3]++} END {for (u in users) print u, users[u]}' "$AUDIT_LOG" | sort -k2 -rn | head -10

# 3. 统计来源 IP
echo ""
echo "--- 来源 IP 统计 ---"
awk -F',' '{ips[$4]++} END {for (ip in ips) print ip, ips[ip]}' "$AUDIT_LOG" | sort -k2 -rn | head -10

# 4. 检测敏感操作(DROP/TRUNCATE/ALTER)
echo ""
echo "--- 敏感操作检测 ---"
grep -iE '(DROP\s+TABLE|TRUNCATE|ALTER\s+TABLE|DELETE\s+FROM)' "$AUDIT_LOG" || echo "未检测到敏感操作"

# 5. 检测失败连接
echo ""
echo "--- 连接失败记录 ---"
grep "CONNECT" "$AUDIT_LOG" | grep -v ",0$" || echo "未检测到连接失败"

# 生成报告文件
{
    echo "MySQL 审计日志分析报告"
    echo "生成日期: $DATE"
    echo "日志文件: $AUDIT_LOG"
    echo ""
    echo "--- 操作类型统计 ---"
    awk -F',' '{count[$7]++} END {for (k in count) print k, count[k]}' "$AUDIT_LOG"
} > "$REPORT_DIR/report_$DATE.txt"

echo ""
echo "报告已生成: $REPORT_DIR/report_$DATE.txt"

4.3 使用 Python 进行高级分析

python
#!/usr/bin/env python3
# 用途:MySQL 审计日志结构化分析
# 保存路径:/usr/local/bin/audit_analyzer.py

import csv
import json
from collections import Counter, defaultdict
from datetime import datetime

def parse_audit_log(log_file):
    """解析 MySQL Audit Plugin 日志"""
    records = []
    with open(log_file, 'r') as f:
        for line in f:
            parts = line.strip().split(',')
            if len(parts) >= 10:
                records.append({
                    'timestamp': parts[0],
                    'server': parts[1],
                    'user': parts[2],
                    'client_ip': parts[3],
                    'conn_id': parts[4],
                    'query_id': parts[5],
                    'event_type': parts[6],
                    'database': parts[7],
                    'query': ','.join(parts[8:-1]),
                    'status': parts[-1]
                })
    return records

def generate_report(records):
    """生成审计分析报告"""
    report = {
        'total_events': len(records),
        'event_types': Counter(r['event_type'] for r in records),
        'top_users': Counter(r['user'] for r in records).most_common(10),
        'top_ips': Counter(r['client_ip'] for r in records).most_common(10),
        'sensitive_ops': []
    }

    # 检测敏感操作
    sensitive_keywords = ['DROP', 'TRUNCATE', 'ALTER TABLE', 'DELETE']
    for r in records:
        for kw in sensitive_keywords:
            if kw in r['query'].upper():
                report['sensitive_ops'].append({
                    'time': r['timestamp'],
                    'user': r['user'],
                    'ip': r['client_ip'],
                    'query': r['query']
                })
                break

    return report

if __name__ == '__main__':
    import sys
    log_file = sys.argv[1] if len(sys.argv) > 1 else '/var/log/mysql/audit.log'
    records = parse_audit_log(log_file)
    report = generate_report(records)
    print(json.dumps(report, indent=2, ensure_ascii=False))

4.4 日志轮转与归档

bash
# logrotate 配置
cat <<'EOF' | tee /etc/logrotate.d/mysql-audit
/var/log/mysql/audit.log {
    daily
    rotate 30
    compress
    delaycompress
    missingok
    notifempty
    create 640 mysql mysql
    sharedscripts
    postrotate
        # 通知 MySQL 刷新日志文件
        mysql -e "FLUSH LOGS;" 2>/dev/null || true
    endscript
}
EOF

# 手动测试 logrotate
logrotate -vf /etc/logrotate.d/mysql-audit

五、合规要求

5.1 等保 2.0 三级要求

控制点要求实现方式
安全审计启用审计功能,记录所有数据库操作MySQL Audit Plugin
审计内容记录用户、时间、操作类型、操作结果审计插件 + 触发器
审计记录保护审计日志不可篡改日志异地存储 + 权限控制
审计记录保存保存 >= 180 天日志轮转 + 归档策略
审计分析具备审计日志分析能力分析脚本 + 定期报告

5.2 数据安全法要求

  • 对重要数据的操作行为进行审计记录。
  • 审计记录至少保存 6 个月。
  • 定期对审计记录进行安全分析。
  • 发现异常操作及时告警。

5.3 金融行业合规(PCI DSS)

  • 对所有数据库访问进行审计。
  • 审计跟踪必须与所有用户活动关联。
  • 审计日志必须定期审查。
  • 保护审计日志不被修改。

六、最佳实践

6.1 审计策略选择

yaml
# 审计策略配置建议
environment:
  production:
    audit_events: "CONNECT,QUERY_DDL,QUERY_DML"
    excl_users: "root,monitor,replication,backup,scheduler"
    log_rotate_size: 1GB
    log_retention_days: 180
    log_format: "JSON"  # 企业版支持

  staging:
    audit_events: "ALL"
    excl_users: "root"
    log_rotate_size: 100MB
    log_retention_days: 30

  development:
    audit_events: "CONNECT,QUERY_DDL"
    excl_users: "root"
    log_retention_days: 7

6.2 性能影响评估

审计方式性能影响建议使用场景
General Log高(10-20% 性能下降)仅临时调试使用
Audit Plugin(DDL 仅)低(< 1%)生产环境推荐
Audit Plugin(DDL + DML)中(3-5%)合规要求较高
触发器低-中(取决于表)仅关键表
第三方审计平台低(旁路抓包)大型企业推荐

6.3 安全建议

  • 审计日志存储:审计日志不应存储在数据库服务器本地,应发送到集中日志平台(如 ELK)。
  • 权限隔离:审计日志的访问权限应独立于数据库 DBA 权限。
  • 定期审计:每月至少进行一次审计日志分析,形成报告。
  • 告警机制:对敏感操作设定实时告警规则(如 DROP TABLE、批量导出)。
  • 加密传输:审计日志传输过程应加密,防止中间人篡改。

参考文档:MySQL 官方文档 - Audit Plugin | MariaDB Audit Plugin 文档 | 等保 2.0 标准