mysql中'localhost'是硬编码字面量,仅'create user 'u'@'localhost''有效;必须配合skip-networking=on禁tcp,并确保socket路径正确、gui工具不自动解析为127.0.0.1。

CREATE USER 'u'@'localhost' 是唯一有效写法
MySQL 对 'localhost' 字面量做了硬编码处理:只要账号 Host 字段是字面量 localhost,客户端不带 -h 参数或显式指定 -h localhost,就会跳过 TCP/IP 协议栈,强制走 Unix socket 连接。这不是 DNS 解析结果,也不是通配符匹配——'u'@'127.0.0.1' 或 'u'@'%' 都无法等效替代。
常见错误包括:
- 误以为
bind-address = 127.0.0.1就能锁死本地 —— 它只限制监听地址,仍开 3306 端口,走的是 TCP 回环 - 试图用
UPDATE mysql.user SET host = 'localhost'改已有账号 —— MySQL 8.0+ 禁止该操作,执行后账号直接失效(Access denied for user 'u'@'old_host') - 建了
'u'@'localhost'却对'u'@'%'授权 —— 权限不生效,必须GRANT ... TO 'u'@'localhost'
正确流程是:
DROP USER 'u'@'old_host';CREATE USER 'u'@'localhost' IDENTIFIED BY 'pwd';GRANT SELECT ON db.* TO 'u'@'localhost';-
FLUSH PRIVILEGES;(DROP 后建议显式执行)
必须配合 skip-networking = ON 关 TCP 监听
仅靠 'u'@'localhost' 不足以“仅能通过 socket 登录”——如果 mysqld 仍在监听 TCP,任何本机进程(包括恶意脚本)都能用 mysql -h 127.0.0.1 -u u -p 绕过 socket 限制,连上 'u'@'127.0.0.1' 或更宽泛的账号。
真正禁用 TCP 的唯一方式是启用 skip-networking = ON:
- 编辑
/etc/mysql/my.cnf,在[mysqld]段添加:skip-networking = ON - 同时注释或删除
bind-address行(否则旧版本行为可能不一致) - MySQL 8.0+ 若启用了 X Plugin,还需加:
mysqlx = 0 - 重启服务:
sudo systemctl restart mysql
验证是否生效:
-
ss -tln | grep :3306应无输出 -
mysql -h 127.0.0.1 -u u -p必须报Can't connect to MySQL server on '127.0.0.1'
客户端连 socket 时别被 GUI 工具自动绕过
很多 GUI 工具(如旧版 DBeaver、Navicat、HeidiSQL)会把 host = localhost 自动解析成 127.0.0.1,导致连接强制走 TCP,哪怕你只建了 'u'@'localhost',也会失败。
应对方法:
- 在工具连接设置中,把 host 留空,或显式填入
unix_socket=/var/run/mysqld/mysqld.sock(路径需与SHOW VARIABLES LIKE 'socket';一致) - 命令行测试务必省略
-h参数:mysql -u u -p→ 走 socket;mysql -h 127.0.0.1 -u u -p→ 必须失败 - 应用代码里,不要写
host='127.0.0.1';更可靠的是直接传unix_socket='/var/run/mysqld/mysqld.sock'
登录后执行 \s,确认 Connection 行显示 Localhost via UNIX socket,而非 127.0.0.1 via TCP/IP。
socket 文件路径和权限必须到位
即使账号和配置全对,/var/run/mysqld/mysqld.sock 不存在、目录不可写、属主不是 mysql 用户,连接照样失败。
检查步骤:
- 查真实路径:
mysql -u root -p -e "SHOW VARIABLES LIKE 'socket';" - 确认文件存在且类型为 socket:
ls -l /var/run/mysqld/mysqld.sock(应为srwxrwxrwx) - 确认父目录存在、属主为
mysql、权限为drwxr-xr-x:ls -ld /var/run/mysqld/ - 若 socket 路径不对,可在
my.cnf中显式设置:socket = /var/run/mysqld/mysqld.sock,再重启
最容易被忽略的是残留账号:只要系统里还存着 'root'@'127.0.0.1' 或 'root'@'%',攻击者换种方式就能连进来——删干净再验证。











