SQL Server需对存储过程单独授予EXECUTE权限,MySQL依赖DEFINER身份与底层表权限,PostgreSQL则需兼顾EXECUTE权限与search_path;三者中动态SQL均为权限控制最大破口,必须参数化并校验对象名。

SQL Server 中如何给存储过程单独授权 EXECUTE 权限
直接给用户或角色授予 EXECUTE 权限,而不是数据库级别的 db_executor 角色或 db_owner,才能真正限制用户只运行指定存储过程。很多人误以为“不给 SELECT 权限就安全了”,其实只要用户有 EXECUTE 权限且存储过程里用了动态 SQL 或未参数化查询,照样可能越权读写。
实操建议:
- 用
GRANT EXECUTE ON [schema].[proc_name] TO [user_or_role]精确授权,避免用GRANT EXECUTE ON SCHEMA::dbo TO ...这种宽泛写法 - 优先基于数据库角色(如
app_reader)授予权限,而非直连用户,方便后续批量调整 - 确认目标存储过程没用
EXEC sp_executesql拼接未校验的输入——这种写法会让EXECUTE权限形同虚设
MySQL 存储过程权限控制为何总失效
MySQL 的存储过程权限模型和 SQL Server 完全不同:它不靠 EXECUTE 权限控制调用,而是依赖定义者(DEFINER)身份 + 调用者对底层表的访问权。常见现象是“明明没给用户 SELECT 权限,却能通过存储过程查出数据”,原因就是 DEFINER 是高权限账号(如 root@localhost),过程以该身份执行。
实操建议:
- 创建时显式指定低权限
DEFINER,例如CREATE DEFINER='app_proc_user'@'%' PROCEDURE ... - 若必须用高权限
DEFINER,则在过程内部加SQL SECURITY DEFINER(默认值),并确保所有 DML 都走参数化、无拼接 - 检查
show create procedure proc_name输出,确认DEFINER值是否符合预期;线上环境严禁用root或空主机名(''@'%')
PostgreSQL 函数执行权限与搜索路径陷阱
PostgreSQL 把存储过程叫函数(FUNCTION),权限控制分两层:一是调用者是否有 EXECUTE 权限,二是函数内部访问的表/视图是否在当前 search_path 下可解析。容易踩坑的是:函数里写 SELECT * FROM users,但调用者 search_path 里有另一个同名 users 表,结果查错数据甚至报错。
实操建议:
- 创建函数时加
SET search_path = pg_catalog, public,或显式写全 schema 名,如SELECT * FROM public.users - 用
REVOKE EXECUTE ON FUNCTION func_name(...) FROM PUBLIC先收回默认权限,再按需GRANT - 注意函数语言类型:
plpgsql函数可被绕过权限检查(如用EXECUTE 'SELECT ...'动态执行),而sql类型函数则严格受权限约束
动态 SQL 是权限控制的最大破口
不管哪个数据库,只要存储过程中出现字符串拼接 + 执行(SQL Server 的 sp_executesql、MySQL 的 PREPARE/EXECUTE、PostgreSQL 的 EXECUTE),就等于把权限检查交给了运行时——此时调用者权限、DEFINER、search_path 全部可能失效。
实操建议:
- 禁用用户输入直接拼进 SQL 字符串,改用参数占位符(
sp_executesql的@params、PostgreSQL 的USING子句) - 若必须动态构造对象名(如表名),先白名单校验:
IF @table_name NOT IN ('orders', 'customers') THROW ... - SQL Server 中启用
EXECUTE AS上下文切换时,注意CALLER和OWNER的权限继承差异,别让EXECUTE AS OWNER反而放大风险
权限不是设完就一劳永逸的事。最常被忽略的是:函数/过程更新后没同步检查权限,或者 DEV 环境用高权限账号测试,上线后 DEFINDER 或 search_path 没对齐。每次修改逻辑,都要重跑一遍最小权限验证。

















