SQL SECURITY INVOKER必须显式声明,MySQL存储过程默认为DEFINER模式;改用INVOKER需重建过程,且调用者须具备过程内所有静态SQL涉及对象的精确权限。

SQL SECURITY INVOKER 必须显式声明
MySQL 存储过程默认是 SQL SECURITY DEFINER,不写就等于写了。想切到调用者权限模式,必须在 CREATE PROCEDURE 语句里明确加上 SQL SECURITY INVOKER——漏掉这句,哪怕其他都对,执行时仍按创建者权限跑。
常见错误现象:CALL mydb.p1() 报 INSERT command denied 或 Table 'mydb.log' doesn't exist,但表明明存在、语句也合法。本质是调用者没被授过对应表的 INSERT 或 SELECT 权限,而过程又没设成 INVOKER,导致权限检查被绕过。
正确写法示例:
CREATE DEFINER = 'admin'@'localhost' SQL SECURITY INVOKER PROCEDURE mydb.p1()
BEGIN
INSERT INTO mydb.audit_log (msg) VALUES ('called by dev');
END;
调用者必须拥有过程内所有静态 SQL 的对象权限
SQL SECURITY INVOKER 下,MySQL 会在每条 SQL 执行前校验当前连接用户是否具备对应权限。注意:只校验“静态 SQL”(即写死在过程体里的语句),不校验动态 SQL(PREPARE+EXECUTE)——后者仍按调用者权限检查,但解析阶段不校验,运行时报错才暴露。
所以授权不能只给 EXECUTE,还得补全过程里涉及的每张表、每个列的实际操作权限:
- 如果过程里有
SELECT id, name FROM users,调用者需有SELECT(id, name)ONmydb.users - 如果有
UPDATE config SET value = ? WHERE key = ?,需有UPDATE(value)和SELECT(key)(WHERE 也算读) - 跨库访问(如
INSERT INTO logdb.events ...)要分别授对应库的权限,mydb.*的权限对logdb无效
重建过程才能改 SQL SECURITY 类型
MySQL 不支持直接修改已有过程的安全上下文。试图用 ALTER PROCEDURE p1 SQL SECURITY INVOKER 会报错 ERROR 1064(语法不支持)。唯一可靠方式是重建:
步骤如下:
- 先用
SHOW CREATE PROCEDURE mydb.p1拿到完整定义 - 把原语句中的
SQL SECURITY DEFINER替换为SQL SECURITY INVOKER(若原来没写,就加在CREATE后、PROCEDURE前) - 执行
DROP PROCEDURE IF EXISTS mydb.p1 - 再执行修改后的
CREATE语句
注意:重建过程会丢失原有 DEFINER,如果还想保留,需在新语句中显式写出,例如 CREATE DEFINER='dev_admin'@'%' SQL SECURITY INVOKER PROCEDURE ...。
INVOKER 模式下 EXECUTE 权限本身不触发表权限校验
很多人误以为给了 EXECUTE 就能调用,其实不然。EXECUTE 权限只控制“能不能发起 CALL”,不控制“过程里能不能执行成功”。只要过程声明了 INVOKER,后续每一步都独立校验调用者权限——哪怕 CALL 成功返回,过程中某条语句仍可能因权限不足中断并回滚。
排查建议:
- 用
SELECT db, name, security_type FROM mysql.proc WHERE name = 'p1'确认确实是INVOKER - 用
SHOW GRANTS FOR 'caller'@'host'查看调用者是否真有过程里每张表的对应权限 - 避免用
'user'@'%'授权后却从localhost连接——这是两个不同账户,权限不互通
真正容易被忽略的是:过程里哪怕只读一张表,调用者也必须有该表的 SELECT 权限;哪怕只插入一列,也得有那列的 INSERT 权限。没有捷径,也没有隐式继承。


















