审计账号须用CREATE USER显式创建并授权,禁用GRANT自动建号;需覆盖mysql、information_schema及业务库只读权限;禁止GRANT OPTION、FILE及高危权限;MySQL 8.0+注意认证插件兼容性与mysql库访问限制。

审计账号必须用 CREATE USER 显式创建,不能靠 GRANT 自动补全
很多人误以为 GRANT SELECT ON mysql.* TO 'auditor'@'10.20.%' 就能自动建账号,实际在 MySQL 5.7+ 默认 strict 模式下会报错:ERROR 1133 (HY000): Can't find any matching row in the user table。必须先用 CREATE USER 建号,再授权。
-
CREATE USER要指定明确的 host(如'10.20.1.%'),'%'虽方便但违反最小权限原则,生产环境应避免 - 密码必须满足当前 MySQL 的密码策略(比如
validate_password.length >= 8),否则CREATE USER直接失败 - MySQL 8.0+ 默认认证插件是
caching_sha2_password,老版本审计工具可能不兼容,必要时加IDENTIFIED WITH mysql_native_password
只读权限要覆盖 mysql、information_schema 和业务库三类对象
外部审计系统不是只查你的业务表,它需要读取元数据和运行时状态——比如连接数、慢查询、复制延迟、用户权限列表。漏掉任意一类,审计就会断档。
- 查当前活跃连接:
SELECT * FROM information_schema.PROCESSLIST→ 需要PROCESS权限(全局) - 查表结构和索引:
SELECT * FROM information_schema.TABLES→ 需要SELECToninformation_schema(MySQL 5.7+ 允许,但需显式授予) - 查用户和权限配置:
SELECT * FROM mysql.user→ 需要SELECTonmysql.user(MySQL 8.0 默认禁止,得用SELECTonmysql库整体) - 查慢日志开关与内容:
SELECT * FROM mysql.slow_log(如果启用了表格式慢日志)→ 同样依赖mysql库的SELECT
FLUSH PRIVILEGES 不是必需的,但权限变更后首次测试必须用新连接
执行完 CREATE USER 和 GRANT 后,权限已写入 grant 表,MySQL 线程会自动在下次权限检查时加载新规则。FLUSH PRIVILEGES 只在你手动改过 mysql 表(不推荐)时才需要。
- 测试账号是否生效,必须开一个全新连接:
mysql -u auditor -h 10.20.1.5 -p -e "SELECT USER(), CURRENT_USER()" - 注意区分
USER()和CURRENT_USER():前者返回登录时声明的账号(auditor@10.20.1.100),后者返回实际匹配的 grant 记录(auditor@10.20.1.%),不一致说明 host 匹配失败 - 如果连得上但查
mysql.user报Access denied,大概率是 MySQL 8.0+ 的默认限制——需额外执行GRANT SELECT ON mysql.* TO 'auditor'@'10.20.1.%'
审计账号不能有 GRANT OPTION,且应禁用 SUPER 类高危权限
哪怕只是“只读”,也要防住权限越界。MySQL 中某些只读操作背后隐含提权路径,比如通过 SELECT ... INTO OUTFILE 写文件,或利用 LOAD_FILE() 读敏感配置。
- 确认没授予
GRANT OPTION:SHOW GRANTS FOR 'auditor'@'10.20.1.%'输出里不能出现该字样 - 禁止
FILE权限:否则可读/etc/my.cnf或写 webshell 到 document root - 禁止
SHUTDOWN、RELOAD、REPLICATION CLIENT等非审计必需的权限,它们对审计无价值却大幅扩大攻击面 - 若审计系统需解析 binlog(如对接 Canal),不要给账号
REPLICATION SLAVE权限,而应由 DBA 单独配置专用复制账号,审计账号仅负责消费已推送的变更消息
真实环境中最容易被忽略的是 information_schema 的访问控制粒度——它不是数据库,而是一组只读视图,MySQL 对它的权限管理是特例。很多团队只授了业务库 SELECT,结果审计系统连表字段名都拉不出来,最后发现卡在 SELECT COLUMN_NAME FROM information_schema.COLUMNS 这一步。


















