最危险账号是host≠'localhost'且super_priv='y'或grant_priv='y',需结合弱哈希(char_length(authentication_string)

直接查 mysql.user 表就能定位高危账号,但必须用对字段和条件
只看 User 和 Host 不够,真正决定风险的是三个组合条件:是否允许远程登录、是否拥有管理权限、密码是否可信。最危险的账号是 Host != 'localhost' 且 Super_priv = 'Y' 或 Grant_priv = 'Y' 的组合——这类账号等于把数据库 root 密码贴在公网门口。
- 用
CHAR_LENGTH(authentication_string) 检测弱哈希(比 <code>authentication_string = ''更可靠,兼容 MySQL 5.7 截断行为) - MySQL 8.0+ 的
caching_sha2_password用户,authentication_string非空也不代表安全,需额外确认是否启用require_secure_transport - 别漏掉
plugin = 'mysql_native_password'且authentication_string为空的账号——它们可免密登录,password_expired = 'Y'完全不拦得住
用纯 bash + mysql 命令就能完成轻量扫描,无需 Python 依赖
巡检脚本的核心难点不是写 SQL,而是安全地解析结果并避免敏感字段泄露。关键在于输出格式控制和字段分割方式。
- 用
mysql -N -s -e "SELECT ... FROM mysql.user WHERE ..."获取无头、无对齐的制表符分隔结果 - 逐行读取时必须设
IFS=$'\t',否则用户名含空格会导致字段错位(比如User被切进Host字段) - 所有过滤条件必须硬编码在 SQL 里,禁止拼接变量传入,防止 shell 注入(例如
WHERE Host = '$host'是高危写法) - 检测到
Super_priv = Y且Host = '%'时,立刻echo "ALERT: wildcard root found"并exit 1,方便上层 CI/CD 中断流程
自动化检查必须覆盖三类默认合规项,返回空才表示通过
人工检查容易遗漏,建议把以下三条命令固化进每日巡检脚本。每条返回空结果才算合规,否则就是明确的安全缺口。
SELECT User, Host FROM mysql.user WHERE Host != 'localhost' AND (Super_priv = 'Y' OR Grant_priv = 'Y');SELECT User, Host FROM mysql.user WHERE CHAR_LENGTH(authentication_string)SELECT User, Host FROM mysql.user WHERE User = 'root' AND Host != 'localhost';
复杂点在于 MySQL 8.0+ 的认证插件差异和连接降级逻辑
很多脚本在云 RDS 或新版 MySQL 上失败,不是权限逻辑错了,而是连不上——caching_sha2_password 默认不被旧版客户端支持。
- 脚本启动前应先尝试标准连接;失败则加
--default-auth=mysql_native_password重试 - 若仍失败,再用
mysql --local-infile=0 -u... -p... -h... -P... --connect-timeout=5 -e "SELECT 1"快速探活,避免卡死 - 不要在脚本里硬编码密码,改用
~/.my.cnf配置文件,并确保权限为600,否则mysql命令会拒绝读取
authentication_string 的存储长度和截断规则不一致,用固定长度判断(如 )比字符串内容匹配更稳定;另外,<code>Host = '10.0.0.%' 这种网段通配也属于远程登录,不能只盯 '%'。











