PL/SQLのプロシージャやパッケージは、単に処理をまとめるだけでなく、データベース権限の入口にもなります。AUTHID DEFINER で所有者権限として実行するのか、AUTHID CURRENT_USER で呼び出し者権限として実行するのかによって、名前解決、権限チェック、障害時の原因、セキュリティリスクが変わります。
この記事では、AUTHIDの詳細仕様そのものよりも、プロシージャやパッケージを本番公開する前に確認したい権限設計を中心に整理します。AUTHIDの内部動作を深掘りしたい場合は Invoker RightsとDefiner Rightsを使い切る完全ガイド、権限を分けた実装例は 権限分離とセキュリティパッケージ もあわせて確認してください。
AUTHID DEFINERとAUTHID CURRENT_USERの使い分け- プロシージャ・パッケージ公開時の直接GRANT設計
- ロール経由権限が原因で起きる
ORA-01031/PLS-00201 INHERIT PRIVILEGES不足によるORA-06598- 動的SQL、スキーマ分離、監査、デプロイ時のチェック項目
AUTHIDで何が変わるか
AUTHID は、ストアドPL/SQLユニットが発行するSQLの実行時の名前解決と権限チェックに影響します。Oracle公式ドキュメントでも、AUTHID は実行時の名前解決と権限チェックに関係し、DEFINER がデフォルト、CURRENT_USER がInvoker Rightsユニットであると説明されています。
AUTHID DEFINER です。まず結論:業務APIはDEFINER、共通部品はCURRENT_USER
実務では、表を直接触らせずにパッケージだけ公開したい業務APIは AUTHID DEFINER、呼び出し者の表や権限を使って動く共通ユーティリティは AUTHID CURRENT_USER を第一候補にします。ただし、どちらが常に安全という話ではありません。重要なのは「誰に何を直接GRANTするか」「動的SQLをどう制限するか」「呼び出し者の切り替わりをどう監査するか」です。
EXECUTE だけを付与します。CURRENT_USER を検討します。DEFINER RightsでAPIを公開する基本形
表への直接権限をアプリユーザーへ渡したくない場合は、APIスキーマにパッケージを作り、アプリユーザーにはパッケージの EXECUTE だけを付与します。これにより、呼び出し側は表を直接更新できず、許可された手続きだけを実行できます。
-- TABLE_OWNER が表を所有している想定
CREATE TABLE table_owner.orders (
order_id NUMBER PRIMARY KEY,
customer_id NUMBER NOT NULL,
amount NUMBER NOT NULL,
updated_at DATE DEFAULT SYSDATE
);
-- API_OWNER に必要最小限の直接権限を付与
GRANT SELECT, INSERT, UPDATE ON table_owner.orders TO api_owner;
-- API_OWNER 側で業務APIを作成
CREATE OR REPLACE PACKAGE api_owner.order_api AUTHID DEFINER AS
PROCEDURE upsert_order(
p_order_id IN NUMBER,
p_customer_id IN NUMBER,
p_amount IN NUMBER
);
END order_api;
/
CREATE OR REPLACE PACKAGE BODY api_owner.order_api AS
PROCEDURE upsert_order(
p_order_id IN NUMBER,
p_customer_id IN NUMBER,
p_amount IN NUMBER
) IS
BEGIN
MERGE INTO table_owner.orders d
USING (
SELECT p_order_id order_id,
p_customer_id customer_id,
p_amount amount
FROM dual
) s
ON (d.order_id = s.order_id)
WHEN MATCHED THEN
UPDATE SET d.customer_id = s.customer_id,
d.amount = s.amount,
d.updated_at = SYSDATE
WHEN NOT MATCHED THEN
INSERT (order_id, customer_id, amount)
VALUES (s.order_id, s.customer_id, s.amount);
END upsert_order;
END order_api;
/
-- APP_USER には表権限ではなくAPI実行権限だけを付与
GRANT EXECUTE ON api_owner.order_api TO app_user;
この設計では、APP_USER は TABLE_OWNER.ORDERS を直接更新できません。更新は必ず API_OWNER.ORDER_API を通るため、入力検証、監査、業務ルールを1か所に集められます。プロシージャ・ファンクション自体の設計観点は プロシージャ・ファンクション完全ガイド も参考になります。
ロールではなく直接GRANTが必要
PL/SQLの権限トラブルで多いのが「SQL DeveloperではSELECTできるのに、プロシージャにすると ORA-01031 や PLS-00201 になる」というケースです。原因の多くは、表権限がロール経由で付与されていることです。ストアドPL/SQLのコンパイルや実行では、ロール経由の権限を前提にしない設計が基本です。
-- NG: ロール経由だけではPL/SQL内で不足することがある GRANT SELECT ON table_owner.orders TO app_read_role; GRANT app_read_role TO api_owner; -- OK: PL/SQL所有者へ必要なオブジェクト権限を直接付与 GRANT SELECT, INSERT, UPDATE ON table_owner.orders TO api_owner; -- 呼び出し側へはAPIのEXECUTEを直接付与 GRANT EXECUTE ON api_owner.order_api TO app_user;
ORA-01031 の切り分けは ORA-01031 完全ガイド、識別子未宣言や権限不足による PLS-00201 は PLS-00201の原因と解決方法 も関連します。権限エラーはSQL単体の問題に見えて、実際にはロール・スキーマ・AUTHIDの組み合わせで起きることが多いです。
CURRENT_USERを使う場面
AUTHID CURRENT_USER は、呼び出し者の権限や現在スキーマを前提に処理したいときに使います。典型例は、複数スキーマに同じ構造の表があり、共通パッケージを各スキーマのデータに対して動かしたいケースです。
CREATE OR REPLACE PACKAGE report_util AUTHID CURRENT_USER AS
FUNCTION total_amount(p_from IN DATE, p_to IN DATE) RETURN NUMBER;
END report_util;
/
CREATE OR REPLACE PACKAGE BODY report_util AS
FUNCTION total_amount(p_from IN DATE, p_to IN DATE) RETURN NUMBER IS
v_total NUMBER;
BEGIN
SELECT NVL(SUM(amount), 0)
INTO v_total
FROM orders
WHERE updated_at >= p_from
AND updated_at < p_to;
RETURN v_total;
END total_amount;
END report_util;
/
この例の orders は、実行時には呼び出し者側の名前解決に影響されます。そのため、呼び出し者スキーマに同じ名前・同じ構造の表やシノニムが必要になる場合があります。マルチスキーマ環境での考え方は マルチスキーマ環境での権限管理とInvoker Rights設計 と相性が良い論点です。
INHERIT PRIVILEGESとORA-06598
Invoker Rightsを使う場合、近年のOracleでは INHERIT PRIVILEGES も重要です。IRユニットの所有者が呼び出し者の権限を継承できない場合、実行時に ORA-06598 が発生することがあります。これは「表のSELECT権限があるか」とは別の観点で、呼び出し者の権限を継承してよいかを制御する仕組みです。
-- APP_USER の権限を UTIL_OWNER が継承できるようにする例 GRANT INHERIT PRIVILEGES ON USER app_user TO util_owner; -- より広い権限。付与先は慎重に限定する GRANT INHERIT ANY PRIVILEGES TO util_owner;
INHERIT ANY PRIVILEGES は広い権限です。便利だからといって安易に付与すると、Invoker Rightsの安全性を損ないます。まずは対象ユーザー単位の GRANT INHERIT PRIVILEGES ON USER ... を検討し、監査対象に含めるのがよいです。
動的SQLを使う場合の注意
Definer Rightsで動的SQLを実行すると、強い権限を持つ所有者スキーマの文脈で危険なSQLが走る可能性があります。Invoker Rightsでも、呼び出し者の権限を使って想定外の表や列にアクセスするリスクがあります。AUTHIDの種類に関係なく、動的SQLは値をバインドし、識別子はホワイトリストで絞るのが基本です。
CREATE OR REPLACE PROCEDURE purge_audit_log(
p_table_key IN VARCHAR2,
p_days IN NUMBER
) AUTHID DEFINER
IS
v_table_name VARCHAR2(30);
BEGIN
v_table_name :=
CASE p_table_key
WHEN 'LOGIN' THEN 'LOGIN_AUDIT_LOG'
WHEN 'API' THEN 'API_AUDIT_LOG'
ELSE RAISE_APPLICATION_ERROR(-20010, 'Invalid table key')
END;
EXECUTE IMMEDIATE
'DELETE FROM audit_owner.' || v_table_name ||
' WHERE created_at < SYSDATE - :days'
USING p_days;
END;
/
動的SQLの安全な書き方は 動的SQLのセキュアな書き方完全ガイド で詳しく扱っています。特にDefiner Rightsのパッケージでは、外部入力をSQL識別子に使わない設計が重要です。
スキーマ分離の推奨構成
大きめの業務システムでは、表所有スキーマ、APIスキーマ、実行ユーザーを分けると権限境界が明確になります。すべてを1スキーマに詰め込むと、便利な反面、どのユーザーが何を直接触れるのか見えにくくなります。
EXECUTE だけを持ちます。-- TABLE_OWNER -> API_OWNER GRANT SELECT, INSERT, UPDATE, DELETE ON table_owner.orders TO api_owner; -- API_OWNER -> APP_USER / BATCH_USER GRANT EXECUTE ON api_owner.order_api TO app_user; GRANT EXECUTE ON api_owner.order_admin_api TO batch_user; -- APP_USER に表権限を直接渡さない -- GRANT SELECT ON table_owner.orders TO app_user; -- 原則NG
呼び出し元検証と監査
Definer Rightsは権限をカプセル化できる一方で、公開した入口を誰でも呼べると危険です。必要に応じて、呼び出し元ユーザー、クライアント識別子、アプリケーションコンテキスト、許可ロールを確認し、監査ログへ残します。
CREATE OR REPLACE PACKAGE BODY api_owner.order_admin_api AS
PROCEDURE assert_allowed IS
BEGIN
IF SYS_CONTEXT('USERENV', 'SESSION_USER') NOT IN ('BATCH_USER', 'OPS_USER') THEN
RAISE_APPLICATION_ERROR(-20020, 'not allowed');
END IF;
IF SYS_CONTEXT('USERENV', 'CLIENT_IDENTIFIER') IS NULL THEN
RAISE_APPLICATION_ERROR(-20021, 'client identifier is required');
END IF;
END;
PROCEDURE close_month(p_yyyymm IN CHAR) IS
BEGIN
assert_allowed;
-- monthly close logic
NULL;
END;
END;
/
接続プールを使うアプリケーションでは、DBユーザーは同じでもアプリ上の利用者は変わります。DBMS_SESSION.SET_IDENTIFIER やアプリケーションコンテキストを使い、誰の操作だったか追跡できる状態にしておくと障害調査と監査が楽になります。
ACCESSIBLE BYで呼び出し元を絞る
Oracleのバージョンや利用方針によっては、ACCESSIBLE BY でサブプログラムの呼び出し元を制限できます。すべてを公開パッケージ仕様に置くのではなく、内部用の処理は内部パッケージへ分け、呼び出し可能なユニットを限定すると公開面を小さくできます。
CREATE OR REPLACE PACKAGE api_owner.order_internal AUTHID DEFINER ACCESSIBLE BY (api_owner.order_api) AS PROCEDURE validate_amount(p_amount IN NUMBER); END order_internal; /
この指定は環境やバージョン差を確認したうえで使います。使えない環境でも、公開パッケージと内部パッケージを分けるだけで、レビュー時に「外部から呼ばせる処理か」を見分けやすくなります。
権限棚卸しに使うSQL
権限設計は作って終わりではありません。リリース後に不要なGRANTが増えたり、退役したAPIが残ったりします。定期的に依存関係と権限を棚卸しし、最小権限に戻す運用が必要です。
-- 自スキーマが持つオブジェクト権限
SELECT owner, table_name, privilege, grantor
FROM user_tab_privs
ORDER BY owner, table_name, privilege;
-- 自スキーマのオブジェクトへ付与している権限
SELECT grantee, table_name, privilege
FROM user_tab_privs_made
ORDER BY grantee, table_name, privilege;
-- PL/SQLユニットのAUTHID確認
SELECT object_name, procedure_name, authid
FROM user_procedures
WHERE object_type IN ('PROCEDURE', 'FUNCTION', 'PACKAGE')
ORDER BY object_name, procedure_name;
-- 依存オブジェクト確認
SELECT name, type, referenced_owner, referenced_name, referenced_type
FROM user_dependencies
WHERE type IN ('PROCEDURE', 'FUNCTION', 'PACKAGE', 'PACKAGE BODY')
ORDER BY name, referenced_owner, referenced_name;
本番前チェックリスト
EXECUTE を付与しているか。ORA-06598 が起きない権限設計になっているか。よくある失敗
SQL単体では動くのにプロシージャ化すると失敗する
ロール経由の権限に依存している可能性が高いです。対象表への権限を、プロシージャ所有者へ直接GRANTしているか確認します。
CURRENT_USERにしたら別スキーマの表を見に行ってしまう
Invoker Rightsでは実行時の名前解決が重要です。未修飾の表名を使う場合、呼び出し者側に同名オブジェクトやシノニムがあるか、意図した構造かを確認します。
Definer Rightsに強い権限を持たせすぎる
Definer Rightsは便利ですが、所有者スキーマの権限を入口から使える設計です。公開するプロシージャを絞り、動的SQLを制限し、管理者向け処理は一般APIから分離します。
まとめ
PL/SQLの権限管理では、AUTHID DEFINER と AUTHID CURRENT_USER の違いを知るだけでなく、どのスキーマへ何を直接GRANTするかが重要です。業務APIはDefiner Rightsで権限をカプセル化し、共通ユーティリティはInvoker Rightsで呼び出し者の文脈を使う、という基本方針から設計すると整理しやすくなります。
本番前には、ロール依存がないか、ORA-01031 や ORA-06598 の原因を潰せているか、動的SQLが安全か、監査できるか、不要な権限が残っていないかを確認します。AUTHIDは小さな句ですが、データベースの公開面を決める重要な設計要素です。

