MySQL 权限粒度必须精确到表甚至列
默认的
GRANT ALL ON *.*或
GRANT SELECT ON database.*极易造成越权,比如运营人员误查用户密码字段、开发连上生产库执行
DROP TABLE。真实权限应按角色最小化分配: 只读账号:用
GRANT SELECT (id, name, status) ON app.users TO 'reporter'@'%'限定列,而非整张表 写入账号:避开
INSERT/UPDATE/DELETE全表授权,改用存储过程封装逻辑,再授
EXECUTE权限 禁止授予
FILE、
PROCESS、
SUPER等高危权限,除非 DBA 明确审批
避免使用通配符主机名 % 导致权限扩散
'user'@'%'看似方便,但会允许任意 IP 连接,一旦密码泄露或内网失守,权限即失控。生产环境必须绑定具体网络段或跳板机 IP: 运维账号限制为跳板机:
CREATE USER 'dba'@'10.20.30.40' IDENTIFIED BY '...';应用账号限定内网子网:
CREATE USER 'app'@'172.16.0.%' IDENTIFIED BY '...';绝对不用
'user'@'localhost'混淆:它只匹配 Unix socket 或 127.0.0.1,Docker 容器内常连不上
定期清理失效账号与权限继承链
MySQL 不自动回收权限,离职人员账号、测试库残留用户、临时开通的调试权限,都可能成为越权入口。关键动作:
用SELECT User, Host FROM mysql.user WHERE account_locked = 'Y' OR password_last_changed 找长期未用账号检查权限来源:
SHOW GRANTS FOR 'dev'@'10.20.30.%';确认没被
WITH GRANT OPTION二次分发 删除前先禁用:
ALTER USER 'old_user'@'%' ACCOUNT LOCK;观察一周再
DROP USER
连接层必须启用强制 TLS + 应用级鉴权兜底
仅靠 MySQL 权限控制是单点防线。明文传输下,抓包就能拿到账号密码;而应用若未校验当前登录用户是否具备操作某条记录的业务权限(如“只能删自己创建的订单”),数据库层权限形同虚设:
强制加密连接:REQUIRE SSL写进
CREATE USER,并验证
SHOW STATUS LIKE 'Ssl_cipher';非空 应用连接串中禁用
allowPublicKeyRetrieval=true等降级参数 关键操作(如删除、金额变更)必须在应用代码里做行级校验,不能全依赖
WHERE user_id = ?—— 若 SQL 拼接出错或参数未绑定,条件就失效了 实际中最容易被忽略的是权限变更后的缓存延迟:MySQL 权限修改后,已存在的连接不会立即生效,需
FLUSH PRIVILEGES或等连接断开重连。线上调整务必配合连接池重启或超时设置。
