sql server、mysql、postgresql 存储过程/函数的权限控制均存在执行上下文切换导致的提权风险:sql server 避免 execute as owner,应显式指定最小权限用户并严格授权;mysql 需指定低权限 definer 并慎用 sql security definer;postgresql 的 security definer 函数须显式限定 schema 并重置 search_path;跨库/跨 schema 访问必须单独授予对应权限,否则权限链断裂。

SQL Server 中 EXECUTE AS 的权限继承陷阱
存储过程默认以调用者上下文执行,但加了 EXECUTE AS 后,权限就转到指定用户身上——这看似可控,实则极易导致权限意外扩散。比如用 EXECUTE AS OWNER,而 owner 是 db_owner,那任何能执行该过程的低权限用户,都能借道完成删表、读敏感视图等操作。
- 永远避免
EXECUTE AS OWNER,除非 owner 是一个专设的、权限严格受限的账户(如proc_executor) - 优先用
EXECUTE AS 'username'显式指定最小权限主体,并确保该用户只被授予过程内实际需要的权限(如仅SELECT某张日志表) - 若过程需跨数据库访问,目标库必须开启
TRUSTWORTHY OFF(默认值),且使用证书签名或模拟登录,不能靠EXECUTE AS穿透
MySQL 存储过程中 DEFINER 和 SQL SECURITY 的实际影响
MySQL 不支持 EXECUTE AS,靠 DEFINER 和 SQL SECURITY 控制权限边界。问题在于:设成 SQL SECURITY DEFINER 时,过程以 DEFINER 账户身份运行;但如果 DEFINER 是 root@'%' 或高权限账号,调用者就等于间接获得了 root 权限。
- 创建过程时显式指定低权限
DEFINER,例如DEFINER='proc_runner'@'localhost',并确认该账号只拥有过程内涉及的表的必要权限 - 始终使用
SQL SECURITY DEFINER(这是默认值),但绝不能依赖它来“提权”——它的作用只是固定执行身份,不是授权机制 - 若过程只需调用者自身权限,改用
SQL SECURITY INVOKER,此时权限检查发生在调用时刻,更符合最小特权原则
PostgreSQL 函数中 SECURITY DEFINER 的签名与搜索路径风险
PostgreSQL 的 SECURITY DEFINER 函数确实能切换执行角色,但有两个隐藏雷区:一是函数体内的对象引用不自动限定 schema,二是 search_path 继承调用者的设置,可能让恶意用户通过篡改自己的 search_path 注入同名函数或表。
- 所有表、序列、函数引用必须显式带上 schema,例如写成
public.users而非users - 在函数开头强制重置
search_path:SET search_path = 'pg_catalog', 'public'; - 函数必须用
CREATE OR REPLACE FUNCTION ... SECURITY DEFINER显式声明,且仅由具备USAGE权限的 schema owner 创建
跨数据库调用时权限链断裂的典型表现
当存储过程要查另一个库的表(比如 SQL Server 的 [otherdb].dbo.table),权限不会自动跨库传递。常见错误是:过程在 A 库里能执行,但一访问 B 库就报 The server principal "xxx" is not able to access the database "yyy" under the current security context.
- SQL Server:目标库需显式授予
CONNECT+ 具体对象权限给过程的执行主体(即EXECUTE AS指定的用户),不能只靠db_owner角色 - MySQL:跨库访问不受
DEFINER权限限制,但调用者必须对目标库有对应权限(SQL SECURITY INVOKER下)或DEFINER必须提前获得目标库权限(DEFINER下) - PostgreSQL:跨 schema 访问需目标 schema 的
USAGE权限,且目标对象要有相应SELECT/EXECUTE授权——这些都得单独配,不会随函数定义自动生效
最小特权不是设个 EXECUTE AS 或 DEFINER 就完事,真正麻烦的是每一条权限路径都要手动封口,漏掉任意一个 schema、一张表、一个数据库,就等于开了后门。










