存储过程本身不是权限防火墙,必须显式配置执行上下文:SQL Server需用WITH EXECUTE AS OWNER或专用低权限账户,避免EXECUTE AS CALLER导致越权;MySQL无EXECUTE AS机制,权限控制不可靠,应依赖视图+行级策略或应用层代理;PostgreSQL函数须显式声明SECURITY INVOKER并严格授表权限。

存储过程本身不是权限防火墙,不加控制的 EXECUTE 权限等于把表权限白送给调用者——尤其在动态 SQL、跨 schema 引用或所有权链断裂时,越权几乎必然发生。
SQL Server 必须显式设置 EXECUTE AS
默认 EXECUTE AS CALLER 模式会让过程完全继承调用者权限,查 sys.tables、执行 sp_executesql、访问其他 schema 的表都可能成功,哪怕你只给了 EXECUTE 权限。
- 安全起点是
WITH EXECUTE AS OWNER:过程始终以创建者(如dbo)身份运行,调用者只需EXECUTE权限,无需底层表权限 - 避免
EXECUTE AS SELF:它把创建时的 login 名硬编码进定义,DBA 换人后 owner 变更会导致过程执行失败 - 所有权链只在所有对象(表、视图、被调过程)同属一个 owner 时生效;一旦引用
sales.Customers而当前过程属dbo,权限检查立即回落到调用者上下文 - 如果过程里有
EXEC(@sql)或sp_executesql,这些语句默认仍走调用者上下文——必须在动态 SQL 内部再套一层EXECUTE AS,或改用sys.dm_exec_describe_first_result_set做元数据预检
MySQL 存储过程无法真正隔离权限
MySQL 没有 EXECUTE AS 机制,权限检查发生在语句执行时,而非过程定义时。只要调用者自己有 SELECT 权限,过程里写 SELECT * FROM users 就能查出来,和过程是谁创建的无关。
- MySQL 5.7 及更早版本中,存储过程权限控制基本不可靠,别指望它挡住越权
- MySQL 8.0+ 的行级策略(
CREATE POLICY)只作用于直接SELECT,对存储过程内部语句无效 - 真要收敛访问面,只能绕开表:用视图封装 + 行级策略,或把逻辑提到应用层做代理;若必须用过程,至少手动过滤字段(如
SELECT id, name FROM users WHERE status = 'active'),禁用SELECT * - 过程内不能直接执行
GRANT/REVOKE,必须用PREPARE+EXECUTE动态 SQL 绕过解析限制,且DEFINER必须是拥有GRANT OPTION的高权限账户
PostgreSQL 函数需明确 SECURITY INVOKER
PostgreSQL 默认按 SECURITY DEFINER 执行函数,权限等同于创建者(常为 postgres),极易越权。要实现最小权限,必须显式声明 SECURITY INVOKER。
- 声明后,函数体内所有
SELECT/UPDATE都校验调用者权限,所以你得单独给调用用户授表权限(如GRANT SELECT ON users TO app_user) - 别依赖
search_path隐式解析表名;写死public.users,防止因 schema 权限混乱导致意外访问 -
SECURITY DEFINER函数必须严格收敛定义者权限,禁用COPY、pg_read_file等危险操作,否则等于开放提权入口
最易被忽略的点是调用链断裂:一个设了 EXECUTE AS OWNER 的过程,如果内部又调用了另一个没设 EXECUTE AS 的过程,后者的执行上下文会回落到原始调用者——权限边界就此消失。每个被调用对象都得独立检查执行上下文配置。

















