开放MySQL远程访问,往往是为了让应用服务器、报表工具或运维脚本能够从其他主机连接数据库。但这个操作如果只停留在修改配置和放行端口,很容易把3306端口暴露到公网。端口一旦被扫描到,攻击者会尝试弱口令登录、枚举账户,甚至借助未及时修复的漏洞发起攻击。管理远程访问安全不是简单禁止外部连接,而是把监听范围、网络来源、账户权限、传输加密和异常行为监控逐层收紧。本文会围绕这些层面展开,给出适合生产环境的配置思路。

第一层:收紧监听地址与网络入口
MySQL是否对外提供服务,首先由监听地址决定。很多发行版安装后默认只监听127.0.0.1,这种情况下远程客户端根本无法建立TCP连接。但也有一些环境为了方便,把bind-address设置为0.0.0.0,甚至注释掉该项,导致数据库对所有网卡开放。检查当前监听状态可以先执行SHOW VARIABLES LIKE 'bind_address';,确认变量值。如果业务确实需要跨主机访问,应该把监听地址设置为内网网卡IP,而不是通配地址。例如应用服务器与数据库服务器同处192.168.10.0/24网段,配置文件可以这样调整。
[mysqld] bind-address = 192.168.10.5 # 如需仅本机访问,保持 127.0.0.1 即可
监听地址只是第一道门,第二道门是主机防火墙。即使MySQL绑定到内网地址,如果防火墙对3306端口完全放行,任何能到达该网段的设备都可以尝试连接。更合理的做法是只允许业务子网或指定管理主机访问。以iptables为例,可以先允许来源网段,再默认拒绝其他来源的3306连接。防火墙规则需要根据实际部署调整来源地址,避免直接写0.0.0.0/0。如果业务必须从公网接入,优先考虑VPN或SSH隧道,而不是把数据库端口直接暴露给公网。
# 仅允许 192.168.10.0/24 访问本机 3306 iptables -A INPUT -p tcp -s 192.168.10.0/24 --dport 3306 -j ACCEPT iptables -A INPUT -p tcp --dport 3306 -j DROP
修改配置后需要重启MySQL服务,再用ss -lntp | grep 3306或netstat -lntp | grep 3306确认监听地址是否已经从0.0.0.0变为内网IP。有些运维习惯改变默认端口来降低扫描命中率,这可以过滤一部分批量扫描,但不能作为主要防护手段。端口变更只能降低噪音,真正能挡住攻击的还是来源限制和后续的账户策略。
第二层:用账户主机白名单替代泛匹配
MySQL的账户身份由用户名和来源主机共同确定,也就是说appuser从192.168.10.8登录与从任意主机登录,在权限系统中是两个不同账户。主机部分支持精确IP、通配符和子网掩码。很多远程访问风险来自使用'%'作为主机白名单,例如创建'appuser'@'%',这意味着任何一个IP都可以尝试用该用户名登录。只要密码足够弱,数据库就可能被拖走。因此,业务账户的Host列应尽量写成具体IP或受限网段,管理账户尤其不能使用'%'。
CREATE USER 'appuser'@'192.168.10.%' IDENTIFIED BY 'Str0ngPassw0rd!'; GRANT SELECT,INSERT,UPDATE,DELETE ON appdb.* TO 'appuser'@'192.168.10.%';
创建用户后还要检查权限是否膨胀。很多教程为了方便直接给ALL PRIVILEGES ON *.*,这样该账户可以访问所有库表,还能执行管理命令。对普通业务账户来说,按库、按操作类型授权才是正确做法。即便业务确实需要跨库读取,也应明确列出库名,而不是使用通配库。授权最小化不仅能降低被攻破后的损失,也能避免开发测试时误操作影响系统表。
对于已经存在的宽松账户,可以通过RENAME USER或重新创建来收敛Host范围。但要注意,RENAME USER只修改账户定义,不修改已授予的权限,所以如果权限本身过大,还需要执行REVOKE回收。管理员还应该定期查询mysql.user表,重点观察哪些账户的Host为'%',以及是否存在空密码账户。下面的语句可以快速列出普通用户及来源。
SELECT user, host, plugin
FROM mysql.user
WHERE user NOT IN ('mysql.sys','mysql.session','mysql.infoschema');
root账户尤其要限制来源。某些云镜像或快速安装脚本会生成root@'%',这是非常危险的配置。如果发现类似账户,应将其改回本地地址,或者直接锁定。远程管理数据库时,不应该让root从公网直连,而是通过SSH登录到数据库服务器后再使用本地连接,这样能把root暴露面降到最低。
-- 如果存在 root@'%',改回本地来源 RENAME USER 'root'@'%' TO 'root'@'localhost';
第三层:强制TLS传输加密并统一认证插件
即使来源IP和账户都做了收敛,如果密码和查询结果在网络上明文传输,依然可能被内网嗅探。MySQL支持TLS加密连接,服务端启用后,客户端与数据库之间的认证信息和查询数据都能被加密。全局强制开启可以在配置文件中设置require_secure_transport为ON,并指定CA、证书和私钥路径。证书可以用企业CA签发,也可以使用自签证书,但自签证书在生产环境需要客户端信任相应CA。
[mysqld] require_secure_transport = ON ssl-ca = /etc/mysql/certs/ca.pem ssl-cert = /etc/mysql/certs/server-cert.pem ssl-key = /etc/mysql/certs/server-key.pem
除了全局开关,还可以对单个账户强制要求SSL。例如对远程业务账户执行ALTER USER ... REQUIRE SSL;,这样即使用户密码被截获,登录过程也会因为缺少加密通道而失败。客户端连接串则需要开启SSL模式。不同语言的驱动参数不同,但核心都是要求服务端证书可被验证。连接后可以执行SHOW STATUS LIKE 'Ssl_cipher';确认当前连接是否已经进入加密状态。如果返回空值,说明连接仍然没有使用TLS。
ALTER USER 'appuser'@'192.168.10.%' REQUIRE SSL; SHOW STATUS LIKE 'Ssl_cipher';
认证插件同样影响远程访问安全。MySQL默认的caching_sha2_password比旧版mysql_native_password更安全,但部分老旧驱动或工具可能无法兼容。遇到兼容性问题时,不应该简单把所有账户改回旧插件。正确做法是升级客户端驱动或使用支持新插件的连接库,并在内网环境统一标准。如果确实为了兼容必须使用旧插件,至少要把该账户限制在受控网段,并开启TLS,弥补旧插件在密码交换上的不足。
第四层:审计登录失败并控制连接频率
再严密的配置也需要通过日志验证效果。MySQL错误日志会记录认证失败事件,管理员应定期关注Access denied类信息。如果短时间内出现大量失败登录,通常意味着有人在尝试爆破密码。社区版可以通过连接控制插件CONNECTION_CONTROL实现失败次数限制和延迟惩罚。该插件会记录每个来源地址的连续失败次数,超过阈值后自动延迟后续连接请求,从而增加暴力破解成本。
INSTALL PLUGIN CONNECTION_CONTROL SONAME 'connection_control.so'; INSTALL PLUGIN CONNECTION_CONTROL_FAILED_LOGIN_ATTEMPTS SONAME 'connection_control.so'; SET GLOBAL connection_control_failed_connections_threshold = 5; SET GLOBAL connection_control_min_connection_delay = 60000;
上述配置表示同一来源连续失败5次后,连接请求将被延迟至少60秒。阈值设置要结合业务场景,不要过低导致正常用户输错密码后长时间无法登录,也不要过高给了攻击者足够多的尝试窗口。Windows平台的插件文件后缀通常是connection_control.dll,Linux平台则是connection_control.so。安装后可以通过查询information_schema.plugins确认插件状态。
除了防爆破,还应该定期检查当前连接和空闲连接。执行SHOW PROCESSLIST;可以观察是否有异常来源、长时间运行的SQL或异常高并发。对于业务不活跃的连接,可以通过wait_timeout和interactive_timeout合理缩短存活时间,避免空闲连接被长期占用,也减少凭据泄露后被人利用的时间窗口。远程访问安全是一个组合策略,单靠某一项无法解决所有问题,只有网络、账户、加密和审计同时配合,才能把风险控制在可接受范围。