应执行 select host, user, authentication_string from mysql.user where trim(user) = '' 来识别 mysql 中 user 为空字符串或仅含空白字符的非法账号,因直接比较 user = '' 无法匹配含空格、制表符等的记录。

如何识别 MySQL 中 user 为空的账号记录
MySQL 的权限系统把账号信息存在 mysql.user 表里,其中 User 列为空字符串('')或仅含空格的账号属于非法/冗余账户,可能被用于匿名登录或绕过认证。直接查 User = '' 不够,因为有些记录的 User 是空格、制表符等空白字符,得用 TRIM() 过滤。
执行这条语句确认是否存在:
SELECT Host, User, authentication_string FROM mysql.user WHERE TRIM(User) = '';
注意:MySQL 8.0+ 默认用 authentication_string 存密码哈希;5.7 及以前可能是 Password 字段,但该字段已在 8.0 废弃。
删除空 User 记录前必须停用 skip-grant-tables
如果 MySQL 启动时加了 --skip-grant-tables,权限系统不生效,此时你无法用 DROP USER 或 DELETE FROM mysql.user —— 所有 DML 都会被忽略或报错 ERROR 1290 (HY000): The MySQL server is running with the --skip-grant-tables option so it cannot execute this statement。
- 先检查是否启用:
SELECT @@skip_grant_tables;返回1就说明开着 - 临时关闭需重启 mysqld,并确保配置文件(如
/etc/my.cnf)里没有skip-grant-tables行 - 重启后用
mysql -u root -p正常登录,再操作
安全删除空用户:优先用 DROP USER,不用 DELETE
直接 DELETE FROM mysql.user WHERE ... 会破坏权限表结构一致性,比如没同步清理 mysql.db、mysql.tables_priv 等关联表,后续执行 FLUSH PRIVILEGES 也可能失败或引发异常。
正确做法是:对每条待删记录,构造 DROP USER 语句,显式指定 Host:
DROP USER ''@'localhost';<br>DROP USER ''@'%';
如果你有批量需求,可用以下查询生成语句(复制结果后手动执行):
SELECT CONCAT('DROP USER ''', User, '''@''', Host, ''';') AS drop_stmt<br>FROM mysql.user<br>WHERE TRIM(User) = '';
执行完每条 DROP USER 后,MySQL 自动刷新权限,无需再 FLUSH PRIVILEGES。
清理后验证是否还有残留及匿名访问风险
删完别以为就结束了。空 User 账号常伴随 Host = '%',意味着任意主机都能连(若密码为空或弱),这是严重安全隐患。
- 再跑一次
SELECT Host, User FROM mysql.user WHERE TRIM(User) = '';,确认返回空集 - 检查是否有
User非空但authentication_string = ''的弱口令账号:SELECT Host, User FROM mysql.user WHERE authentication_string = '' AND TRIM(User) != ''; - 测试能否匿名连接:
mysql -h 127.0.0.1 -u '' -p(输任意密码或直接回车),如果能进,说明还有漏网之鱼或 host 匹配逻辑未覆盖
真正难处理的是那些 User 为空但 Host 是具体 IP 或域名的记录——它们可能来自旧版迁移脚本或手工误插,容易被忽略。











