authid current_user是唯一实现调用者权限继承的方式,需显式声明,否则默认authid definer以创建者权限执行;开启后sql按调用者权限校验,但动态sql拼接表名/列名会绕过校验,须用dbms_assert校验或白名单控制。

AUTHID CURRENT_USER 是唯一能真正让存储过程“继承调用者权限”的方式,不是靠配置或授予权限就能自动生效的。
为什么默认不继承调用者权限?
Oracle 存储过程默认使用 AUTHID DEFINER(定义者权限),也就是以创建者身份执行所有 SQL。哪怕调用者只有 SELECT 权限,只要创建者有 DELETE 权限,EXECUTE IMMEDIATE 'DELETE FROM emp' 就能成功执行。
必须显式声明 AUTHID CURRENT_USER
不加这句,权限继承就是空谈。加了之后,过程内每条语句(包括 EXECUTE IMMEDIATE)都会按调用者当前权限校验:
-
AUTHID CURRENT_USER后,SELECT * FROM salaries会检查调用者是否有SELECT ON salaries,而不是创建者有没有 - 如果调用者没权限,直接报
ORA-01031: insufficient privileges,不会静默越权 - 注意:
AUTHID CURRENT_USER不影响过程本身的EXECUTE权限授予逻辑——你仍需GRANT EXECUTE ON proc_name TO user
动态 SQL 里拼表名/列名会彻底废掉权限控制
即使开了 AUTHID CURRENT_USER,只要你在 EXECUTE IMMEDIATE 里拼接用户输入,权限校验就形同虚设:
- 错例:
v_table := user_input; EXECUTE IMMEDIATE 'SELECT * FROM ' || v_table;—— 输入salaries,调用者没查权限也照查 - 正确做法:用
DBMS_ASSERT.SIMPLE_SQL_NAME(v_table)校验表名,它会拒绝含点号、空格、引号等非法字符 - 列名同理,不能拼,要用白名单或
DBMS_ASSERT.ENQUOTE_NAME(col_name)包裹后再拼
权限必须手动一条条授予,没人替你兜底
AUTHID CURRENT_USER 开启后,DBA 不会自动把过程里涉及的所有表权限配给调用者。你得自己确认过程里访问了哪些对象:
- 查了
employees和departments?就得GRANT SELECT ON employees TO app_user和GRANT SELECT ON departments TO app_user - 更新了
orders的status字段?就得GRANT UPDATE(status) ON orders TO app_user - 漏一条,过程运行时直接报错;多给一条,越权风险立刻回来
最常被忽略的一点是:权限校验发生在 SQL 解析阶段,而绑定变量只影响值代入时机,对表名、列名的合法性毫无约束力。拼接才是权限失控的根源。











