PL/SQLのプロシージャやパッケージは、「どのユーザーの権限で実行されるか」をAUTHID句で指定できます。デフォルトのDefiner Rights(定義者権限)は作成者の権限で実行され、Invoker Rights(呼び出し者権限)は呼び出したユーザーの権限で実行されます。シンプルな違いに見えますが、選択を誤ると「権限不足エラー」「想定外の権限昇格」「ロールが効かない」といった事故に直結します。
多くの解説では「定義者権限はカプセル化、呼び出し者権限はライブラリ向け」のような大雑把な説明で終わりますが、実装現場で本当に欲しいのは「自分の関数はどちらにすべきか」を即決できる判断基準です。加えて12c以降のINHERIT PRIVILEGESやBEQUEATH句といった重要な拡張を理解しないと近代的な設計はできません。
この記事ではAUTHID選択の判断フローを中心に、内部の権限解決メカニズム、INHERIT PRIVILEGES、ビュー・トリガーでの動作差、名前解決の罠、切り替え時の注意点、アンチパターン6選、FAQまで2026年版で整理します。権限設計の全体像(スキーマ分離・GRANT戦略)を学びたい場合は権限分離とセキュリティパッケージを、AUTHIDのベストプラクティスはAUTHID設計のベストプラクティスを併せてご覧ください。
この記事でわかること
- Definer Rights/Invoker Rightsの内部動作と権限解決のタイミング
- 「迷ったらどっちか」を即決できる判断フローチャート
- ロールがDefinerでは効かないという最重要ルール
- Definer Rightsの典型ユースケース(API公開・カプセル化)
- Invoker Rightsの典型ユースケース(ライブラリ・マルチテナント)
- 12c以降のINHERIT PRIVILEGES/BEQUEATH句の使い方
- 名前解決の罠(シノニム・ビュー・スキーマ修飾)と回避策
- パッケージ・トリガー・ビューでのAUTHID挙動の差
- Definer→Invokerへの安全な切り替え手順
- 本番で踏むアンチパターン6選
30秒でわかるAUTHID選択の結論
忙しい読者向けの結論先出しです。
| 結論 | 理由・効果 |
|---|---|
| ① 業務APIならDefiner Rights(デフォルト) | 呼出し元に表権限を渡さずAPIだけ公開できる |
| ② ユーティリティならInvoker Rights | 呼出し者の権限と可視性を尊重しライブラリ的に使える |
| ③ ロールはDefinerで無効化される | 必要な表権限はロール経由ではなく直接GRANTする |
| ④ Invoker Rightsで使うパッケージは名前解決が呼出し者基準 | シノニム・ビューが想定外の表に解決される事故防止に明示的修飾を |
| ⑤ 12c以降はINHERIT PRIVILEGESで権限昇格を制御 | 呼出し者がdefinerに不当に昇格する攻撃を防げる |
| ⑥ ビューのBEQUEATHもAUTHIDと同じ概念 | BEQUEATH DEFINER/CURRENT_USERでビューの権限解釈を制御 |
| ⑦ 切り替えはテスト必須 | Definer↔Invokerの切替は権限と名前解決の両方を変える |
権限解決の内部動作|どのタイミングで何が起きるか
AUTHIDは「誰の権限で実行するか」を決めるだけでなく、名前解決(テーブル名がどのスキーマを指すか)にも影響します。両者を区別して理解することが本質です。
Definer Rights(AUTHID DEFINER)
関数を作成したスキーマ(DEFINER)の権限と名前解決コンテキストで実行されます。SELECT * FROM employeesと書いてあればDEFINERのスキーマのemployeesを参照し、その表へのアクセス権限はDEFINER所有者を見て判定。呼び出し者がempolyeesに権限を持っていなくても実行できるのが特徴です。これは「呼び出し者を信頼してDEFINERの強い権限を貸す」モデルなのでカプセル化と過剰権限の両側面があります。
Invoker Rights(AUTHID CURRENT_USER)
関数を呼び出したユーザー(CURRENT_USER)の権限と名前解決コンテキストで実行されます。同じSELECT * FROM employeesでも呼出し者のスキーマのemployeesを参照しに行き、アクセス権限も呼出し者で判定。マルチスキーマで「誰が呼ぶか」によって参照表が変わるのが特徴で、共通ライブラリ的な使い方に適します。反面、想定したテーブルが呼出し者のスキーマにないとORA-00942: table or view does not existになります。
権限解決の重要ルール|ロールはDefinerで無効
ここが最も重要なポイントです。Definer Rightsで実行されるPL/SQL内では、DEFINER所有者がロール経由で持っている権限は完全に無視されます。たとえばDEFINERがDBAロールでSELECT権限を持っていても、PL/SQL内ではその権限は使えません。必ず直接GRANTで付与した権限のみが有効です。Invoker Rightsではロールが有効になる、という違いも使い分けの判断基準になります。
-- ユーザーDATAがemployees表を所有
CREATE USER data IDENTIFIED BY ...;
GRANT CREATE TABLE TO data;
GRANT CREATE PROCEDURE TO data;
-- ユーザーAPPがpkg_empを作成(DATAの表を参照)
CREATE USER app IDENTIFIED BY ...;
GRANT CREATE PROCEDURE TO app;
GRANT SELECT ON data.employees TO app; -- ★直接GRANTが必須
-- ユーザーCALLERがpkg_empを呼び出す
CREATE USER caller IDENTIFIED BY ...;
GRANT EXECUTE ON app.pkg_emp TO caller;
-- CALLERにはdata.employeesへの直接権限なし
-- ① Definer Rightsの場合(呼出し者に表権限不要)
CREATE OR REPLACE PACKAGE app.pkg_emp_def AUTHID DEFINER AS
PROCEDURE list_all;
END;
/
CREATE OR REPLACE PACKAGE BODY app.pkg_emp_def AS
PROCEDURE list_all IS
BEGIN
FOR rec IN (SELECT employee_id FROM data.employees) LOOP
DBMS_OUTPUT.PUT_LINE(rec.employee_id);
END LOOP;
END;
END;
/
-- CALLERが呼ぶ → 成功(APPの権限で実行されるため)
CONNECT caller/...
EXEC app.pkg_emp_def.list_all; -- OK
-- ② Invoker Rightsの場合(呼出し者に表権限必要)
CREATE OR REPLACE PACKAGE app.pkg_emp_inv AUTHID CURRENT_USER AS
PROCEDURE list_all;
END;
/
CREATE OR REPLACE PACKAGE BODY app.pkg_emp_inv AS
PROCEDURE list_all IS
BEGIN
FOR rec IN (SELECT employee_id FROM data.employees) LOOP
DBMS_OUTPUT.PUT_LINE(rec.employee_id);
END LOOP;
END;
END;
/
-- CALLERが呼ぶ → 失敗(CALLERにdata.employees権限なし)
CONNECT caller/...
EXEC app.pkg_emp_inv.list_all; -- ORA-00942
-- 解決:CALLERに直接GRANT
GRANT SELECT ON data.employees TO caller;
EXEC app.pkg_emp_inv.list_all; -- OK
「ロールでGRANTしたのにPL/SQL内で見えない」はDefiner Rightsの最大の落とし穴です。本番リリースでは「動作確認はDBA権限で行い、実運用では権限不足で失敗」というケースが頻発します。PL/SQLが参照する表には必ず所有者から直接GRANTすることを徹底してください。
AUTHID選択の判断フロー|迷ったらこれで決める
実装時に「Definer/Invokerどちらにすべきか」を即決できる判断フローを示します。3つの質問に答えるだけで決まります。
質問1:呼び出し元に表権限を渡したくないか?
「APIだけ公開して内部の表は隠したい」「呼び出し者にSELECT/INSERT権限を直接付与したくない」のようなカプセル化要件がある場合はDefiner Rights。APIで業務操作だけを抽象化して提供できます。
質問2:呼び出し者ごとに参照する表が変わるべきか?
「ユーザーAが呼ぶとユーザーAの表、ユーザーBが呼ぶとユーザーBの表」のようなマルチテナント/ライブラリ要件がある場合はInvoker Rights。同じコードで呼出し者ごとに違う表を扱えます。
質問3:呼び出し者のロール権限を反映させたいか?
「呼出し者がREADER_ROLEならRead-only、WRITER_ROLEならRead/Write」のような動的アクセス制御が必要ならInvoker Rights。Definer RightsではPL/SQL内でロールが効かないため実現できません。
| 状況 | 選択 |
|---|---|
| 業務APIで呼出し者に内部表を見せたくない | Definer Rights |
| 共通ユーティリティでスキーマごとに違う表を扱う | Invoker Rights |
| マルチテナントで呼出し者の可視性をそのまま反映 | Invoker Rights |
| 呼出し者のロール(READER/WRITER等)で挙動を切替 | Invoker Rights |
| 監査・特権操作を所有者の権限で安全に実行 | Definer Rights |
| 動的SQL(DDL等)を呼出し者の権限で実行 | Invoker Rights+AUTHID CURRENT_USER |
| 迷っていてとりあえず動かす | Definer Rights(デフォルト) |
INHERIT PRIVILEGES(12c以降)|権限昇格を防ぐ重要機能
Oracle 12cから導入されたINHERIT PRIVILEGESは、Invoker RightsでDefinerが呼び出される際の権限昇格を制御する重要な機能です。これを理解せずに本番運用すると、権限の弱いユーザーが強い権限を持つDefinerプロシージャを通じて意図しない操作を実行できてしまうセキュリティ事故が起きます。
問題:Invoker Rights関数の中で起きる権限昇格
Invoker Rights関数はCALLERの権限で動きますが、その中で別のDefinerプロシージャを呼ぶとその瞬間からDEFINERの強い権限に切り替わります。これは便利な反面、悪意あるDEFINERが用意した関数を呼び出し者が知らずに呼ぶと権限昇格攻撃になり得ます。12c以降ではこれをブロックするセキュリティ機構が入りました。
INHERIT [ANY] PRIVILEGES の動作
呼び出し元ユーザー(CALLER)が「DEFINERの権限を借りてもよい」と明示的にGRANTしないと権限昇格が起きません。これによりDEFINERは「自分が信頼するCALLERだけが自分のDefinerを実行時に権限借用できる」設計が可能になります。
-- 12c以降では、Invoker Rights関数からDefiner関数を呼ぶときに -- CALLERがDEFINERにINHERIT PRIVILEGESをGRANTしている必要がある -- ① 通常の状態(特に何もしない) CREATE OR REPLACE PROCEDURE strong_def_proc AUTHID DEFINER AS BEGIN -- 強い権限が必要な処理 EXECUTE IMMEDIATE 'GRANT SELECT ON sensitive_table TO public'; END; / -- ② Invoker Rights側からの呼び出し CREATE OR REPLACE PROCEDURE caller_proc AUTHID CURRENT_USER AS BEGIN strong_def_proc; -- ← CALLERが信頼してINHERITしないと失敗 END; / -- ③ CALLERユーザーがDEFINERに権限借用を許可 CONNECT caller/... GRANT INHERIT PRIVILEGES ON USER caller TO definer_user; -- これでcaller_procからstrong_def_procを呼べる -- ④ デフォルトで全員から借用権を取得(推奨されない) GRANT INHERIT ANY PRIVILEGES TO definer_user; -- 全ユーザーから借用OK -- ⑤ 取り消し(権限昇格を完全に遮断) REVOKE INHERIT PRIVILEGES ON USER caller FROM definer_user;
Oracle 12cにアップグレードした既存システムでは移行期間中の互換性のためINHERIT PRIVILEGES がデフォルトでPUBLICに付与されている場合があります。セキュリティを強化したい場合はREVOKE INHERIT PRIVILEGES ON USER ... FROM PUBLIC;を実行し、必要なペアだけに明示的にGRANTする運用に切り替えてください。
名前解決の罠|シノニム・ビューでスキーマが切り替わる
Invoker Rightsで最も事故が起きやすいのが名前解決です。コード内に書いたSELECT * FROM employeesがどのスキーマのemployeesを指すかが、AUTHIDによって変わります。シノニムやビューが絡むと特に複雑になります。
-- DATA, APP, CALLER の3スキーマで、それぞれにemployees表があるとする CREATE TABLE data.employees(...); CREATE TABLE app.employees(...); CREATE TABLE caller.employees(...); -- APPがプロシージャを作成 CREATE OR REPLACE PROCEDURE app.show_emps AUTHID DEFINER AS BEGIN -- 修飾なし → APP.employees を参照(DEFINERのスキーマ) FOR rec IN (SELECT * FROM employees) LOOP NULL; END LOOP; END; / CREATE OR REPLACE PROCEDURE app.show_emps_inv AUTHID CURRENT_USER AS BEGIN -- 修飾なし → CALLER.employees を参照(呼出し者のスキーマ) FOR rec IN (SELECT * FROM employees) LOOP NULL; END LOOP; END; / CONNECT caller/... EXEC app.show_emps; -- → APP.employees を見る EXEC app.show_emps_inv; -- → CALLER.employees を見る -- ✅ 確実に同じ表を見るには完全修飾名を使う CREATE OR REPLACE PROCEDURE app.show_emps_safe AUTHID CURRENT_USER AS BEGIN -- 完全修飾でDATA.employees を明示 FOR rec IN (SELECT * FROM data.employees) LOOP NULL; END LOOP; END; /
シノニムの罠:シノニムも名前解決の対象なので、PUBLICシノニムやスキーマ別シノニムが絡むと「どの表に解決されたか」を追うのが困難になります。Invoker Rightsで信頼性が必要な処理では必ず完全修飾名(owner.table)を使うのが安全策。デバッグ困難なバグを未然に防げます。
ビュー・トリガーでの特殊事情
AUTHIDはプロシージャ/関数/パッケージだけの概念ではなく、ビューやトリガーにも類似の機構があります。個別の挙動を押さえておかないと「同じ感覚で書いたのに動作が違う」事故が起きます。
ビューの BEQUEATH 句(12c以降)
ビューは伝統的に常にDefiner権限で評価されていましたが、12cからBEQUEATH CURRENT_USERを指定してInvoker Rights相当の動作にできるようになりました。マルチテナント環境で呼び出し者ごとに違う行を返すビューを作りたい場合に有用です。
-- 通常のビュー(BEQUEATH DEFINER がデフォルト) CREATE OR REPLACE VIEW v_emp_def AS SELECT * FROM employees; -- BEQUEATH CURRENT_USER で呼出し者の権限・名前解決を使う CREATE OR REPLACE VIEW v_emp_inv BEQUEATH CURRENT_USER AS SELECT * FROM employees; -- → 呼出し者のスキーマのemployeesを参照 -- 設定確認 SELECT view_name, bequeath FROM user_views WHERE view_name LIKE 'V_EMP%';
トリガーは常にDefiner権限
トリガーは定義者権限のみで実行されます。AUTHIDを指定することはできません。トリガー内で外部スキーマの表を更新する場合は定義者に直接GRANTが必要です。これはトリガーの本質である「テーブル変更時の自動処理」を一貫した権限で実行するための設計上の制限です。
パッケージ全体に対する単一指定
AUTHIDはパッケージSPECに書き、パッケージ内の全サブプログラムに一括適用されます。個別のプロシージャごとにDefiner/Invokerを混在できないので、混在が必要なら別パッケージに分割する設計判断が必要です。
Definer↔Invoker切り替えの安全な手順
既存のDefiner RightsをInvoker Rightsに切り替えたい(あるいはその逆)場合、権限と名前解決の両方が変わるため本番影響が読めないことが多いです。次の手順で安全に進めます。
-- 切り替え前の調査スクリプト
-- ① 対象パッケージの呼び出し元一覧
SELECT owner, name, type, referenced_owner, referenced_name
FROM dba_dependencies
WHERE referenced_owner = 'APP'
AND referenced_name = 'PKG_TARGET'
ORDER BY owner, name;
-- ② 対象パッケージが参照する表・ビュー
SELECT referenced_owner, referenced_name, referenced_type
FROM dba_dependencies
WHERE owner = 'APP' AND name = 'PKG_TARGET'
AND referenced_type IN ('TABLE','VIEW','SYNONYM');
-- ③ 呼出し者候補ユーザーの権限チェック
-- (Invoker化したときに必要な権限を持っているか)
SELECT grantee, privilege, table_name, owner
FROM dba_tab_privs
WHERE grantee = 'CALLER_USER'
AND owner = 'DATA';
-- ④ シノニム解決の確認(INVOKER化で名前解決が変わる影響)
SELECT owner, synonym_name, table_owner, table_name
FROM dba_synonyms
WHERE table_name IN ('EMPLOYEES','ORDERS','PRODUCTS');
-- 切り替え後の動作確認
-- ⑤ AUTHID変更
ALTER PACKAGE app.pkg_target COMPILE SPECIFICATION
REUSE SETTINGS;
-- AUTHIDはSPEC編集が必要なのでCREATE OR REPLACEで書き直し
-- ⑥ 全呼び出し元から動作テストを実施
-- ⑦ 監査ログで権限不足エラー(ORA-00942/01031)を確認
-- ⑧ ロールバック準備:旧版を別名で保存しておく
-- CREATE OR REPLACE PACKAGE app.pkg_target_v1 AS ...
本番切り替えは絶対に段階的に。①ステージング環境で全呼び出し元から動作テスト、②本番では深夜帯など低負荷時間に切り替え、③切替後30分は監査ログでORA-942/ORA-1031(権限不足)を監視、④旧版にロールバックできるよう旧コードを別名で保管。権限切替の事故は復旧が難しいので慎重すぎるくらいでちょうど良いです。
本番で踏むアンチパターン6選
① ロール経由の権限を当てにしてDefiner Rightsで実装
「DBAロールを持っているから動く」と開発時に確認した実装が、本番で個別ユーザに切り替えた瞬間にORA-00942。必ず直接GRANTで必要権限を付与してください。
② Invoker RightsなのにシノニムやビューでDefinerスキーマを参照
「Invokerだから呼出し者の表を見るはず」と思っていたらPUBLICシノニムが先に解決されてDefinerの表を見ていた、というのは典型的バグ。完全修飾名で書くか、シノニムを使うならUSER_SYNONYMSで解決先を確認する習慣を。
③ 動的SQL内でロールが効くと勘違い
Definer Rightsの動的SQL(EXECUTE IMMEDIATE)でもロール権限は無効です。「動的SQLは別モードで動くはず」という誤解で権限不足エラーを踏みます。動的でも静的でも同じルールです。
④ パッケージ内で部分的にAUTHIDを変えようとする
AUTHIDはパッケージ単位で1つだけ。個別プロシージャでAUTHIDを混在させたい場合はパッケージを分割するしかありません。インラインで切り替える方法はないので、責務分割の設計判断が必要。
⑤ 12cでINHERIT PRIVILEGESを意識しない
古いシステムをアップグレードした後、INHERIT PRIVILEGESの仕組みを理解せず使い続けると気付かない権限昇格経路が残ります。本番ではPUBLICからINHERIT PRIVILEGESを必ずREVOKEし、必要なペアだけに明示的GRANTする運用に。
⑥ 切り替え前に呼び出し元を全部洗い出さない
Definer→Invoker切替で「主要な呼び出し元しか確認しなかった」結果、夜間バッチや別チームのスクリプトで権限不足エラーが頻発。DBA_DEPENDENCIESで全呼び出し元を必ず洗い出してから切り替えてください。
よくある質問
AUTHID DEFINERと書いておくとコードの意図が明確になり、レビュー・保守時に判断材料になります。CURRENT_USERは現在の権限コンテキストのユーザー、SESSION_USERはログイン時のユーザーです。Definer Rightsで実行中はCURRENT_USER=DEFINER、SESSION_USER=ログインユーザー。Invoker RightsではCURRENT_USER=SESSION_USER。SYS_CONTEXT('USERENV', ...)で取得して監査・分岐に使えます。pkg_b内では DEFINER の権限で実行されます。逆に Definer pkg_a が Invoker pkg_b を呼ぶと、pkg_b内では呼出し元のSESSION_USERの権限で実行されます。12c以降ではINHERIT PRIVILEGESによる制御が必要なので注意。関連記事で深掘りする
権限管理に関連する記事を集めました。
- 【PL/SQL】プロシージャの権限管理とAUTHIDの設計ベストプラクティス(GRANT戦略・SECURE DEFINERパターン)
- 【PL/SQL】権限分離とセキュリティパッケージ(Invoker Rights活用例)(スキーマ分離戦略)
- 【PL/SQL】マルチスキーマ環境での権限管理とInvoker Rights設計(大規模環境の権限設計)
- 【PL/SQL】プロシージャ・ファンクション完全ガイド|10軸比較・戻り値5種・DETERMINISTIC/RESULT_CACHE・AUTHID(基本構文)
- 【Oracle】ユーザ・権限管理完全ガイド(DB全体の権限管理)
- 【PL/SQL】動的SQLのセキュアな書き方完全ガイド(動的SQLでの権限注意点)
- 【PL/SQL】パッケージ設計でコード管理と再利用性を極める(パッケージレイヤとAUTHIDの組み合わせ)
- 【PL/SQL】AUTONOMOUS_TRANSACTIONで独立処理を実装する完全ガイド(権限とトランザクションの組み合わせ)
- 【PL/SQL】依存オブジェクトとINVALID再コンパイルの制御(AUTHID変更時のINVALID影響)
- 【Oracle】ORA-28000完全ガイド(権限関連エラー対処)
まとめ|AUTHIDを正しく選んで安全な権限設計
Definer RightsとInvoker Rightsは「どちらが優れている」のではなく、用途で正しく選ぶものです。内部動作・名前解決・ロールの扱いを理解し、12c以降のINHERIT PRIVILEGESを使いこなせれば、権限事故のないPL/SQL設計が可能になります。本記事の要点を7つに集約します。
- 業務APIで内部表を隠したいならDefiner、ライブラリ的な共通処理ならInvoker
- Definer RightsではロールでGRANTした権限が無効。必ず直接GRANT
- Invoker Rightsの名前解決は呼出し者基準。完全修飾名で曖昧さを排除
- 12c以降はINHERIT PRIVILEGESで権限昇格を制御(PUBLICからのREVOKE推奨)
- ビューはBEQUEATH句で同等の制御。トリガーは常にDefiner固定
- パッケージ単位でAUTHIDは1つ。混在が必要なら分割
- 切り替えは依存先全洗い出し→ステージング検証→段階的本番展開
権限設計は「分かったつもり」で進めると本番で必ず躓く領域です。本記事の判断フローと内部動作を押さえてから実装することで、権限不足エラー・想定外の権限昇格・ロール無効化の罠といった典型事故を構造的に防げます。深掘りが必要な領域はAUTHID設計のベストプラクティスやセキュリティパッケージ設計を併せて参照してください。

