外观
MySQL 审计日志配置与分析
审计日志概述
审计日志的作用
| 作用 | 详细说明 |
|---|---|
| 合规审计 | 满足PCI DSS、HIPAA、SOX等合规要求 |
| 安全监控 | 记录所有数据库访问和操作 |
| 故障追溯 | 追踪导致数据库问题的操作 |
| 权限审计 | 验证用户权限使用是否合规 |
| 操作审计 | 记录管理员的关键操作 |
版本差异
| 特性 | MySQL 5.6 | MySQL 5.7 | MySQL 8.0 |
|---|---|---|---|
| 企业版审计插件 | 支持 | 支持 | 支持 |
| 审计日志JSON格式 | 不支持 | 支持 | 支持 |
| 审计日志过滤 | 基础支持 | 增强支持 | 完整支持 |
| 审计日志加密 | 不支持 | 不支持 | 支持 |
| 审计日志压缩 | 不支持 | 不支持 | 支持 |
| 审计日志旋转策略 | 基础支持 | 增强支持 | 完整支持 |
审计日志的实现方式
| 实现方式 | 特点 | 适用场景 |
|---|---|---|
| MySQL企业版审计插件 | 官方支持,功能全面 | 企业级生产环境 |
| MariaDB审计插件 | 开源,与MySQL兼容 | 开源环境 |
| Percona Audit Log Plugin | 开源,Percona Server内置 | Percona用户 |
| 第三方审计工具 | 如McAfee MySQL Audit、Imperva | 特定需求场景 |
| 二进制日志+慢查询日志 | 基础审计能力 | 轻量级需求 |
审计日志配置
Percona Audit Log Plugin配置
安装与启用
bash
# 1. 确认插件是否已安装
mysql -u root -p -e "SHOW PLUGINS;" | grep audit_log
# 2. 如未安装,安装插件
mysql -u root -p -e "INSTALL PLUGIN audit_log SONAME 'audit_log.so';"
# 3. 验证插件安装成功
mysql -u root -p -e "SHOW PLUGINS;" | grep audit_log核心配置参数
| 参数 | 描述 | 版本支持 | 推荐值 |
|---|---|---|---|
| audit_log | 启用审计日志 | 5.6+ | ON |
| audit_log_file | 审计日志文件路径 | 5.6+ | /var/log/mysql/audit.log |
| audit_log_format | 审计日志格式 | 5.6+ | JSON |
| audit_log_rotation_policy | 审计日志轮换策略 | 5.6+ | size |
| audit_log_rotate_on_size | 日志大小限制 | 5.6+ | 100M |
| audit_log_file_rotations | 日志保留数量 | 5.6+ | 10 |
| audit_log_events | 审计事件类型 | 5.6+ | CONNECT,QUERY,TABLE |
| audit_log_filter | 审计过滤规则 | 5.7+ | 按需配置 |
ini
# 审计日志插件配置
[mysqld]
# 启用审计日志
audit_log=ON
# 审计日志文件路径
audit_log_file=/var/log/mysql/audit.log
# 审计日志格式
audit_log_format=JSON
# 可选值:OLD、JSON、CSV
# 审计日志轮换策略
audit_log_rotation_policy=size
# 可选值:none、size、time
# 日志大小限制(当policy=size时)
audit_log_rotate_on_size=100M
# 日志保留数量
audit_log_file_rotations=10
# 审计事件类型
audit_log_events=CONNECT,QUERY,TABLE
# 可选值:CONNECT,QUERY,TABLE,QUERY_DDL,QUERY_DML,QUERY_DCL
# 审计过滤规则
audit_log_filter='{"filter":{"log":{"connect":true,"query":true,"table":true}}}'不同场景的配置示例
生产环境配置
ini
[mysqld]
audit_log=ON
audit_log_file=/var/log/mysql/audit.log
audit_log_format=JSON
audit_log_rotation_policy=size
audit_log_rotate_on_size=200M
audit_log_file_rotations=20
audit_log_events=CONNECT,QUERY_DDL,QUERY_DCL,TABLE
audit_log_filter='{"filter":{"log":{"connect":true,"query":{"types":["create","alter","drop","grant","revoke","delete"]},"table":true}}}'开发测试环境配置
ini
[mysqld]
audit_log=ON
audit_log_file=/var/log/mysql/audit-dev.log
audit_log_format=CSV
audit_log_rotation_policy=time
audit_log_rotate_interval=24h
audit_log_file_rotations=7
audit_log_events=CONNECT,QUERY,TABLE
audit_log_filter='{"filter":{"log":{"connect":true,"query":true,"table":true}}}'高安全环境配置
ini
[mysqld]
audit_log=ON
audit_log_file=/var/log/mysql/audit-secure.log
audit_log_format=JSON
audit_log_rotation_policy=size
audit_log_rotate_on_size=100M
audit_log_file_rotations=30
audit_log_events=CONNECT,QUERY_DDL,QUERY_DML,QUERY_DCL,TABLE
audit_log_filter='{"filter":{"log":true}}'
# 记录所有事件MariaDB Audit Plugin配置
安装与启用
bash
# 1. 安装插件
mysql -u root -p -e "INSTALL SONAME 'server_audit';"
# 2. 验证插件
mysql -u root -p -e "SHOW PLUGINS;" | grep server_audit核心配置参数
ini
[mysqld]
# 启用审计
server_audit_logging=ON
# 审计日志文件
server_audit_file_path=/var/log/mysql/server_audit.log
# 日志格式
server_audit_format=OLD
# 可选值:OLD, JSON
# 审计事件类型
server_audit_events='CONNECT,QUERY,TABLE'
# 日志大小限制
server_audit_file_rotations=10
server_audit_file_size_limit=100000000
# 排除的数据库
server_audit_excl_users='mysql.sys,root@localhost'
# 包含的用户
server_audit_incl_users='admin,app_user'MySQL企业版审计插件配置
ini
[mysqld]
# 启用审计插件
plugin-load=audit_log.so
audit-log=FORCE_PLUS_PERMANENT
# 审计日志文件
audit-log-file=/var/log/mysql/audit.log
# 审计日志格式
audit-log-format=JSON
# 审计事件
audit-log-events=CONNECT,QUERY,TABLE
# 审计策略
audit-log-policy=ALL
# 可选值:ALL, LOGINS, QUERIES, NONE审计日志分析
审计日志格式解析
JSON格式示例
json
{
"timestamp": "2023-01-01T10:00:00Z",
"server_id": 100,
"command_class": "connect",
"connection_id": 12345,
"status": 0,
"user": "admin",
"host": "192.168.1.100",
"db": "",
"sql_text": "",
"table_name": "",
"table_schema": ""
}字段说明
| 字段 | 描述 | 重要性 |
|---|---|---|
| timestamp | 事件发生时间 | 用于时间范围分析 |
| server_id | 服务器ID | 主从环境标识 |
| command_class | 命令类型 | 事件分类依据 |
| connection_id | 连接ID | 关联同一连接的事件 |
| status | 操作结果状态码 | 成功/失败判断 |
| user | 操作用户 | 权限审计 |
| host | 客户端主机 | 来源分析 |
| db | 操作数据库 | 范围限定 |
| sql_text | 执行的SQL语句 | 核心审计内容 |
| table_name | 操作的表名 | 表级审计 |
| table_schema | 表所属数据库 | 库级审计 |
审计日志分析工具
命令行工具
bash
# 查看审计日志的基本信息
wc -l /var/log/mysql/audit.log
# 查看今天的审计记录
grep "$(date +"%Y-%m-%d")" /var/log/mysql/audit.log
# 统计不同用户的操作次数
grep -o '"user": "[^"]*"' /var/log/mysql/audit.log | sort | uniq -c
# 查找特定用户的操作
grep '"user": "admin"' /var/log/mysql/audit.log
# 查找特定命令类型
grep '"command_class": "DELETE"' /var/log/mysql/audit.log
# 查找失败的登录尝试
grep -E '"command_class": "connect".*"status": [1-9]' /var/log/mysql/audit.log
# 提取所有DDL操作
grep -E '"command_class": "(CREATE|ALTER|DROP|TRUNCATE)"' /var/log/mysql/audit.log使用jq工具分析JSON格式审计日志
bash
# 安装jq
yum install jq -y
# 或
debian install jq -y
# 查看所有DELETE操作
jq 'select(.command_class == "DELETE")' /var/log/mysql/audit.log
# 统计每种命令类型的数量
jq -s 'group_by(.command_class) | map({class: .[0].command_class, count: length})' /var/log/mysql/audit.log
# 查找影响特定表的操作
jq 'select(.table_name == "users" and .table_schema == "mydb")' /var/log/mysql/audit.log
# 查找执行时间在特定范围的操作
jq 'select(.timestamp >= "2023-01-01T10:00:00Z" and .timestamp <= "2023-01-01T11:00:00Z")' /var/log/mysql/audit.log
# 查找失败的操作
jq 'select(.status != 0)' /var/log/mysql/audit.log专业审计分析平台
| 平台 | 特点 | 适用场景 |
|---|---|---|
| MySQL Enterprise Monitor | 官方工具,集成审计分析 | 企业版用户 |
| Percona Monitoring and Management (PMM) | 开源,集成审计日志分析 | Percona用户 |
| ELK Stack | 开源,强大的日志分析能力 | 通用场景 |
| Splunk | 企业级日志管理 | 大型环境 |
| Graylog | 开源,易于部署 | 中小型环境 |
审计日志监控与告警
基于脚本的监控
bash
#!/bin/bash
# 配置信息
AUDIT_LOG="/var/log/mysql/audit.log"
ALERT_EMAIL="admin@example.com"
LOG_FILE="/var/log/mysql/audit_monitor.log"
echo "$(date +"%Y-%m-%d %H:%M:%S") - Audit log monitoring started" >> $LOG_FILE
# 检查审计日志文件是否存在
if [ ! -f $AUDIT_LOG ]; then
echo "$(date +"%Y-%m-%d %H:%M:%S") - Audit log file not found: $AUDIT_LOG" >> $LOG_FILE
exit 1
fi
# 检测失败的登录尝试(最近24小时)
FAILED_LOGINS=$(grep -c -E '"command_class": "connect".*"status": [1-9]' $AUDIT_LOG | tail -20000)
if [ $FAILED_LOGINS -gt 5 ]; then
# 提取失败的登录详情
FAILURE_DETAILS=$(grep -E '"command_class": "connect".*"status": [1-9]' $AUDIT_LOG | tail -20000 | tail -10)
# 发送告警邮件
echo -e "Subject: MySQL Failed Login Alert\n\nFailed login attempts in the last 24 hours: $FAILED_LOGINS\n\nDetails:\n$FAILURE_DETAILS" | sendmail $ALERT_EMAIL
echo "$(date +"%Y-%m-%d %H:%M:%S") - Sent failed login alert, count: $FAILED_LOGINS" >> $LOG_FILE
fi
# 检测敏感表的DELETE操作(最近24小时)
SENSITIVE_DELETES=$(grep -c -E '"command_class": "DELETE".*"table_name": "(users|accounts|orders)"' $AUDIT_LOG | tail -20000)
if [ $SENSITIVE_DELETES -gt 0 ]; then
# 提取敏感删除详情
DELETE_DETAILS=$(grep -E '"command_class": "DELETE".*"table_name": "(users|accounts|orders)"' $AUDIT_LOG | tail -20000)
# 发送告警邮件
echo -e "Subject: MySQL Sensitive Table Delete Alert\n\nSensitive table DELETE operations detected: $SENSITIVE_DELETES\n\nDetails:\n$DELETE_DETAILS" | sendmail $ALERT_EMAIL
echo "$(date +"%Y-%m-%d %H:%M:%S") - Sent sensitive delete alert, count: $SENSITIVE_DELETES" >> $LOG_FILE
fi
# 检测权限变更操作(最近24小时)
PRIVILEGE_CHANGES=$(grep -c -E '"command_class": "(GRANT|REVOKE|CREATE USER|DROP USER)"' $AUDIT_LOG | tail -20000)
if [ $PRIVILEGE_CHANGES -gt 0 ]; then
# 提取权限变更详情
PRIVILEGE_DETAILS=$(grep -E '"command_class": "(GRANT|REVOKE|CREATE USER|DROP USER)"' $AUDIT_LOG | tail -20000)
# 发送告警邮件
echo -e "Subject: MySQL Privilege Change Alert\n\nPrivilege change operations detected: $PRIVILEGE_CHANGES\n\nDetails:\n$PRIVILEGE_DETAILS" | sendmail $ALERT_EMAIL
echo "$(date +"%Y-%m-%d %H:%M:%S") - Sent privilege change alert, count: $PRIVILEGE_CHANGES" >> $LOG_FILE
fi
echo "$(date +"%Y-%m-%d %H:%M:%S") - Audit log monitoring completed" >> $LOG_FILE集成到监控平台
Prometheus + Alertmanager
配置Prometheus监控审计日志指标:
yaml# prometheus.yml scrape_configs: - job_name: 'mysql_audit' static_configs: - targets: ['localhost:9104'] metrics_path: /metrics配置Alertmanager告警规则:
yamlgroups: - name: mysql_audit_alerts rules: - alert: MySQLFailedLoginAttempts expr: mysql_audit_failed_logins_total > 5 for: 5m labels: severity: warning annotations: summary: "High number of failed MySQL login attempts" description: "MySQL server {{ $labels.instance }} has seen {{ $value }} failed login attempts in the last 5 minutes." - alert: MySQLSensitiveTableDeletes expr: mysql_audit_sensitive_deletes_total > 0 for: 5m labels: severity: critical annotations: summary: "Sensitive table delete operations detected" description: "MySQL server {{ $labels.instance }} has seen {{ $value }} delete operations on sensitive tables."
Zabbix监控
创建监控项:
- 类型:Zabbix agent (active)
- 键值:
log[/var/log/mysql/audit.log,"command_class": "connect".*"status": [1-9],,,] - 数据类型:数字(无正负号)
创建触发器:
- 名称:MySQL Failed Login Attempts
- 表达式:
{mysql-server:log[/var/log/mysql/audit.log,"command_class": "connect".*"status": [1-9],,,].count(5m)} > 5 - 严重性:警告
审计日志管理
日志轮转
使用logrotate
bash
# 创建logrotate配置文件
cat > /etc/logrotate.d/mysql-audit << 'EOF'
/var/log/mysql/audit.log {
daily
rotate 30
compress
delaycompress
missingok
notifempty
create 640 mysql mysql
postrotate
# 通知MySQL重新打开日志文件
mysqladmin -u root -p flush-logs
endscript
}
EOF插件内置轮转
根据插件配置自动轮转,如:
audit_log_rotation_policy=size:按大小轮转audit_log_rotation_policy=time:按时间轮转
日志保留策略
| 环境 | 保留时间 | 轮转频率 | 压缩方式 | 版本建议 |
|---|---|---|---|---|
| 生产环境 | 90天 | 每天 | gzip | 5.6+ |
| 开发环境 | 7天 | 每天 | 不压缩 | 5.6+ |
| 测试环境 | 3天 | 每天 | 不压缩 | 5.6+ |
| 高安全环境 | 1年 | 每天 | gzip + 归档 | 5.6+ |
日志清理
bash
# 手动清理30天前的审计日志
find /var/log/mysql -name "audit.log.*" -mtime +30 -delete
# 清理超过指定大小的旧日志
ls -lh /var/log/mysql/audit.log.* | awk '$5 > "100M" {print $9}' | xargs rm -f
# 自动化清理脚本
#!/bin/bash
LOG_DIR="/var/log/mysql"
RETENTION_DAYS=30
# 清理旧的审计日志
find $LOG_DIR -name "audit.log.*" -mtime +$RETENTION_DAYS -delete
# 记录清理操作
echo "$(date +"%Y-%m-%d %H:%M:%S") - Cleaned up audit logs older than $RETENTION_DAYS days" >> $LOG_DIR/audit_cleanup.log审计日志最佳实践
配置最佳实践
合理选择审计事件类型:
- 生产环境:只审计必要的事件类型(如CONNECT、DDL、DCL、敏感表操作)
- 避免审计所有事件,减少性能影响
- 根据合规要求调整审计范围
使用过滤规则:
- 排除系统用户和频繁访问的应用用户
- 只审计关键数据库和表
- 针对不同用户设置不同的审计规则
选择合适的日志格式:
- JSON格式便于自动化分析和处理
- 生产环境推荐JSON格式
- 开发环境可使用CSV格式便于直接查看
设置合理的日志大小和保留时间:
- 平衡磁盘空间和审计需求
- 定期归档和清理旧日志
- 根据合规要求调整保留期限
启用插件永久化:
ini[mysqld] # 确保插件无法被动态卸载 audit-log=FORCE_PLUS_PERMANENT
性能优化
| 优化项 | 具体措施 | 预期效果 | 版本要求 |
|---|---|---|---|
| 减少审计事件 | 只审计必要事件 | 降低CPU和I/O开销 | 5.6+ |
| 使用过滤规则 | 排除不必要的用户和操作 | 减少日志量 | 5.7+ |
| 选择合适的存储 | 使用SSD存储审计日志 | 提高写入性能 | 5.6+ |
| 合理设置轮转 | 避免单文件过大 | 提高查询性能 | 5.6+ |
| 异步写入 | 配置插件异步写入日志 | 减少对主业务的影响 | 5.7+ |
| 批量写入 | 配置插件批量写入日志 | 降低I/O频率 | 8.0+ |
安全最佳实践
保护审计日志文件:
bash# 设置正确的权限 chown mysql:mysql /var/log/mysql/audit.log chmod 640 /var/log/mysql/audit.log # 限制访问权限 setfacl -m u:auditor:r /var/log/mysql/audit.log加密传输和存储:
- 对审计日志进行加密存储(MySQL 8.0+支持内置加密)
- 使用SSL/TLS加密审计日志的传输
- 定期轮换加密密钥
定期备份审计日志:
bash# 自动化备份脚本 #!/bin/bash BACKUP_DIR="/backup/mysql/audit/$(date +%Y%m%d)" mkdir -p $BACKUP_DIR # 备份审计日志 cp /var/log/mysql/audit.log.* $BACKUP_DIR/ # 压缩备份 tar -zcvf $BACKUP_DIR.tar.gz $BACKUP_DIR # 复制到远程服务器 scp $BACKUP_DIR.tar.gz backup@remote-server:/backup/mysql/audit/ # 清理本地临时文件 rm -rf $BACKUP_DIR限制审计日志的访问:
- 只有授权的审计人员可以访问
- 定期审查访问权限
- 记录审计日志的访问情况
完整性验证:
- 定期验证审计日志的完整性
- 使用哈希算法确保日志未被篡改
bash# 生成审计日志哈希值 sha256sum /var/log/mysql/audit.log > /var/log/mysql/audit.log.sha256
审计日志案例分析
案例一:未授权访问检测
问题现象:审计日志中出现大量来自未知IP的登录尝试
审计日志分析:
json
{
"timestamp": "2023-01-01T10:00:00Z",
"command_class": "connect",
"user": "root",
"host": "10.0.0.1",
"status": 1045
}处理流程:
- 立即封禁该IP:
iptables -A INPUT -s 10.0.0.1 -j DROP - 检查是否有成功登录记录
- 重置root密码:
ALTER USER 'root'@'%' IDENTIFIED BY 'new_strong_password'; - 启用IP白名单:
bind-address = 192.168.1.0/24 - 配置更严格的登录失败限制
案例二:误操作追踪
问题现象:数据库中的重要表被意外删除
审计日志分析:
json
{
"timestamp": "2023-01-01T14:30:00Z",
"command_class": "drop_table",
"user": "admin",
"host": "192.168.1.100",
"db": "mydb",
"sql_text": "DROP TABLE important_table;",
"status": 0
}处理流程:
- 从备份恢复表结构和数据
- 对admin用户进行权限审查
- 实施更严格的操作审批流程
- 配置敏感操作告警
- 定期演练误操作恢复
案例三:权限滥用检测
问题现象:普通用户执行了只有管理员才能执行的操作
审计日志分析:
json
{
"timestamp": "2023-01-01T16:45:00Z",
"command_class": "grant",
"user": "app_user",
"host": "192.168.2.200",
"sql_text": "GRANT ALL PRIVILEGES ON *.* TO 'hacker'@'%';",
"status": 0
}处理流程:
- 立即撤销非法授予的权限:
REVOKE ALL PRIVILEGES ON *.* FROM 'hacker'@'%'; - 检查app_user的权限:
SHOW GRANTS FOR 'app_user'@'%'; - 收回不必要的权限:
REVOKE ALL PRIVILEGES ON *.* FROM 'app_user'@'%'; GRANT SELECT, INSERT, UPDATE ON mydb.* TO 'app_user'@'%'; - 审计所有用户权限
- 配置权限变更告警
审计日志与其他日志的协同
与错误日志协同
- 审计日志记录"who did what"
- 错误日志记录"what went wrong"
- 结合分析可以完整还原事件场景
与慢查询日志协同
- 慢查询日志记录性能问题
- 审计日志记录执行慢查询的用户和上下文
- 结合分析可以找出性能问题的责任人
与二进制日志协同
- 二进制日志用于数据恢复
- 审计日志用于操作审计
- 结合使用可以实现完整的"操作审计+数据恢复"方案
总结
审计日志是MySQL安全运维的重要组成部分,通过合理配置和分析审计日志,可以:
- 提高数据库安全性:及时发现和阻止未授权访问
- 满足合规要求:提供完整的审计轨迹
- 快速定位问题:追踪导致数据库问题的操作
- 优化权限管理:验证用户权限使用是否合规
- 改进操作流程:基于审计结果优化操作流程
作为DBA,应该根据业务需求和合规要求,选择合适的审计方案,建立完善的审计日志管理和分析机制,确保MySQL数据库的安全可靠运行。
