MySQLのロールがビューやストアドプロシージャで反映されない隠し仕様について

目次

はじめに

ビューやストアドプロシージャについて、ロールで権限が付与されているように見えて、エラーになるケースがあります。

以下の全ての条件を満たすとエラーになります。

  1. SQL SECURITY特性がDEFINERである
  2. DEFINER属性で指定されているユーザがロールで権限付与されている
  3. ロールがデフォルトロールに設定されていない

解決策としては、ロールを使用せずに直接権限を付与するか、適切なデフォルトロールが設定されている必要があります。
ドキュメントにも間違った説明がされており、実際の挙動とは異なるので注意が必要です。

検証環境

執筆時点でLTSの最新リリースとなるMySQL 8.4 LTSを使用します。

検証準備

検証用ユーザを作成します。
定義側ユーザとしてtestユーザ
起動側ユーザとしてcallerユーザを作成します。

検証用テーブルをtable_dbスキーマに作成します。

次に、上記で作成したテーブルを参照するビューとストアドプロシージャを別スキーマviewproc_dbに作成します。

  • 単純なSELECT文と、JOINを使用したSELECT文と二通り作成します。
  • DEFINER属性をtestユーザに設定します。

補足

  • DEFINER 属性
    • ビューやストアドプロシージャなどの、オブジェクトの定義者を指定することができます。未指定の場合はそれを作成したユーザーが定義者となります。後述のSQL SECURITY 特性と組み合わせて使用することで、起動側の権限ではなく、定義側の権限で実行されるよう指定することができます。
  • SQL SECURITY 特性
    • ビューやストアドプロシージャなどの、オブジェクトが定義側の権限で実行されるか起動側の権限で実行されるかを指定できます。デフォルトは定義側です。
  • 詳細は以下ドキュメントをご参照ください。

viewproc_dbスキーマにビューを作成します。

ストアドプロシージャを作成します。

DEFINER属性にtestユーザを指定したので、起動側ユーザが誰であれ、定義側のtestユーザの権限で実行されることになります。
今回はcallerユーザで実行したいので、作成したビューやストアドプロシージャを参照するには、

  1. testユーザに対して、元テーブルへの参照権限
  2. testユーザに対して、ビューの参照権限とストアドプロシージャの実行権限
  3. callerユーザに対して、ビューの参照権限とストアドプロシージャの実行権限
    以上が必要になります。

(検証の結果、2のtestユーザに対するビューの参照権限は不要でした。見やすさの都合上付与してしまっています。)

①権限を直接付与する

直接GRANTステートメントで権限付与する場合は以下になります。

必要な権限が付与されたので、ビューやストアドプロシージャを参照できます。

②権限をロールで付与する

権限管理をロールでする場合は以下のようになります。

前提として、activate_all_roles_on_loginを有効にしておきます。
ログインの度にSET ROLEステートメントでロールを有効にする手間が省けます。

※先ほどの検証で付与した権限をリセットしておきましょう。

ロールを作成し、ロールに権限を付与し、ロールをユーザに付与します。

一旦testユーザでログインしてみて、必要な権限やロールが付与されていることを確認します。
activate_all_roles_on_loginが有効に設定されているので、SET ROLEステートメントを発行せずともロールが有効化されているのがCURRENT_ROLE()から確認できます。
また、SHOW GRANTSステートメントの結果を見ると、ロールによって権限が付与されているのが確認できます。

callerユーザに切り替えて、ビューやストアドプロシージャを参照できるかと思いきや、エラーとなってしまいました。

原因:ロールが効いていない

MySQL Bugs: #104732: In the case of using the role, there is a problem with the acl check of the view
上記MySQLバグレポートによれば、以下の問題が指摘されています。

Privileges associated with a role activated via activate_all_roles_on_login is not checked when a view is accessed.
(activate_all_roles_on_login でアクティブ化されたロールに関連付けられた権限は、ビューへのアクセス時にチェックされません。)

しかし、これはドキュメントの以下の記述と矛盾します。

Stored program and view objects that execute in definer context execute with the default roles of the user named in their DEFINER attribute. If activate_all_roles_on_login is enabled, such objects execute with all roles granted to the DEFINER user, including mandatory roles.
(定義者コンテキストで実行されるストアド プログラムおよびビュー オブジェクトは、DEFINER 属性で指定されたユーザーのデフォルト ロールで実行されます。activate_all_roles_on_login が有効になっている場合、このようなオブジェクトは、必須ロールを含む、DEFINER ユーザーに付与されたすべてのロールで実行されます。)
https://dev.mysql.com/doc/refman/8.4/en/roles.html#roles-activating

正しい挙動に即した記述に修正するには、
次のようにドキュメントを書き換える必要があります。

Roles activated via activate_all_roles_on_login or including mandatory roles privileges should not be considered for stored program and view objects.
(activate_all_roles_on_login を介してアクティブ化されたロール、または必須のロール権限を含むロールは、ストアド プログラムおよびビュー オブジェクトでは考慮されません。)

以上をまとめると、次のことがわかります。

  • ビューやストアドプロシージャでは、DEFINER属性で指定されたユーザのデフォルトロールで実行される。
  • 「activate_all_roles_on_login=1の場合は、DEFINER属性で指定されたユーザの全てのロールで実行される」とドキュメントに書かれているが、その記述は正しくない。デフォルトロールのみで実行されるというのが正しい。

③ロールをデフォルトロールに設定する

そこで、ロールをデフォルトロールに設定して、ビューを参照できるかどうか試してみます。

デフォルトロールの設定方法は、二通りあります。

  1. SET DEFAULT ROLE ... TO ~;
  2. ALTER USER ~ DEFAULT ROLE ...;

デフォルトロールの確認方法は、SHOW CREATE USERステートメントで確認ができます。

今回はtestユーザに付与したロールをデフォルトロールに設定すれば、エラー回避ができました。

callerユーザで実行して、エラーなく参照できるようになりました。

まとめ

結果を表にまとめると以下の通りです。

①権限を直接付与 ②ロールで付与 ③デフォルトロールに設定
ビュー references invalid ERROR 1356
ビュー (結合テーブル) SELECT command denied ERROR 1143
ストアドプロシージャ execute command denied ERROR 1370
ストアドプロシージャ (結合テーブル) execute command denied ERROR 1370

ドキュメントにも記載が無く、エラーメッセージも結合テーブルか否かで異なるため、エラーになったときに躓き易いです。
ロールを使用する場合は、デフォルトロールの設定が不足していないか注意を払いましょう。

スマートスタイルTECHブログについて

スマートスタイルTECHブログでは、日頃MySQLのサポート業務に従事している有資格者で構成された技術サポートチームがMySQLに関する技術情報を発信しています。データベースのお困りごとはお気軽にご相談下さい。

よかったらシェアしてね!
  • URLをコピーしました!
目次