MySQL Host字段不支持CIDR,有效写法是'app'@'192.168.1.%'(点加%),需先DROP USER 'app'@'%'再CREATE USER并GRANT,配合iptables -I INPUT -p tcp --dport 3306 -s 203.0.113.0/24 -j DROP及bind-address与skip_name_resolve协同配置。

MySQL用户表里怎么写IP段才有效
MySQL的Host字段不支持CIDR(比如192.168.1.0/24),也不做IP合法性校验,只认SQL模式通配符:%匹配任意长度字符串,_匹配单个字符。想放行C类网段,必须写成'app'@'192.168.1.%'——注意末尾是点加%,不是斜杠。
常见错误包括:
- 执行
UPDATE mysql.user SET Host = '192.168.1.0/24':MySQL直接忽略或报错,该写法无效 - 用
'192.168.%'试图覆盖多个子网:它会匹配192.168.100.200甚至192.168abc.example.com,边界失控 - 保留
'app'@'%'再建'app'@'192.168.1.%':MySQL按最长前缀匹配,但'%'优先级低,实际仍允许任意IP连上(因为账号已存在)
真正有效的做法是先删后建:DROP USER 'app'@'%';,再CREATE USER 'app'@'192.168.1.%' IDENTIFIED BY 'pwd';,最后GRANT时也必须用完全一致的host。
iptables封禁非授权IP必须插在链首
仅靠MySQL层限制不够——如果攻击者发SYN包,MySQL仍会响应(哪怕后续鉴权失败),暴露服务存在。必须在网络层提前拦截。
关键点在于规则顺序和端口限定:
- 必须用
iptables -I INPUT(不是-A),否则新规则排在已有ACCEPT之后,被直接放行 - 务必加上
--dport 3306,否则-s 203.0.113.0/24 -j DROP会误杀SSH、HTTP等其他服务 - 测试是否生效:从被封IP执行
telnet 203.0.113.5 3306,应超时或连接被拒绝;若返回Connection refused,说明MySQL已响应,防火墙没起作用
持久化别漏掉:iptables-save > /etc/iptables/rules.v4(Debian/Ubuntu)或service iptables save(CentOS 6)。
bind-address和skip_name_resolve必须协同配置
bind-address不是“IP白名单”,而是控制MySQL监听哪个网卡。设成127.0.0.1就彻底屏蔽所有远程连接;设成0.0.0.0或*则监听全部接口——此时防火墙规则才真正必要。
验证当前监听地址用:ss -tlnp | grep :3306。若输出是127.0.0.1:3306,外部根本连不到,iptables封3306毫无意义。
另一个常被忽略的是skip_name_resolve=ON。默认开启DNS反查,会导致连接延迟,且可能把192.168.1.100解析成域名再匹配Host,结果不一致。强制关掉它,让MySQL只比对原始IP字符串。
localhost和127.0.0.1在权限系统里是两个账号
MySQL把'user'@'localhost'和'user'@'127.0.0.1'视为完全独立的账户。localhost强制走Unix socket,绕过TCP/IP栈;127.0.0.1走TCP,受Host字段和防火墙双重约束。
这意味着:
- 只建了
'app'@'localhost',但应用连接串写host=127.0.0.1,就会报Access denied for user 'app'@'127.0.0.1' - 若需两者都支持,得分别创建并授权:
CREATE USER 'app'@'localhost'和CREATE USER 'app'@'127.0.0.1' - Docker容器内连接通常走TCP,即使网络模式是host,也得用
127.0.0.1而非localhost
真实环境中,IP段限制的脆弱点往往不在语法,而在于bind-address没调、skip_name_resolve没关、或localhost/127.0.0.1混用导致权限不生效——这些细节不检查,规则写得再准也没用。


















