mysql原生不支持按过程名白名单授权;5.7仅支持库级execute(grant execute on db.*),8.0.16+才支持procedure级授权且需先授usage;逼近“仅执行某几个过程”应隔离至专用库并回收show_routine等权限。

不能只靠 GRANT EXECUTE 就实现“仅执行指定存储过程”——MySQL 原生不支持对象级白名单式授权,必须组合策略规避越权风险。
MySQL 5.7 不支持 GRANT EXECUTE ON PROCEDURE db.sp_name
直接执行该语句会报错:ERROR 1064 (42000): You have an error in your SQL syntax。5.7 只认库级授权语法:GRANT EXECUTE ON db_name.* TO 'user'@'host'。想“精确到单个过程”,必须升级到 MySQL 8.0.16+,且需先授 USAGE 权限:
GRANT USAGE ON `mydb`.* TO 'appuser'@'%'; GRANT EXECUTE ON PROCEDURE `mydb`.`p_calc` TO 'appuser'@'%';
否则会提示:ERROR 1142 (42000): USAGE command denied to user。
如何在 5.7 中逼近“仅执行某几个过程”的效果
核心思路是隔离 + 限制可见性,而非真正按对象名过滤:
- 把要开放的过程统一移到专用库(如
api_routines),只对该库授EXECUTE:GRANT EXECUTE ON `api_routines`.* TO 'appuser'@'%' - 显式回收源码查看权限,防止用户通过
SHOW CREATE PROCEDURE或查INFORMATION_SCHEMA.ROUTINES看到所有过程定义:REVOKE SHOW ROUTINE ON *.* FROM 'appuser'@'%';REVOKE SELECT ON INFORMATION_SCHEMA.ROUTINES FROM 'appuser'@'%' - 避免用
GRANT EXECUTE ON *.*—— 全局授权无法撤销单个过程权限,且暴露全部 routine 结构
CALL 报 EXECUTE command denied 的真实原因往往不是权限没给
这个错误极具误导性,实际卡点常在别处:
- 用户连接的
host和授权的host不一致,比如授的是'appuser'@'localhost',但应用连的是'appuser'@'127.0.0.1'(MySQL 视为两个账号) - 过程内部用了动态 SQL(
PREPARE/EXECUTE),MySQL 会按调用者权限检查语句中涉及的表,此时SQL SECURITY INVOKER生效,缺的是表权限,不是EXECUTE - 过程定义在
otherdb,你只对mydb授了EXECUTE,跨库调用时目标库也得授 - 过程里查了
otherdb.table,但用户没有otherdb的SELECT权限;即使SQL SECURITY DEFINER,若定义者账号已删或不可用,也会 fallback 到调用者权限校验
最容易被忽略的权限细节
EXECUTE 权限本身不解决过程内部的表访问问题。哪怕语法、对象、host 全对,只要过程里一条 SELECT 缺权限,MySQL 就统一报 EXECUTE command denied,不会告诉你具体哪张表卡住。验证路径是:
- 用
SHOW CREATE PROCEDURE p_name看定义和SQL SECURITY设置 - 查
INFORMATION_SCHEMA.ROUTINES确认DEFINER是否有效 - 手动以该用户身份尝试执行过程内每条 SQL,定位真实缺权点











