MySQL默认非deny-all,需显式授权控制;应禁用匿名用户、限制root远程登录、用具体IP/CIDR替代'%'、禁用GRANT OPTION、细粒度授予权限,并结合bind-address与防火墙实现纵深防御。

MySQL 默认没有 deny-all 策略,必须显式限制用户权限
MySQL 启动后默认只允许本地 root@localhost 登录,且该账户无密码(旧版本)或通过 socket 认证(新版本),但**这不等于“默认 deny”**。只要有人能连上 MySQL 实例(比如开放了 3306 端口、配置了弱密码、或存在匿名用户),就可能绕过基础防护。真正的访问控制靠的是 GRANT 和 REVOKE 的显式授权模型——它天生是 allow-list,不是 deny-list。
如何实现“默认拒绝”效果:从创建用户开始收紧
关键不是后期加防火墙,而是让每个用户只拥有最小必要权限,并禁用所有宽泛匹配。常见疏漏点:
- 避免使用
'user'@'%'这类通配主机名,改用具体 IP 或子网,如'app_user'@'10.20.30.40'或'report_user'@'192.168.1.0/255.255.255.0'(MySQL 8.0+ 支持 CIDR) - 删除匿名用户:
DROP USER ''@'localhost'; DROP USER ''@'%'; - 禁用 root 远程登录:
DELETE FROM mysql.user WHERE User='root' AND Host!='localhost'; FLUSH PRIVILEGES; - 新建用户时**不指定 WITH GRANT OPTION**,防止权限扩散
- 对生产库,避免直接授
ALL PRIVILEGES,改用细粒度授权,例如:GRANT SELECT, INSERT ON myapp.* TO 'api_user'@'172.16.0.100';
bind-address 和防火墙是第一道防线,不是替代方案
bind-address 控制 MySQL 监听哪些网卡。设为 127.0.0.1 可阻止外部 TCP 连接,但会同时禁掉同机其他容器或服务的访问;设为 0.0.0.0 则完全暴露——除非你明确需要远程管理,否则优先选 127.0.0.1 或内网 IP。但这只是网络层隔离,不能替代账号权限控制。
配套建议:
- 操作系统级防火墙(如
ufw或iptables)只放行可信源 IP 的 3306 端口 - 云环境务必关闭安全组中对 3306 的公网开放
- 若用代理(如 HAProxy、Cloud SQL Auth Proxy),让 MySQL 仅监听本地,由代理负责认证和路由
验证是否真被“deny”:用实际连接测试
配置完别只信 SHOW GRANTS,要用真实客户端验证。容易忽略的点:
- MySQL 会按
User@Host匹配,顺序是 Host 字符串长度优先(越具体越先匹配),'user'@'192.168.1.%'比'user'@'%'优先,但'user'@'192.168.1.100'又比前者更优——所以删掉宽泛账号后,记得FLUSH PRIVILEGES; - 测试时用
mysql -u testuser -h 192.168.1.200 -p,而不是只连localhost;错误信息如Access denied for user 'testuser'@'192.168.1.200'才说明网络层和权限层都生效 - 注意 DNS 反解:如果
skip_name_resolve = OFF(默认),MySQL 会尝试反向解析客户端 IP,可能导致延迟或意外匹配到'user'@'host.example.com'—— 生产建议设为ON并只用 IP 授权
最常被跳过的其实是 host 匹配逻辑和 DNS 行为,调权限前先确认 SELECT USER(), CURRENT_USER(); 返回的是你预期的那个 User@Host。











