MySQL 中如何禁止用户执行 DROP TABLE
直接禁止
DROP TABLE的关键是不授予
DROP权限,哪怕用户有
CREATE或
ALTER也不代表能删表。MySQL 权限是显式授权制,没给就不行。
常见错误是误以为只收回
ALL PRIVILEGES就安全了——其实如果之前用
GRANT ALL ON db.* TO 'u'@'h'授过权,必须先
REVOKE DROP ON db.* FROM 'u'@'h',再
FLUSH PRIVILEGES才生效。 对单库限制:用
REVOKE DROP ON `myapp`.* FROM 'appuser'@'%'若用户只有
SELECT, INSERT, UPDATE,默认就无法
DROP,无需额外操作 注意:
DROP权限作用于数据库级(
DROP DATABASE)和表级(
DROP TABLE),两者权限名相同,但作用对象不同
限制用户不能执行 TRUNCATE TABLE
TRUNCATE TABLE不是独立权限,它依赖
DROP权限——因为 MySQL 内部把
TRUNCATE实现为「删表重建」。只要没授
DROP,
TRUNCATE就会报错:
ERROR 1142 (42000): TRUNCATE command denied to user。
不需要单独处理
TRUNCATE,但要注意:某些 ORM 或迁移工具(如 Django
flush、Laravel
migrate:fresh)会自动发
TRUNCATE,若用户权限不足就会中断。 验证方式:用目标用户登录后执行
TRUNCATE TABLE test;,看是否报错 替代方案:如需清空数据又不让删结构,可改用
DELETE FROM table;(需
DELETE权限) 性能提示:无
DROP权限时,
TRUNCATE失败不会回滚事务,但也不会影响已有数据
禁止用户创建或删除数据库(CREATE/ALTER/DROP DATABASE)
控制数据库层级操作,关键在授权范围——不要用
ON *.*,而要限定到具体库名。例如:
GRANT SELECT, INSERT, UPDATE ON `sales_db`.* TO 'reporter'@'10.0.1.%';
这样用户连
SHOW DATABASES都只能看到
sales_db(前提是没开
show databases全局权限),更别说
CREATE DATABASE。
CREATE DATABASE和
DROP DATABASE都需要
CREATE权限作用于
*.*或对应库名;限制方法就是不授这个范围
ALTER DATABASE同理,且极少需要,生产环境通常禁用 注意
mysql系统库:普通用户不应有其任何权限,否则可能绕过限制(如修改
mysql.user表)
为什么 REVOKE 后权限仍生效?
最常被忽略的是权限缓存机制:
REVOKE或
GRANT修改的是磁盘上的权限表,但服务端会缓存权限信息,直到连接重连或手动刷新。
典型现象:执行完
REVOKE DROP ON mydb.* FROM 'u'@'h',用同一连接再试
DROP TABLE依然成功——这不是权限没生效,而是连接还拿着旧权限快照。 必须执行
FLUSH PRIVILEGES;(仅当直接改了
mysql表才强制需要;通过
GRANT/REVOKE操作一般自动刷新,但保险起见仍建议加) 更可靠的方式:让应用断开重连,或重启客户端连接 检查当前连接权限:登录后执行
SHOW GRANTS FOR CURRENT_USER;,不是
SHOW GRANTS FOR 'u'@'h'(后者查的是定义,不是当前会话实际权限)
权限配置真正难的不是语法,而是厘清「谁在什么上下文里执行什么语句」。比如一个应用用连接池复用连接,即使你改了权限,旧连接仍按老规则跑;又比如存储过程里用
SQL SECURITY DEFINER,执行时会以定义者身份校验权限,跟调用者无关。这些细节不排查清楚,光 revoke 几条命令根本拦不住。
