存在匿名用户隐患:执行SELECT User,Host FROM mysql.user WHERE User='';若返回''@'localhost'等记录即存在风险,需同步检查mysql.db、tables_priv、proxies_priv等表中User=''的残留权限,并防范初始化、备份恢复等场景导致其复活。

必须立刻查,不能等出事再扫——匿名用户(User = '')不是“没名字”,是“不验名字就能进”的后门。
怎么快速确认有没有匿名用户
登录有 SUPER 或 SELECT ON mysql.* 权限的账号,执行:
SELECT User, Host FROM mysql.user WHERE User = '';
只要返回任意记录,比如 ''@'localhost'、''@'127.0.0.1' 或 ''@'::1',就存在隐患。别信“我只用本地”,IPv6 的 ::1 和 socket 登录路径常被忽略。
- 如果结果为空,基本可排除——但建议加个巡检脚本定期跑,因为备份恢复、Docker 重部署都可能带回来
- 别用
SELECT user()或CURRENT_USER()查,它们只返回当前连接身份,跟系统里存了啥无关 - 注意大小写和空格:
WHERE User = ' '(带空格)不算匿名用户,WHERE User = ''(纯空字符串)才算
为什么光看 mysql.user 不够
删掉 ''@'localhost' 只清了主账户,权限可能还挂在别的表里继续生效:
-
mysql.db表里可能有User = ''的库级授权,尤其是对test库的ALL PRIVILEGES,删用户后仍能建表占满磁盘 -
mysql.tables_priv或mysql.columns_priv也可能残留空用户名的细粒度权限 -
mysql.proxies_priv里若有Proxied_host = '',可能允许代理绕过认证
所以完整检查语句至少要包括:
SELECT * FROM mysql.db WHERE User = '';<br>SELECT * FROM mysql.tables_priv WHERE User = '';<br>SELECT * FROM mysql.proxies_priv WHERE Proxied_user = '';
哪些场景下匿名用户会“悄悄复活”
不是你删了就一劳永逸,这些动作会重新生成空用户名:
- 用未设密码的
mysqld --initialize初始化实例(尤其某些旧版 Docker 镜像默认这么干) - 手动执行
CREATE USER '@localhost';(漏写用户名)或GRANT ... TO '@%' ;(引号内为空) - 运行
mysql_secure_installation时一路回车跳过 “Remove anonymous users?” 提示 - 从含匿名用户的备份恢复数据(
mysqldump --all-databases默认导出mysql库)
防复发最简单的动作:把 SELECT User, Host FROM mysql.user WHERE User = ''; 加进你的运维巡检脚本,配合 cron 每周一凌晨自动告警。
删之前必须验证是否真没人用
有些老监控脚本、遗留 PHP mysqli_connect('localhost', '', '') 或 Perl DBI 连接,确实依赖空用户名。直接 DROP USER 可能导致服务中断:
- 先在业务低峰期抓一下连接来源:
SELECT user, host, db FROM information_schema.processlist WHERE user = ''; - 检查应用配置文件、启动脚本、CI/CD 流水线中是否有
-u ""或user: ""类似写法 - MySQL 8.0+ 支持
DROP USER IF EXISTS ''@'localhost';,避免因误删不存在用户报错,但不解决“删了之后业务挂了”的问题
真正麻烦的从来不是怎么删,而是删完发现某个没人认领的定时任务突然连不上——空用户名的隐蔽性,正在于它不报错、不告警、只在关键时候静默失效。

















