すべてのプロダクト
Search
ドキュメントセンター

PolarDB:行セキュリティポリシー

最終更新日:Jun 03, 2024

この記事では、行セキュリティポリシーの関連コンテンツを紹介します。

説明

GRANTを通じて利用可能なSQL標準の特権システムに加えて、テーブルには、通常のクエリで返される行、またはデータ変更コマンドで挿入、更新、または削除される行をユーザーごとに制限する行セキュリティポリシーを設定できます。 この機能は、行レベルセキュリティとも呼ばれます。 既定では、テーブルにはポリシーがありません。そのため、ユーザーがSQL特権システムに従ってテーブルへのアクセス特権を持っている場合、その中のすべての行をクエリまたは更新に等しく使用できます。

テーブルで行セキュリティが有効になっている場合 (ALTER tableを使用して... 行レベルのセキュリティを有効にする場合、行を選択または行を変更するためのテーブルへのすべての通常のアクセスは、行セキュリティポリシーによって許可されている必要があります。 (ただし、テーブルの所有者は通常、行セキュリティポリシーの対象にはなりません。) テーブルにポリシーが存在しない場合、default-denyポリシーが使用されます。つまり、行は表示されず、変更できません。 TRUNCATEREFERENCESなど、テーブル全体に適用される操作は、行セキュリティの対象にはなりません。

行セキュリティポリシーは、コマンド、ロール、またはその両方に固有のものにすることができます。 ポリシーは、ALLコマンド、SELECTINSERTUPDATE、またはDELETEに適用するように指定できます。 複数のロールを特定のポリシーに割り当てることができ、通常のロールのメンバーシップと継承のルールが適用されます。

ポリシーに従って表示または変更可能な行を指定するには、ブール値の結果を返す式が必要です。 この式は、ユーザーのクエリからの条件または関数の前に、行ごとに評価されます。 (このルールの唯一の例外は、情報を漏洩しないことが保証されている漏洩防止関数であり、オプティマイザは、行セキュリティチェックの前にそのような関数を適用することを選択することができる。) 式がtrueを返さない行は処理されません。 別個の式を指定して、可視の行と変更が可能な行とを独立して制御することができる。 ポリシー式は、クエリの一部として、クエリを実行するユーザーの特権で実行されますが、セキュリティ定義関数を使用して、呼び出し元のユーザーが使用できないデータにアクセスできます。

BYPASSRLS属性を持つスーパーユーザーとロールは、テーブルにアクセスするときに常に行セキュリティシステムをバイパスします。 テーブルの所有者は通常、行のセキュリティもバイパスしますが、テーブルの所有者はALTER Tableを使用して行のセキュリティの対象とすることができます... FORCE ROWレベルセキュリティ

行セキュリティの有効化と無効化、およびテーブルへのポリシーの追加は、常にテーブル所有者のみの特権です。

ポリシーは、CREATE POLICYコマンドを使用して作成され、ALTER POLICYコマンドを使用して変更され、DROP POLICYコマンドを使用して削除されます。 特定のテーブルの行セキュリティを有効または無効にするには、ALTER tableコマンドを使用します。

各ポリシーには名前があり、テーブルに対して複数のポリシーを定義できます。 ポリシーはテーブル固有であるため、テーブルの各ポリシーには一意の名前が必要です。 異なるテーブルが同じ名前のポリシーを持つ場合があります。

複数のポリシーが特定のクエリに適用される場合、それらはOR (デフォルトである許容ポリシーの場合) またはAND (制限ポリシーの場合) のいずれかを使用して結合されます。 これは、特定のロールがメンバーであるすべてのロールの特権を持つというルールに似ています。 許容ポリシーと制限ポリシーについては、以下でさらに説明します。

簡単な例として、マネージャーロールのメンバーのみが行にアクセスでき、アカウントの行のみにアクセスできるように、アカウントリレーションにポリシーを作成する方法を次に示します。

CREATE TABLEアカウント (マネージャテキスト、会社テキスト、contact_emailテキスト);

ALTER TABLEアカウントはROWレベルセキュリティを有効にします。CREATE POLICY account_managers ON accounts TO manager
        USING (manager = current_user); 

上記のポリシーは、USING句と同じWITH CHECK句を暗黙的に提供するため、コマンドによって選択された行の両方に制約が適用されます (したがって、マネージャーはSELECTUPDATE、またはDELETE別のマネージャに属する既存の行) およびコマンドによって変更された行 (異なるマネージャに属する行はINSERTまたはUPDATEを介して作成できません) 。

ロールが指定されていない場合、または特別なユーザー名PUBLICが使用されている場合、ポリシーはシステム上のすべてのユーザーに適用されます。 すべてのユーザーがusersテーブル内の自分の行にのみアクセスできるようにするには、簡単なポリシーを使用できます。

ユーザーに対する
POLICY user_policyの作成
        USING (user_name = current_user); 

これは前の例と同様に機能します。

テーブルに追加されている行に対して、表示されている行とは異なるポリシーを使用するには、複数のポリシーを組み合わせることができます。 このポリシーのペアでは、すべてのユーザーがusersテーブルのすべての行を表示できますが、独自の行を変更するだけです。

POLICYを作成ユーザーのuser_sel_policy
        選択のため
        USING (true);
ユーザーのPOLICY user_mod_policyの作成
        USING (user_name = current_user); 

SELECTコマンドでは、これら2つのポリシーはORを使用して結合され、すべての行を選択できるという効果があります。 他のコマンドタイプでは、2番目のポリシーのみが適用されるため、効果は以前と同じです。

行セキュリティは、ALTER TABLEコマンドで無効にすることもできます。 行セキュリティを無効にしても、テーブルで定義されているポリシーは削除されません。 テーブル内のすべての行が表示され、標準のSQL特権システムに従って変更可能になります。

以下は、本番環境でこの機能を使用する方法のより大きな例です。 テーブルpasswdはUnixパスワードファイルをエミュレートします。

-- 単純なpasswdファイルベースの例
テーブルの作成passwd (
      user_nameテキストUNIQUE NOT NULL、
      pwhashテキスト、
      uid int PRIMARYキー、
      gid int NOT NULL,
      real_nameテキストNULLではなく、
      home_phoneテキスト、
      extra_infoテキスト、
      home_dirテキストNOT NULL,
      シェルテキストNOT NULL
    );

CREATE ROLE admin; -- 管理者
ROLE bobを作成します。-通常のユーザー
ROLE aliceを作成します。-通常のユーザー

    -テーブルを作成する
passwdの価値に挿入して下さい
      ('admin' 、'xxx' 、0,0、'Admin' 、'111-222-3333 '、null、'/root' 、'/bin/dash');
passwdの価値に挿入して下さい
      ('bob' 、'xxx' 、1,1、'Bob' 、'123-456-7890 '、null、'/home/bob' 、'/bin/zsh');
passwdの価値に挿入して下さい
      ('alice' 、'xxx' 、2,1、'Alice' 、'098-765-4321 '、null、'/home/alice' 、'/bin/zsh');

    -テーブルで行レベルのセキュリティを有効にしてください
ALTER TABLE passwd ENABLE ROWレベルセキュリティ;

    -- ポリシーの作成
    -管理者はすべての行を表示し、行を追加できます
CREATE POLICY admin_all ON passwd TO admin USING (true) WITH CHECK (true);
    -通常のユーザーはすべての行を表示できます
CREATE POLICY all_view ON passwd FOR SELECT USING (true);
    -通常のユーザーは自分のレコードを更新できますが、
    -通常のユーザーが設定できるシェルを制限する
更新のためのpasswdでPOLICY user_modを作成する
      USING (current_user = user_name)
      チェック付き (
        current_user = user_name AND
        シェルIN ('/bin/bash' 、'/bin/sh' 、'/bin/dash' 、'/bin/zsh' 、'/bin/zsh' 、'/bin/tcsh')
      );

    -- 管理者のすべての通常の権限を許可する
    選択、挿入、更新、削除を許可します。
    -ユーザーは公開列でのみ選択アクセスを取得します
    選択を許可
      (user_name, uid, gid, real_name, home_phone, extra_info, home_dir, shell)
      ON passwd TO public;
    -ユーザーが特定の列を更新できるようにする
    許可の更新
      (pwhash、real_name、home_phone、extra_info、shell)
      ON passwd TO public; 

他のセキュリティ設定と同様に、システムが期待どおりに動作していることをテストして確認することが重要です。 上記の例を使用すると、これは許可システムが適切に機能していることを示します。

-- adminはすべての行とフィールドを表示できます
postgres=> set role admin;
セット
postgres=> テーブルpasswd;
 user_name | pwhash | uid | gid | real_name | home_phone | extra_info | home_dir | シェル
----------- -------- -----------------------------------------------------------------------------------
 admin | xxx | 0 | 0 | 管理者 | 111-222-3333 | | /ルート | /ビン /ダッシュ
 ボブ | xxx | 1 | 1 | ボブ | 123-456-7890 | | /home/bob | /bin/zsh
 alice | xxx | 2 | 1 | アリス | 098-765-4321 | | /home /Alice | /bin/zsh
(3行)

-アリスができることをテストする
postgres=> set role alice;
セット
postgres=> テーブルpasswd;
エラー: テーブルpasswdの許可が拒否されました
postgres=> passwdからuser_name、real_name、home_phone、extra_info、home_dir、shellを選択します。
 user_name | real_name | home_phone | extra_info | home_dir | シェル
--------------------------------------------------------------------------------------------
 admin | 管理者 | 111-222-3333 | | /ルート | /ビン /ダッシュ
 ボブ | ボブ | 123-456-7890 | | /ホーム /ボブ | /bin/zsh
 alice | アリス | 098-765-4321 | | /home /Alice | /bin/zsh
(3行)

postgres=> update passwd set user_name = 'joe';
エラー: テーブルpasswdの許可が拒否されました
-アリスは自分のreal_nameを変更できますが、他の人は変更できません
postgres=> update passwd set real_name = 'Alice Doe';
更新1
postgres=> update passwd set real_name='JohnDo' (user_name = 'admin);
更新0
postgres=> update passwd set shell = '/bin/xx';
エラー: 新しい行は「passwd」のWITH CHECKオプションに違反します
postgres=> passwdから削除します。エラー: テーブルpasswdの許可が拒否されました
postgres=> passwd (user_name) 値 ('xxx') に挿入します。エラー: テーブルpasswdの許可が拒否されました
-アリスは自分のパスワードを変更できます。RLSは他の行の更新を静かに防ぎます
postgres=> update passwd set pwhash = 'abc';
更新1 

これまでに構築されたすべてのポリシーは許容ポリシーであり、複数のポリシーが適用されると、「OR」ブール演算子を使用して組み合わされることを意味します。 許容ポリシーは、意図された場合に行へのアクセスのみを許可するように構築することができるが、許容ポリシーを制限ポリシー (レコードが通過しなければならず、「and」ブール演算子を使用して結合される) と結合する方がより簡単であり得る。 上記の例に基づいて、passwdテーブルのレコードにアクセスするために、管理者がローカルのUnixソケットを介して接続することを要求する制限ポリシーを追加します。

CREATE POLICY admin_local_only ON passwd管理者の制限として
        USING (pg_catalog.inet_client_addr() はNULL); 

次に、ネットワーク経由で接続している管理者は、制限されたポリシーのためにレコードを表示しないことがわかります。

=> SELECT current_user;
     current_user
    --------------
     admin
    (1行)

    => select inet_client_addr();
     inet_client_addr
    ------------------
     127.0.0.1
    (1行)

    => SELECT current_user;
     current_user
    --------------
     admin
    (1行)

    => テーブルpasswd;
     user_name | pwhash | uid | gid | real_name | home_phone | extra_info | home_dir | シェル
    -----------+--------+-----+-----+-----------+------------+------------+----------+-------
    (0 rows)

    => UPDATE passwdセットpwhash = NULL;
    更新0 

一意または主キー制約や外部キー参照などの参照整合性チェックは、常に行のセキュリティをバイパスして、データ整合性が維持されるようにします。 スキーマおよび行レベルポリシーを開発するときは、そのような参照整合性チェックによる情報の「秘密チャネル」漏洩を回避するように注意する必要があります。

コンテキストによっては、行セキュリティが適用されていないことを確認することが重要です。 たとえば、バックアップを取得するときに、行のセキュリティによって一部の行がバックアップから削除された場合、悲惨なことになる可能性があります。 このような状況では、row_security設定パラメーターをoffに設定できます。 これ自体は行のセキュリティをバイパスするものではありません。クエリの結果がポリシーによってフィルタリングされた場合にエラーがスローされます。 次に、エラーの理由を調査して修正することができます。

上記の例では、ポリシー式は、アクセスまたは更新される行の現在の値のみを考慮します。 これは最も単純で最もパフォーマンスの高いケースです。可能な場合は、このように動作するように行セキュリティアプリケーションを設計することをお勧めします。 他の行または他のテーブルを参照してポリシーを決定する必要がある場合は、ポリシー式でサブSELECTまたはSELECTを含む関数を使用して実行できます。 ただし、このようなアクセスは、注意を払わないと情報が漏洩する可能性のある競合状態を引き起こす可能性があることに注意してください。 例として、次のテーブルデザインを考えてみましょう。

-- 特権グループの定義
CREATE TABLEグループ (group_id int PRIMARY KEY,
                     group_nameテキストNULLではない);

グループ値に挿入する
  (1、'low ') 、
  (2、'medium') 、
  (5、「高」);

aliceにすべてのグループを承認します。-- aliceは管理者です
一般にグループを選択してください。-ユーザーの特権レベルの定義
CREATE TABLEユーザー (user_nameテキストPRIMARY KEY,
                    group_id int NOT NULL REFERENCESグループ);

ユーザーの値に挿入する
  ('alice' 、5) 、
  ('bob' 、2) 、
  ('mallory' 、2);

すべてのユーザーを認可してください。ユーザーの選択を承認して公開する。-保護する情報を保持しているテーブル
テーブル情報の作成 (infoテキスト、
                          group_id int NOT NULL REFERENCESグループ);

情報値に挿入する
  (「かろうじて秘密」、1) 、
  (「わずかに秘密」、2) 、
  ('非常に秘密' 、5);

ALTER TABLE情報有効な行レベルセキュリティ;

-セキュリティgroup_idが
-- 行のgroup_id以上
SELECTのポリシーfp_sオン情報を作成する
  USING (group_id <= (SELECT group_id FROM users WHERE user_name = current_user));
更新のための情報についてポリシーを作成するfp_u
  USING (group_id <= (SELECT group_id FROM users WHERE user_name = current_user));

-- 私達は情報テーブルを保護するためにRLSにだけ頼る
すべての情報を公開します。

アリスが「わずかに秘密の」情報を変更したいが、その行の新しいコンテンツでマロリーを信頼すべきではないと判断したとします。

BEGIN;
UPDATEユーザーSET group_id = 1 WHERE user_name = 'mallory';
UPDATE情報SET info = 'secret from mallory' WHERE group_id = 2;
    コミット; 

それは安全に見えます。マロリーが「マロリーからの秘密」文字列を見ることができるはずのウィンドウはありません。 ただし、ここにはレース条件があります。 マロリーが同時にやっている場合は、

SELECT * FROM情報WHERE group_id = 2 FOR UPDATE;

彼女の取引はREAD COMMITTEDモードであるため、彼女は「malloryからの秘密」を見ることができます。 それは、彼女のトランザクションがアリスの直後に情報行に到達した場合に発生します。 aliceのトランザクションがコミットするのをブロックし、for UPDATE句のおかげで更新された行の内容をフェッチします。 ただし、サブSELECTにはfor UPDATEがなかったため、暗黙的なSELECTの更新行をusersから取得しません。代わりに、users行はクエリの開始時に取得されたスナップショットで読み取られます。 したがって、ポリシー式は、malloryの特権レベルの古い値をテストし、更新された行を表示できるようにします。

この問題にはいくつかの方法があります。 1つの簡単な答えは、SELECTを使用することです... FOR SHARE in sub-SELECT in rowセキュリティポリシー。 ただし、影響を受けるユーザーに参照テーブル (ここではユーザー) のUPDATE権限を付与する必要があります。 (ただし、別の行セキュリティポリシーを適用して、実際にその特権を行使しないようにすることもできます。または、サブSELECTをセキュリティ定義関数に埋め込むこともできます。) また、参照されるテーブルに対する行共有ロックの大量の同時使用は、特にその更新が頻繁である場合、パフォーマンスの問題を引き起こす可能性があります。 参照されたテーブルの更新が頻繁でない場合に実用的な別の解決策は、参照されたテーブルを更新するときにACCESS EXCLUSIVEロックを使用して、古い行の値を調べることができないようにすることです。 または、参照されるテーブルの更新をコミットした後、新しいセキュリティ状況に依存する変更を行う前に、すべての同時トランザクションが終了するのを待つだけでもよい。