应立即执行SELECT User, Host FROM mysql.user WHERE User = 'root'确认root@'%'是否残留,若存在必须用DROP USER 'root'@'%'删除而非DELETE,并同步设置bind-address = 127.0.0.1、封禁3306端口,三步缺一不可。

确认 root@'%' 是否还存在于 mysql.user 表中
很多人以为改过 host 就安全了,结果一查发现 root@'%' 还在表里——这是最常见也最危险的疏漏。MySQL 8.0+ 的权限缓存机制会让旧记录继续参与认证,哪怕你执行过 UPDATE mysql.user SET host = 'localhost',FLUSH PRIVILEGES 也不保证清空所有上下文。
真正该做的,是登录本地 MySQL 后立刻运行:
SELECT User, Host FROM mysql.user WHERE User = 'root';
如果输出包含 %、0.0.0.0、192.168.% 或任意公网 IP,说明远程入口仍开着。这时候必须用:
-
DROP USER 'root'@'%';(不是DELETE FROM mysql.user) - 如有其他网段形式,如
'root'@'10.0.0.%',也一并DROP USER - 再执行一次
FLUSH PRIVILEGES; - 重新查一遍
SELECT User, Host FROM mysql.user WHERE User = 'root';,结果应只剩'root'@'localhost'或'root'@'127.0.0.1'
bind-address 必须设为 127.0.0.1 并重启服务
删掉用户只是砍权限入口,网络层不关严等于门框还在。哪怕没 root@'%',只要 mysqld 监听 *:3306,攻击者就能用其他高权账号(比如被提权的 dba_admin)走 TCP 连入。
编辑配置文件(常见路径:/etc/mysql/mysql.conf.d/mysqld.cnf 或 /etc/my.cnf),在 [mysqld] 段下写死:
bind-address = 127.0.0.1
严禁写成 0.0.0.0、::、留空或注释掉该行——那是默认监听所有接口。改完必须重启:
- Debian/Ubuntu:
sudo systemctl restart mysql - RHEL/CentOS:
sudo systemctl restart mysqld
验证是否生效:sudo ss -tlnp | grep :3306 输出应为 127.0.0.1:3306,不是 *:3306。
防火墙必须封死 3306 的外网访问
云服务器上,安全组和本地防火墙是两套独立系统。漏掉其中任一,远程连接仍可能成功。
必须同步操作:
- 云平台控制台(阿里云/腾讯云等)关闭 3306 入方向规则
- 本地封端口:
sudo ufw deny 3306(Ubuntu)或sudo firewall-cmd --permanent --remove-port=3306/tcp(CentOS)并 reload
测试时别用 mysql -h localhost,它走 Unix socket,绕过 TCP 和防火墙;真测要用 mysql -h 127.0.0.1 或从另一台机器连真实 IP。
localhost 和 127.0.0.1 在 MySQL 中是两个不同 host
这是最容易被忽略的细节:前者走 Unix socket,后者走 TCP。只留 'root'@'localhost' 不能防 mysql -h 127.0.0.1 连接,因为那是另一个 host,且走的是网络栈。
所以最终检查结果里,Host 字段必须是 localhost 或 127.0.0.1,但不能混用;若你留了 'root'@'127.0.0.1',就得确保 bind-address = 127.0.0.1 生效,否则连接会失败。
复杂点在于:三步缺一不可,任何一步松动,都可能让远程连接复活——尤其是自动化脚本里带 CREATE USER 'root'@'%' 的情况,会瞬间绕过所有人工修改。


















