sql server存储过程能实现数据库级权限控制,但必须显式用execute as切换上下文;默认execute as caller会使调用者权限完全穿透,导致元数据扫描、动态sql注入和跨schema越权访问等风险,故必须显式指定owner或专用低权限账户以实现安全隔离。

SQL Server 存储过程能实现数据库级权限控制,但必须显式用 EXECUTE AS 切换执行上下文;不写这句,默认就是 EXECUTE AS CALLER,调用者权限直接穿透,等于没设防。
为什么默认 EXECUTE AS CALLER 是危险的
它让存储过程完全继承调用者的权限边界。用户只要能执行过程,就能顺带执行里面所有语句——包括 SELECT * FROM sys.tables 查元数据、EXEC(@sql) 执行拼接 SQL、甚至跨 schema 访问未授权表。
- 常见报错:
The SELECT permission was denied on the object 'sys.tables',说明你没关掉 caller 模式,又没给用户授元数据权限 - 动态 SQL 在
CALLER模式下完全暴露在注入风险中;换成OWNER或指定用户后,执行范围就被严格框定 - 跨 schema 引用(如
sales.Customers)会立即中断所有权链,除非两个 schema 的 owner 完全一致
EXECUTE AS OWNER 是最稳妥的起点
它让过程始终以创建者(通常是 dbo 或专用高权限账户)身份运行,调用者只需有 EXECUTE 权限,无需底层表的任何 SELECT/INSERT 权限。
- 声明方式:
CREATE PROCEDURE dbo.GetActiveOrders WITH EXECUTE AS OWNER AS ... - 所有权链生效前提:过程里访问的所有对象(表、视图、其他过程)都得属于同一个 owner(比如全是
dbo),否则权限检查会中断 - 别用
EXECUTE AS SELF:它把创建时的 login/user 名字硬编码进过程定义,DBA 换人后容易因 owner 变更导致执行失败
需要更细粒度控制?建专用执行账户
当过程只应更新某几个字段、不能查敏感列、或需跨库访问系统视图时,EXECUTE AS OWNER 就太宽了。这时该用 EXECUTE AS 'proc_executor'。
- 该账户必须已存在,且数据库中已有对应
USER(不能只是 server-levellogin) - 只授予最小必要权限,例如:
GRANT UPDATE(status, updated_at) ON orders TO proc_executor,不给SELECT就真不能查 - 过程内部所有语句都受
proc_executor的权限约束;如果还调用了另一个没设EXECUTE AS的过程,调用链会回落到 caller 权限——这是最容易被忽略的越权缺口
MySQL 和 PostgreSQL 的现实约束
MySQL 根本没有 EXECUTE AS 机制,存储过程权限检查完全依赖调用者自身权限。哪怕你用低权限账户创建过程,只要调用者有 SELECT 权限,过程里照样能查出来。
- MySQL 5.7 及以下版本,存储过程权限控制基本不可靠,别指望它挡住越权访问
- MySQL 8.0+ 可用行级策略(
CREATE POLICY)配合视图封装,但仅作用于SELECT,且不覆盖过程内部语句 - PostgreSQL 的
SECURITY DEFINER函数虽可切换上下文,但必须严格收敛定义者权限,并禁用dblink、copy from program等危险操作
真正难的不是写那句 WITH EXECUTE AS OWNER,而是确保过程引用的所有对象都在同一 owner 下、下游依赖过程也做了同样声明、以及专用执行账户的权限真的收得够窄——漏掉任意一环,权限控制就形同虚设。











