必须同步切断网络层、认证层、权限层三处入口,检查mysql用户host值是否为%或通配符,修改bind-address为内网地址或127.0.0.1,关闭防火墙3306端口放行,云环境还需清理安全组规则,并从公网实测telnet和mysql连接验证断开效果。

直接停用外网远程访问,不能只删用户或改配置文件某一行——必须同步切断网络层、认证层、权限层三处入口,否则残留路径仍可能被利用。
检查当前有哪些用户具备外网访问能力
先确认哪些账户实际暴露在公网,避免误删内网专用账号。执行:
SELECT User, Host FROM mysql.user WHERE Host IN ('%', '0.0.0.0', '::') OR Host LIKE '%.%';
重点看 Host 值为 % 或含通配符(如 192.168.% 但实际被公网路由可达)的记录。若发现 root@% 或 admin@% 这类高危组合,需优先处理。
- 不要仅靠
DROP USER 'xxx'@'%'删除——部分 MySQL 版本(如 5.7)中该语句不自动刷新权限缓存 - 删除后务必执行
FLUSH PRIVILEGES;,否则旧权限仍生效 - 若用户是通过
UPDATE mysql.user SET Host='%' WHERE User='xxx'修改的,需连同对应行一并DELETE,否则残留空 Host 记录可能引发冲突
修改 bind-address 并重启 mysqld
仅删用户不够:如果 bind-address 仍是 0.0.0.0,MySQL 仍在监听所有接口,只要有人重建用户就能立刻恢复访问。
- 编辑配置文件(Linux 常为
/etc/mysql/mysql.conf.d/mysqld.cnf,Windows 为my.ini),定位[mysqld]段落 - 将
bind-address = 0.0.0.0改为明确的内网地址,例如bind-address = 192.168.1.100;若仅需本地访问,设为bind-address = 127.0.0.1 - 切勿注释掉该行——MySQL 会 fallback 到默认值(通常是
0.0.0.0) - 重启服务:
sudo systemctl restart mysql(Debian/Ubuntu)或sudo systemctl restart mysqld(RHEL/CentOS)
重启后验证是否生效:ss -tln | grep :3306 应只显示 127.0.0.1:3306 或具体内网 IP,而非 *:3306。
关闭防火墙上的 3306 端口放行规则
即使 MySQL 不监听外网,若防火墙仍允许 3306 入站,攻击者扫描到端口开放后可能触发其他漏洞(如协议指纹探测、SSL 握手异常等)。
- ufw 用户:
sudo ufw delete allow 3306/tcp,然后sudo ufw reload - firewalld 用户:
sudo firewall-cmd --permanent --remove-port=3306/tcp,再sudo firewall-cmd --reload - 云服务器必须同步检查安全组——控制台里删掉所有针对 3306 端口的入方向规则,尤其是源地址为
0.0.0.0/0的条目
注意:有些环境(如 Docker 容器)会额外映射端口,需检查 docker ps 输出中是否有 0.0.0.0:3306->3306/tcp 类似映射,有则需 docker stop 对应容器或重配 --publish 参数。
验证是否真正断开外网访问
别信“看起来没报错”,要用外部视角实测:
- 从另一台公网机器(非内网)执行:
telnet your.mysql.ip 3306—— 应连接超时或被拒绝,而非出现 MySQL 协议响应 - 用 MySQL 客户端尝试连接:
mysql -h your.mysql.ip -u anyuser -p—— 必须返回ERROR 2003 (HY000),而非ERROR 1045 (28000)(后者说明已连上但认证失败,意味着外网通道仍通) - 检查 MySQL 错误日志(通常在
/var/log/mysql/error.log)是否有近期来自公网 IP 的连接尝试记录
最容易被忽略的是:花生壳、ZeroTier、Tailscale 等穿透工具创建的隧道仍可能绕过传统防火墙,若环境中存在此类服务,需一并停用或调整其 ACL 规则。











