definer 会导致权限提升,因其使存储过程以定义者而非调用者权限执行;若低权限用户创建 definer='root'@'%' 的过程,调用时即获得 root 权限,可越权操作。

为什么 DEFINER 在存储过程中会引发权限提升?
MySQL 存储过程默认以 DEFINER 身份执行,而不是调用者(INVOKER)。如果一个低权限用户能创建或修改一个 DEFINER = 'root'@'%' 的过程,后续调用时就会以 root 权限运行——哪怕该用户自己连 SELECT 都没开。
常见错误现象:Access denied for user 'app_user'@'%' to database 'mysql' 这类报错不会出现,因为过程在执行时已“切换身份”,实际操作可能悄无声息地删库、读取系统表、写文件。
- 只要用户有
CREATE ROUTINE和EXECUTE权限,就可能被利用来植入高权限过程 -
sql_mode中未启用NO_DEFINER(该模式不存在)或类似限制,MySQL 不校验DEFINER是否真实存在或是否被授权 - 备份恢复、主从同步时,
DEFINER会被原样复制,容易把高权限上下文带到新环境
如何安全设置 DEFINER 或改用 SQL SECURITY INVOKER?
最直接的解法不是禁用 DEFINER(它本身合法且有用),而是控制谁可以设、设成谁、以及默认走什么安全模型。
- 新建过程时显式声明:
CREATE DEFINER = CURRENT_USER SQL SECURITY INVOKER PROCEDURE p1() ...—— 这样过程以调用者权限运行,不越权 - 避免写死
DEFINER = 'root'@'%';如必须指定,应限定为最小必要账号,例如'proc_admin'@'localhost',且该账号仅授予过程所需的具体库表权限 - 全局禁止普通用户指定任意
DEFINER:启动 MySQL 时加参数--skip-grant-tables❌(危险!别这么干);正确做法是回收SET USER类权限,并确保mysql.proc表不可被非管理员直接更新 - 检查现有过程:
SELECT name, definer, security_type FROM mysql.proc;,重点排查security_type = 'DEFINER'且definer权限过高的项
为什么 CREATE PROCEDURE 默认不走 INVOKER?
MySQL 设计上把 DEFINER 当作默认行为,是为了支持“封装逻辑+集中授权”的场景,比如 DBA 写一个清理归档的存储过程,赋予所有应用账号 EXECUTE 权限,但不想开放对原始表的 DELETE 权限。这本身没问题,问题出在权限管理松动之后。
-
SQL SECURITY默认值是DEFINER,不是可配置的全局变量,无法通过SET GLOBAL修改 - 客户端工具(如 phpMyAdmin、DBeaver)建过程时往往不显式写
SQL SECURITY,结果悄悄继承了高权限DEFINER - MySQL 5.7+ 对
DEFINER用户存在性做了一定校验,但若该用户只是被DROP掉而未被彻底清理,仍可能残留风险
生产环境必须做的三件事
别只盯着语法,要卡住入口、监控变更、限制能力。
- 禁止非 DBA 账号拥有
CREATE ROUTINE、ALTER ROUTINE、EXECUTE权限;应用账号只给EXECUTE,且仅针对白名单过程名 - 在备份脚本或 CI/CD 流水线中加入检查:
mysqldump --no-data --routines db_name | grep -E "(DEFINER|SQL SECURITY)",发现异常DEFINER立即阻断发布 - 开启通用查询日志或审计插件(如
mysql_enterprise_audit),过滤CREATE PROCEDURE和ALTER PROCEDURE操作,重点关注非预期 IP 和账号
真正难防的不是语法漏洞,而是开发顺手写了 DEFINER = 'root'@'%',测试也跑通了,上线后没人再看那行定义。











