mysql不支持cidr语法,须用'user'@'192.168.1.%'等通配符模拟网段,且必须删除冗余账号(如'user'@'%')、配置bind-address与防火墙,并启用skip_name_resolve才真正生效。

MySQL本身不支持CIDR(如192.168.1.0/24)语法限制IP网段,必须用'user'@'192.168.1.%'这类通配符模拟,且需配合bind-address和防火墙才真正生效。
创建用户时必须显式指定host模式
MySQL把'user'@'192.168.1.%'和'user'@'%'视为两个完全独立账号。如果你只执行CREATE USER 'user' IDENTIFIED BY 'pwd';,默认创建的是'user'@'%',后续再GRANT ... TO 'user'@'192.168.1.%'会失败或授错对象。
- 正确写法:
CREATE USER 'app'@'192.168.1.%' IDENTIFIED BY 'StrongPass123!'; -
GRANT SELECT, INSERT ON mydb.* TO 'app'@'192.168.1.%';— host字符串必须一字不差 - 执行
FLUSH PRIVILEGES;,否则权限不加载 - 检查是否创建成功:
SELECT User, Host FROM mysql.user WHERE User = 'app';
为什么改了host还是能从其他IP连上?
最常见原因是冗余账号冲突:'app'@'%'和'app'@'192.168.1.%'共存时,MySQL可能匹配到前者——尤其当'app'@'%'创建更早、或客户端DNS解析异常导致IP被转成主机名。
- 先查冗余:
SELECT Host FROM mysql.user WHERE User = 'app'; - 删掉泛化账号:
DROP USER 'app'@'%';(不是REVOKE) - 验证实际匹配结果:
SELECT USER(), CURRENT_USER();—— 后者才是生效的权限记录 - 如果
CURRENT_USER()返回'app'@'%',说明限制没起作用
防火墙和bind-address必须协同配置
只靠MySQL层限制是脆弱的:如果bind-address = '0.0.0.0'且防火墙放行所有IP,攻击者仍可暴力试探账号;反过来,如果bind-address = '127.0.0.1',连内网IP都连不上。
- 确认监听地址:
SELECT @@bind_address;或ss -tlnp | grep :3306 - 生产推荐设为具体内网IP,例如
bind-address = 192.168.1.100 - 防火墙规则必须插在链首并限定端口:
iptables -I INPUT -s 192.168.1.0/24 -p tcp --dport 3306 -j ACCEPT,再-j DROP其余 - 务必启用
skip_name_resolve = ON,避免DNS反查导致IP匹配失效
最容易被忽略的是localhost和127.0.0.1的区别:前者走Unix socket,完全绕过IP检查;后者才是TCP连接,受host字段约束。测试时别用mysql -h localhost,改用mysql -h 127.0.0.1。还有,MySQL 8.0+虽支持CIDR语法,但低版本会直接报错,上线前必须核对SELECT VERSION();。











