PostgreSQL security_barrierビューの仕組み

PostgreSQL 9.2でsecurity_barrierビューのサポートが追加されたことにお気づきかもしれません。 AXLEプロジェクトの進化するラインレベルのセキュリティ作業の一環として、自動更新サポートを追加する目的でこのコードを調査し、それらがどのように機能するかを説明しようと思いました。



ロバートは、そのような表現の利点とそれが保護するものをすでに説明しています(さらに、これは「 PostgreSQL 9.2の新機能 」で説明されていました)。 次に、それらの動作に移り、security_barrierビューと自動更新されたビューとの相互作用について説明します。



通常のビュー



単純な通常のビューは、サブクエリとしてマクロ形式で展開されます。通常、サブクエリは、述語を出力し、含まれるクエリの条件に追加することで最適化されます。 これは例を使用してより明確にすることができます。 テーブルは次のとおりです。



CREATE TABLE t AS SELECT n, 'secret'||n AS secret FROM generate_series(1,20) n;
      
      





およびプレゼンテーション:



 CREATE VIEW t_odd AS SELECT n, secret FROM t WHERE n % 2 = 1;
      
      





タイプリクエスト:



 SELECT * FROM t_odd WHERE n < 4
      
      





リクエストハンドラ内で次の形式に変換されます。



 SELECT * FROM (SELECT * FROM t WHERE n % 2 = 1) t_odd WHERE n < 4
      
      





オプティマイザーは、外部クエリのサブクエリとWHERE条件を取り出して、一度に実行されるクエリに変換します。



 SELECT * FROM t t_odd WHERE (n % 2 = 1) AND (n < 4)
      
      





瞬間的なクエリを直接見ることはできず、実際のSQLには存在しませんが、postgresql.confにdebug_print_parse = ondebug_print_rewritten = onおよびdebug_print_plan = onを含めることで、このプロセスを見ることができます。 上記の例に基づいて生成するのはかなり大きくて簡単なので、ここでは解析ツリーと計画ツリーを再現しません。



セキュリティビューの使用に関する問題



テーブル自体へのアクセスを許可せずにビューへのアクセスを許可しても、行を見ることができないと考えるかもしれません。 実際、それは真実のように見えます:



 regress=> SELECT * FROM t_odd WHERE n < 4; n | secret ---+--------- 1 | secret1 3 | secret3 (2 rows)
      
      





しかし、計画を見ると、潜在的な問題を確認できます。



 regress=> EXPLAIN SELECT * FROM t_odd WHERE n < 4; QUERY PLAN --------------------------------------------------- Seq Scan on t (cost=0.00..31.53 rows=2 width=36) Filter: ((n < 4) AND ((n % 2) = 1)) (2 rows)
      
      





ビューのサブクエリが最適化され、その識別子が外部リクエストに作成されました。



SQLでは、ANDとORは順序付けられていません。 オプティマイザー/エグゼキューターは、実行するブランチを選択する完全な自由があり、応答を発行するという点でより高速であると見なされ、おそらく他のブランチの開始を回避できます。 つまり スケジューラーがn <4が n%2よりもはるかに速いと考える場合、スケジューラーが最初に実行します。 無害に見えますよね? 試してください:



 regress=> CREATE OR REPLACE FUNCTION f_leak(text) RETURNS boolean AS $$ BEGIN RAISE NOTICE 'Secret is: %',$1; RETURN true; END; $$ COST 1 LANGUAGE plpgsql; regress=> SELECT * FROM t_odd WHERE f_leak(secret) AND n < 4; NOTICE: Secret is: secret1 NOTICE: Secret is: secret2 NOTICE: Secret is: secret3 NOTICE: Secret is: secret4 NOTICE: Secret is: secret5 NOTICE: Secret is: secret6 NOTICE: Secret is: secret7 NOTICE: Secret is: secret8 NOTICE: Secret is: secret9 NOTICE: Secret is: secret10 NOTICE: Secret is: secret11 NOTICE: Secret is: secret12 NOTICE: Secret is: secret13 NOTICE: Secret is: secret14 NOTICE: Secret is: secret15 NOTICE: Secret is: secret16 NOTICE: Secret is: secret17 NOTICE: Secret is: secret18 NOTICE: Secret is: secret19 NOTICE: Secret is: secret20 n | secret ---+--------- 1 | secret1 3 | secret3 (2 rows) regress=> EXPLAIN SELECT * FROM t_odd WHERE f_leak(secret) AND n < 4; QUERY PLAN ---------------------------------------------------------- Seq Scan on t (cost=0.00..34.60 rows=1 width=36) Filter: (f_leak(secret) AND (n < 4) AND ((n % 2) = 1)) (2 rows)
      
      





おっと! ご覧のとおり、ユーザー指定の述語を持つ関数は、他のテストよりも実行コストが低いと見なされたため、プレゼンテーション述語が不適切なものを除外する前にすべての行をスキップしました。 悪意のある機能は、同じトリックを使用して文字列をコピーする可能性があります。



Security_barrierビュー



security_barrierビューは、ユーザーが作成した条件が適用される前に最初にビュー条件が実行されるようにすることでこれを修正します。 プレゼンテーションを展開し、外部リクエストでプレゼンテーションの条件を作成する代わりに、プレゼンテーションへのリンクをサブクエリに置き換えます。 このサブクエリのsecurity_barrierフラグは、テーブル内のエントリの範囲全体に設定されており、通常の場合のように、サブクエリに触れたり、条件を作成したりしないことをオプティマイザに伝えます。



したがって、保護バリアを使用したパフォーマンス:



 CREATE VIEW t_odd_sb WITH (security_barrier) AS SELECT n, secret FROM t WHERE n % 2 = 1;
      
      





取得します:



 regress=> SELECT * FROM t_odd_sb WHERE f_leak(secret) AND n < 4; NOTICE: Secret is: secret1 NOTICE: Secret is: secret3 n | secret ---+--------- 1 | secret1 3 | secret3 (2 rows) regress=> EXPLAIN SELECT * FROM t_odd_sb WHERE f_leak(secret) AND n < 4; QUERY PLAN --------------------------------------------------------------- Subquery Scan on t_odd_sb (cost=0.00..31.55 rows=1 width=36) Filter: f_leak(t_odd_sb.secret) -> Seq Scan on t (cost=0.00..31.53 rows=2 width=36) Filter: ((n < 4) AND ((n % 2) = 1)) (4 rows)
      
      





クエリ実行プランは、説明の出力に保護バリアの属性を表示しませんが、何が起こっているかを知らせる必要があります。 ネストされたサブクエリは、ビューのサブクエリの条件でtを強制的にスキャンします。その後、ユーザーが作成した関数の条件が受信データで満たされます。



しかし。 ちょっと待って ユーザー定義の述語n <4もサブクエリに表示されるのはなぜですか? それは潜在的なセキュリティホールではありませんか? n <4が省略されている場合、なぜf_leak(secret)ではありませんか?



LEAKPROOF演算子と関数



これの説明は、演算子<が LEAKPROOFとしてマークされていることです 。 この属性は、指定された演算子または関数が情報の信頼を許可しないことを、 security_barrierビューに安全に適用できることを示します。 明らかな理由により、 LEAKPROOF属性を通常のユーザーとして設定することはできません。



 regress=> ALTER FUNCTION f_leak(text) LEAKPROOF; ERROR: only superuser can define a leakproof function
      
      





スーパーユーザーは何でもできますし、表現の保護障壁を通過するために情報漏えいの機能を使ったトリックに頼る必要はありません。



security_barrierビューを更新できないのはなぜですか



PostgreSQL 9.3の通常のビューは自動的に更新されますが、security_barrierビューは「シンプル」を意味しません。 これは、ビューの更新がビューのサブクエリを削除する機能に依存しており、更新を通常のテーブル更新に変換するためです。 security_barrier提出の全体的なポイントは、この例外が提出を条件付けることを許可しないことです。 現在、UPDATEはサブクエリを直接処理できないため、PostgreSQLはsecurity_barrierビューの更新を拒否します。



 regress = > UPDATE t_odd SET secret = 'secret_haha' || n; UPDATE 10 regress = > UPDATE t_odd_sb SET secret = 'secret_haha' || n; ERROR: cannot UPDATE VIEW "t_odd_sb" DETAIL: SECURITY - barrier views ARE NOT automatically updatable. HINT: TO ENABLE updating the VIEW, provide an INSTEAD OF UPDATE TRIGGER OR an unconditional ON UPDATE DO INSTEAD RULE.
      
      





これはまさに、AXLEプロジェクトのラインレベルセキュリティを開発する作業の一環として、キャンセルすることに関心がある制限です。 KaiGai Koheiは、行レベルのセキュリティで多大な作業を行っており、security_barrierやLEAKPROOFのようなものは、PostgreSQLに行レベルのセキュリティを追加するという仕事から多くのことを得ています。 次の課題は、保護バリアを安全に更新し、将来的にサービスが提供されるように更新する方法です。



なぜサブクエリなのか?



このためにサブクエリを使用する理由を疑問に思うかもしれません。 私はそれについて考えました。 短いバージョンはすべきではありませんが、サブクエリを使用しない場合、代わりにAND演算子とOR演算子の新しいソート依存のバリエーションを作成し、オプティマイザーに条件を渡すことができないことを教える必要があります。 ビューはすでにサブクエリによって拡張されているため、サブクエリをデータの取得/追加を許可しないフェンスとしてマークする方がはるかに簡単です。



PostgreSQLにはすでに、単純化された順序付けされた操作CASEがあります。 CASEを使用する際の問題は、 LEAKPROOFであってもCASEの境界を越える操作ができないことです 。 オプティマイザーと同様に、インデックスの使用について決定を下すことはできませんか? CASEブロック内の式に基づきます。 したがって、ここで質問したようにCASEを使用した場合、ユーザーが指定した条件を満たすためにインデックスを使用することはできませんでした。



コード内



Security_barrierサポートは0e4611c0234d89e288a53351f775c59522baed7cで追加され、 cd30728fb2ed7c367d545fc14ab850b5fa2a4850の LEAKPROOFサポートによって強化されています。 ノートをコミットしてくれてありがとう。 参加してくれたみんなに感謝します。



PS。 この記事は比較的古いものですが、次の記事の翻訳の紹介として重要です。



All Articles