mysql用户绑定具体ip需在create user时指定host,如'username'@'10.20.30.40';grant中host必须严格一致,禁止误用'%';须配合bind-address限制监听ip及防火墙放行,且生产环境应启用skip_name_resolve=on确保ip精确匹配。

MySQL用户账户绑定具体IP地址
直接在创建或修改用户时指定host值,是最基础也最有效的白名单控制方式。MySQL的用户身份由username@host共同决定,同一个用户名、不同host就是完全不同的账号。
常见错误是用'%' 通配符放行所有IP,上线后才发现无法精确限制来源。生产环境应避免'%',改用具体IP或CIDR网段(注意:MySQL 5.7+才支持子网掩码写法,如'192.168.10.0/24')。
- 添加只允许从
10.20.30.40访问的用户:CREATE USER 'app_user'@'10.20.30.40' IDENTIFIED BY 'strong_pass';
- 已有用户需先删除再重建(MySQL不支持直接UPDATE
mysql.user表的host字段,否则权限不生效) - 若应用部署在K8s或NAT后,实际连接IP可能是负载均衡或网关地址,务必确认真实源IP而非容器内网IP
GRANT语句中host参数必须与CREATE USER一致
很多人以为GRANT能覆盖或修正用户绑定的IP,其实不能。GRANT只是授予权限,但目标用户必须已存在且host完全匹配,否则会静默创建一个新用户(比如'app_user'@'%'),导致白名单形同虚设。
典型误操作:
CREATE USER 'app_user'@'10.20.30.40';<br>GRANT SELECT ON mydb.* TO 'app_user'@'%';——第二行实际新建了一个不受限的用户,原用户反而没权限。
- 授予权限时,
GRANT ... TO 'user'@'host'中的host必须和CREATE USER时一模一样 - 检查当前有效用户列表用:
SELECT user, host FROM mysql.user WHERE user = 'app_user';
- 忘记绑定IP而误建了
'app_user'@'%'?立刻执行DROP USER 'app_user'@'%';,再补上正确授权
防火墙与MySQL bind-address双重防护
仅靠MySQL用户host限制不够。如果MySQL配置了bind_address = 0.0.0.0(默认监听所有接口),攻击者仍可能绕过权限系统直连本地socket或通过其他端口转发试探。必须配合系统级网络控制。
常见疏漏是只改MySQL配置却忘了开防火墙,或防火墙规则未指定端口,导致3306端口暴露在公网。
- MySQL配置文件中设置
bind_address = 127.0.0.1(仅本地)或具体内网IP(如10.20.30.10),禁止监听0.0.0.0 - Linux上用
iptables或ufw显式放行可信IP:ufw allow from 10.20.30.40 to any port 3306
- 云厂商安全组(如AWS SG、阿里云ECS安全组)必须同步收紧,优先级高于本地防火墙
排查“Access denied”时优先查host匹配结果
当应用报Access denied for user 'app_user'@'x.x.x.x',第一反应不该是密码错,而是看这个'x.x.x.x'是否在你允许的host列表里。MySQL日志里记录的host值,才是真实匹配依据。
容易被忽略的是DNS反向解析影响:如果MySQL启用了skip_name_resolve = OFF(默认),它会尝试把客户端IP反解成域名,再拿域名去匹配host字段——这会导致本该匹配'10.20.30.40'的请求,因DNS不稳定而失败。
- 确认连接时MySQL看到的host:
SELECT CURRENT_USER();
(返回'user'@'resolved_host') - 生产库务必设置
skip_name_resolve = ON,强制按IP匹配,避免DNS拖累和不确定性 - 若必须用域名白名单(如
'app_user'@'web-prod.example.com'),确保所有客户端IP都能稳定反解,且DNS无缓存污染
实际生效依赖三个环节同时正确:用户定义的host、MySQL的bind_address、系统防火墙规则。少一个,白名单就漏风。尤其注意CURRENT_USER()返回值和skip_name_resolve开关状态,这是线上排查最快的关键线索。











