用户能CREATE DATABASE是因为残留全局权限(如GRANT ALL ON .或GRANT OPTION),而非显式授予CREATE权限;需用SHOW GRANTS确认并执行REVOKE ALL PRIVILEGES, GRANT OPTION ON .清理,同时检查角色和mysql.db残留记录。

只给 SELECT 权限,用户却还能 CREATE DATABASE?检查全局权限残留
用户能创建或删除数据库,根本不是因为给了 CREATE 或 DROP 权限,而是因为账号被无意中授予了全局权限(GRANT ALL ON *.*)或残留了 GRANT OPTION。MySQL 的权限检查顺序是:先查 mysql.user 表(全局),再往下到库级、表级。只要全局权限存在,库级 REVOKE 就无效。
实操建议:
- 执行
SHOW GRANTS FOR 'username'@'host';,确认是否出现ON *.*字样 - 若存在,立刻执行:
REVOKE ALL PRIVILEGES, GRANT OPTION ON *.* FROM 'username'@'host'; - 不要依赖“没显式给就等于没有”——旧账号可能长期带全局权限,尤其从测试环境迁移来的
- 新建账号务必用库级授权起点:
GRANT SELECT ON mydb.* TO 'u1'@'%';,而非先CREATE USER再模糊补权
CREATE DATABASE 被拒绝但 DROP DATABASE 成功?注意 host 匹配和权限层级差异
CREATE DATABASE 和 DROP DATABASE 都依赖 CREATE 和 DROP 权限,但它们的生效位置不同:CREATE 权限在全局层级(*.*)才允许建库;而 DROP 权限若在库级(mydb.*)授予,用户只能删那个库——但若该用户同时有全局 DROP,就能删任意库。
常见错误现象:
- 用户执行
CREATE DATABASE test;报错Access denied,但DROP DATABASE otherdb;却成功 → 说明有全局DROP无全局CREATE - 用户能删库却不能建同名库 → 很可能是之前用
GRANT ... ON otherdb.*授过权,而otherdb已被删,权限记录仍留在mysql.db表中
验证方法:SELECT Host,Db,Drop_priv FROM mysql.db WHERE User='username';,清理残留库级记录后需 FLUSH PRIVILEGES;
MySQL 8.0+ 下禁用 CREATE DATABASE 的隐藏开关:角色与默认权限组
MySQL 8.0 引入角色(role)机制,但默认角色(如 role_admin)或预设权限组(如 mysql.backup_role)可能隐含 CREATE 权限。即使你没手动授过权,用户若被赋予某个角色,就可能获得意料之外的建库能力。
排查步骤:
- 查角色分配:
SELECT * FROM mysql.role_edges WHERE TO_HOST='username'@'host'; - 查角色权限:
SHOW GRANTS FOR 'rolename'@'%'; - 撤销角色:
DROP ROLE IF EXISTS 'rolename';或REVOKE 'rolename' FROM 'username'@'host'; - 避免使用
SET DEFAULT ROLE ALL TO ...,它会把所有角色默认激活,包括系统内置角色
应用连接后自动建库?检查 ORM 初始化行为和 mysql.proc 访问权限
某些 ORM(如 Django 1.x、SQLAlchemy + alembic)在首次连接时会尝试 CREATE DATABASE 或执行 SHOW PROCEDURE STATUS。后者虽不建库,但若用户无 EXECUTE 或 SELECT 权限访问 mysql.proc,部分驱动会回退尝试建库失败,报错掩盖真实原因。
典型错误信息:
-
Table 'mysql.proc' doesn't exist→ 实际是权限不足,不是表真丢了 -
Access denied; you need (at least one of) the CREATE privilege(s) for this operation→ 真正缺CREATE,但要确认是全局还是库级缺失
解决办法:
- 对 ORM 用户,显式授予
SELECT权限到mysql.proc(仅当必须用存储过程时):GRANT SELECT ON mysql.proc TO 'appuser'@'%'; - 更安全的做法:在应用配置里关闭自动建库/同步行为,由 DBA 统一初始化
- 禁止应用账号拥有任何全局权限,哪怕只读 ——
USAGE ON *.*是唯一允许的全局权限,且不包含CREATE
真正卡住建库/删库动作的,从来不是“没给权限”,而是“多给了不该给的权限”。权限链路长、层级多、残留难清,比写错 SQL 更容易出 silent failure。


















