1. 问题背景:MySQL账号安全排查的痛点
最近在排查数据库安全问题时,发现了一个棘手的情况:审计日志中频繁出现不明IP的访问记录,但常规的排查手段无法快速定位问题源头。这种情况在MySQL运维中并不罕见——当系统突然出现性能波动或数据异常时,我们常常需要回答两个关键问题:"谁在访问数据库?"和"这些访问来自哪里?"
传统的排查方法主要有三种:
- 查看
information_schema.processlist:只能看到当前连接,历史记录无法追溯 - 分析慢查询日志:仅记录执行时间超过阈值的SQL,信息不完整
- 审计插件:需要额外安装配置,对线上环境有侵入性
而general_log这个原生功能往往被忽视。它能完整记录所有MySQL服务端收到的请求,包括客户端连接信息、执行的SQL语句以及时间戳,是排查未知访问源的利器。下面我将结合实战案例,详细说明如何利用这个"数据库监控摄像头"来锁定可疑访问。
注意:开启general_log会带来约5%-15%的性能开销,建议仅在排查期间临时启用
2. general_log的配置与启用
2.1 检查当前日志状态
首先连接MySQL执行:
SHOW VARIABLES LIKE 'general_log%';典型输出如下:
+------------------+--------------------------+ | Variable_name | Value | +------------------+--------------------------+ | general_log | OFF | | general_log_file | /var/lib/mysql/query.log | +------------------+--------------------------+2.2 动态开启日志记录
无需重启服务,直接运行:
SET GLOBAL general_log = 'ON'; SET GLOBAL log_output = 'TABLE'; -- 可选FILE/TABLE/NONE推荐使用TABLE模式,日志会存入mysql.general_log表,方便SQL查询分析。FILE模式则直接写入文件系统,适合大规模日志场景。
2.3 关键参数说明
| 参数名 | 可选值 | 推荐设置 | 作用说明 |
|---|---|---|---|
| general_log | ON/OFF | 按需开启 | 总开关 |
| log_output | FILE/TABLE/NONE | TABLE | 日志存储形式 |
| general_log_file | 文件路径 | 自定义路径 | FILE模式下的日志文件位置 |
3. 日志分析与可疑访问定位
3.1 基础查询:获取完整访问记录
SELECT event_time, user_host, command_type, argument FROM mysql.general_log ORDER BY event_time DESC LIMIT 100;关键字段解析:
user_host:格式为username[client_name] @ hostname [IP]command_type:Connect/Query/Quit等argument:执行的SQL内容
3.2 识别异常连接的特征
通过以下特征筛选可疑记录:
- 非常规时间段的访问(如凌晨2-4点)
- 使用默认账号或弱密码(如root/空密码)
- 来自非常见IP段的连接
- 高频的Connect/Disconnect操作
- 执行敏感操作(如DROP TABLE、GRANT等)
示例排查SQL:
-- 统计各IP的连接次数 SELECT SUBSTRING_INDEX(SUBSTRING_INDEX(user_host,'[',-1),']',1) AS client_ip, COUNT(*) AS connection_count FROM mysql.general_log WHERE command_type = 'Connect' GROUP BY client_ip ORDER BY connection_count DESC; -- 查找执行过DROP/ALTER的操作 SELECT * FROM mysql.general_log WHERE argument LIKE '%DROP%' OR argument LIKE '%ALTER%';3.3 高级分析技巧
3.3.1 关联performance_schema
SELECT gl.event_time, gl.user_host, gl.argument, ps.HOST FROM mysql.general_log gl JOIN performance_schema.threads ps ON ps.PROCESSLIST_ID = gl.thread_id WHERE gl.command_type = 'Connect';3.3.2 使用正则过滤
-- 匹配特定IP段 SELECT * FROM mysql.general_log WHERE user_host REGEXP '192\\.168\\.1\\.[0-9]{1,3}';4. 实战案例:定位恶意扫描行为
某次安全审计中发现大量异常连接,通过以下步骤锁定问题:
- 发现特征:每分钟约30次Connect,且user_host格式异常
SELECT user_host, COUNT(*) FROM mysql.general_log WHERE command_type = 'Connect' GROUP BY user_host;输出显示大量''@'1.1.1.%'格式的连接(1.1.1.x为示例IP)
- 追踪完整访问链:
SELECT * FROM mysql.general_log WHERE user_host LIKE '%@1.1.1.%' ORDER BY event_time;发现攻击者在尝试常见账号组合(root/空密码、admin/admin等)
- 应对措施:
- 立即防火墙封禁该IP段
- 修改所有账号为强密码
- 设置
skip_name_resolve防止反向解析消耗资源
5. 性能优化与注意事项
5.1 日志清理策略
长期开启general_log会导致:
- 日志表/文件体积暴涨
- 写入性能下降
- 磁盘空间占用
建议的维护方案:
-- 定期清理旧日志(TABLE模式) TRUNCATE TABLE mysql.general_log; -- 或按时间删除 DELETE FROM mysql.general_log WHERE event_time < DATE_SUB(NOW(), INTERVAL 7 DAY); -- FILE模式需手动轮转 mv general.log general.log.old mysqladmin flush-logs5.2 生产环境最佳实践
- 使用FILE模式时,将日志放在独立磁盘分区
- 高并发场景建议设置
log_output=TABLE并定期导出 - 配合脚本实现自动报警(如检测到敏感操作)
#!/bin/bash alert_operations=("DROP" "GRANT" "FLUSH PRIVILEGES") for op in "${alert_operations[@]}"; do mysql -e "SELECT * FROM mysql.general_log WHERE argument LIKE '%${op}%'" \ | mail -s "MySQL敏感操作告警" admin@example.com done6. 延伸应用场景
6.1 SQL审计合规
满足GDPR等法规要求,记录所有数据访问行为:
-- 创建审计视图 CREATE VIEW sql_audit AS SELECT event_time, user_host, argument FROM mysql.general_log WHERE argument NOT LIKE '%general_log%'; -- 排除日志查询自身6.2 性能问题排查
分析特定时间段的SQL模式:
SELECT LEFT(argument, 50) AS short_sql, COUNT(*) AS execution_count, AVG(LENGTH(argument)) AS avg_length FROM mysql.general_log WHERE event_time BETWEEN '2023-01-01 14:00' AND '2023-01-01 15:00' AND command_type = 'Query' GROUP BY short_sql ORDER BY execution_count DESC;6.3 应用程序调试
验证ORM框架生成的真实SQL:
SELECT argument FROM mysql.general_log WHERE user_host LIKE 'app_user%@%' AND argument LIKE '%FROM orders%';我在实际使用中发现,结合general_log与tcpdump能获得更完整的访问链路。例如当遇到SSL加密连接时,可以先用tcpdump抓包获取原始IP,再通过general_log分析具体操作。这种组合拳在应对高级威胁时特别有效。