general_log开着等于裸奔,因其完整记录所有sql(含明文密码、敏感查询),攻击者通过只读账号即可窃取数据;必须同时禁用开关与输出目标,推荐永久配置general_log=off并设log_file为/dev/null或nul,且删除mysql.general_log表以防历史泄露。
为什么 general_log 开着等于裸奔
mysql 的 general_log 会完整记录所有客户端发送的 sql,包括 select、insert、带明文密码的 set password,甚至 mysql -u root -p123456 这种命令行参数(取决于启动方式)。一旦攻击者拿到数据库只读账号,只要能查 mysql.general_log 表或读取日志文件,就能直接看到敏感查询——比如用户手机号、身份证号、token 查询语句。
禁用通用日志的两种可靠方式
必须同时关闭日志开关和日志输出目标,只关一个没用。常见错误是只执行 SET GLOBAL general_log = OFF,但没确认 general_log_file 是否还在写入(某些版本在 OFF 状态下仍可能缓存并落盘)。
-
永久禁用(推荐):在
my.cnf或my.ini的[mysqld]段添加两行:general_log = OFF general_log_file = /dev/null
(Linux)或general_log_file = NUL(Windows)。重启生效,且无法被运行时命令打开。 -
临时禁用(仅调试):先确认当前状态:
SELECT @@global.general_log, @@global.general_log_file;;再执行:SET GLOBAL general_log = OFF; SET GLOBAL general_log_file = '/tmp/empty.log';
(注意:不能设为空字符串,MySQL 会拒绝;设为不可写路径或/dev/null更稳妥)。
防视图直接读取:删掉 mysql.general_log 表本身
MySQL 5.1+ 默认把通用日志存成 CSV 表(mysql.general_log),即使日志关闭,只要表存在,有 SELECT 权限的账号仍可 SELECT * FROM mysql.general_log 查历史记录。这不是“视图”,而是真实表,且默认不设权限隔离。
- 检查是否存在:
SHOW TABLES IN mysql LIKE 'general_log'; - 如果存在且你不需要历史日志,直接删:
DROP TABLE IF EXISTS mysql.general_log; - 删表后,即使日后误开
general_log,也会因无目标表而静默失败(日志转到文件,而非表)。 - 注意:删除操作需
root或DELETE+CREATE权限;普通账号即使有SELECT权限,也查不到已删的表。
别漏掉日志文件残留和权限绕过
禁用后,旧日志文件(如 /var/lib/mysql/hostname.log)依然存在,且文件权限常为 -rw-rw----,同组用户(如 mysql 组下的其他服务)可能读取。更隐蔽的是:如果应用用 LOAD DATA INFILE 或 SELECT ... INTO OUTFILE 写了自定义日志,那跟 MySQL 自身日志无关,得单独审计。
- 清理旧文件:
rm -f $(mysqld --verbose --help 2>/dev/null | grep "general_log_file" | awk '{print $NF}')(先确认路径再删) - 检查文件权限:
ls -l $(mysql -Nse "SELECT @@global.general_log_file;"),确保非mysql用户不可读 - 禁止非必要权限:
REVOKE FILE ON *.* FROM 'app_user'@'%';(防止通过INTO OUTFILE造新日志)
真正难防的不是配置开关,而是日志路径是否被其他进程监控、备份脚本是否意外同步了日志目录、以及有没有人用 strace -p $(pidof mysqld) -e trace=write 直接抓写系统调用——这些不在 MySQL 配置控制范围内。











