mysql的grant语句不支持子网掩码或cidr,如'192.168.5.0/24'会被当作字面量主机名而非网段,仅字符串前缀匹配(如'192.168.5.%')有效,且需禁用dns解析并配合防火墙实现真正子网控制。

MySQL 的 GRANT 语句不支持子网掩码(如 192.168.5.0/24),也不能在 host 字段里写 CIDR 表达式——它只会被当作文本字面量或静默截断,完全无效。
为什么 GRANT ... TO 'user'@'192.168.5.0/24' 不起作用
MySQL 解析 Host 字段时只认三种模式:%(任意长度字符串)、_(单字符)、纯字面量(如 192.168.5.100)。斜杠 / 和数字 24 不是通配符,也不触发子网计算。你执行:
GRANT SELECT ON *.* TO 'app'@'192.168.5.0/24' IDENTIFIED BY 'pwd';
MySQL 实际创建的是一个叫 'app'@'192.168.5.0/24' 的**字面量主机名**——它只匹配真正发来 Host 名为 192.168.5.0/24 的客户端(现实中不存在这种 IP)。这不是子网限制,是拼写错误。
- 查不到该账号:运行
SELECT User, Host FROM mysql.user WHERE User = 'app';就会看到那个奇怪的Host值 - 连接时错误通常是
Access denied for user 'app'@'192.168.5.10',因为 MySQL 根本没找到'app'@'192.168.5.10'这个账号,更别说'app'@'192.168.5.0/24' - MySQL 8.0+ 甚至会拒绝这种写法,报错
ERROR 1470 (HY000): String '192.168.5.0/24' is too long for host name
想限制 C 段,只能用 '192.168.5.%',但要注意它的行为边界
这是唯一被 MySQL 原生支持的“类子网”写法,但它不是网络层子网匹配,而是字符串前缀匹配:
-
'app'@'192.168.5.%'会匹配192.168.5.1、192.168.5.254,也会匹配192.168.5.abc(DNS 反查结果)、甚至192.168.5.999(MySQL 静默截断为192.168.5.99) - 它**不会**匹配
192.168.50.1或192.168.5.1234(后者超长,会被截断;前者因前缀不符而失败) - 必须重建账号:已有
'app'@'%'时,不能靠GRANT覆盖,得先DROP USER 'app'@'%';,再CREATE USER 'app'@'192.168.5.%' IDENTIFIED BY 'pwd';,最后GRANT ... TO 'app'@'192.168.5.%'; - 记得
FLUSH PRIVILEGES;—— 虽然 MySQL 8.0+ 多数情况自动刷新,但显式执行更稳妥
为什么不能只依赖 'x.x.x.%'?防火墙才是子网控制主力
'192.168.5.%' 是数据库层的“尽力而为”匹配,它发生在 TCP 连接建立、认证握手阶段。在此之前,攻击者已能探测到 MySQL 服务存在、获取版本号、甚至发起暴力破解。真要阻断整个 IP 段的连接尝试,必须在网络层动手:
- iptables 必须用
-I INPUT插入链首,否则会被前面的ACCEPT规则绕过:iptables -I INPUT -s 192.168.5.0/24 -p tcp --dport 3306 -j DROP - 注意内核兼容性:某些老系统需加载
xt_iprange模块,或改用两行规则覆盖 C 段范围 - 测试是否生效:从被封 IP 执行
telnet your-mysql-ip 3306,应表现为**超时或连接被拒绝(Connection refused)**;若返回Access denied,说明防火墙没拦住,请求已抵达 mysqld - 云环境优先用安全组(Security Group),规则比 iptables 更前置、更稳定
容易被忽略的关键协同点
单独做任何一层都可能失效。比如:
- 只设
bind-address = 192.168.5.10,但服务器有第二张公网网卡且防火墙放行了 3306,那192.168.5.%就形同虚设 - 开了
skip_name_resolve = OFF(默认),MySQL 会对每个连接做 DNS 反查,既拖慢登录,又可能因 DNS 返回域名导致'app'@'192.168.5.%'匹配失败——务必设为ON - 误以为
UPDATE mysql.user SET Host = '192.168.5.%'能修改已有用户,MySQL 8.0+ 直接禁止该操作,必须DROP + CREATE
最易出问题的地方,是把网络层和数据库层当成可互换的方案——它们作用时机、生效位置、失败表现完全不同,必须按角色各司其职。











