PostgreSQLのルールシステムによる問い合わせの書き換えによって、オリジナルの問い合わせで使われたものではない他のテーブル/ビューがアクセスされます。 更新ルールを使うことによってテーブルへの書き込みアクセスを包含することができます。
書き換えルールに別々の所有者はいません。 リレーション(テーブルまたはビュー)の所有者は自動的にそれに定義された書き換えルールの所有者となります。 PostgreSQLのルールシステムはデフォルトのアクセス制御システムの振舞いを変更します。 ルールによって使用されるリレーションは、ルールを起動したユーザの権限ではなく、ルール所有者の権限でチェックされます。 このことは、ユーザは問い合わせで明記するテーブル/ビューに対しての権限だけあればよいことを示しています。
例えば、以下のようにします。 あるユーザが、いくつかは個人用の、その他は事務所で秘書が利用するための、電話番号のリストを持っていたとします。 ユーザは次のようにして構築することができます。
CREATE TABLE phone_data (person text, phone text, private boolean); CREATE VIEW phone_number AS SELECT person, CASE WHEN NOT private THEN phone END AS phone FROM phone_data; GRANT SELECT ON phone_number TO assistant;
そのユーザ(とデータベースのスーパーユーザ)以外はphone_data
テーブルにアクセスできません。
しかし、GRANT
により秘書はphone_number
ビューに対しSELECT
できます。
ルールシステムはphone_number
からのSELECT
操作をphone_data
からのSELECT
操作に書き換えます。
そのユーザはphone_number
の所有者、したがってルールの所有者ですから、phone_data
の読み込みに対するアクセスはそのユーザの権限に従ってチェックされ、問い合わせを受け付けてもよいことになります。
phone_number
へのアクセスもチェックされますが、これは呼び出したユーザに対して行われますので、秘書とユーザ以外は使うことができません。
権限はルールごとにチェックされます。
ですから秘書だけが今のところ公開の電話番号を参照することができます。
しかし、秘書は別のビューを作成し、それにPUBLICに対するアクセス許可を与えることができます。
こうすると秘書のビューを通して誰もがphone_number
データを見ることができます。
秘書ができないことはphone_data
に直接アクセスするビューを作ることです
(実際には作成はできますが、アクセスは全て、権限チェックで拒絶されます)。
そして、秘書が独自のphone_number
ビューを開いたことにユーザが気付いた時点で、秘書の権限を取り上げることができます。
秘書のビューへのアクセスは即座に失敗に終わります。
このルールごとのチェックがセキュリティホールになると考える人がいるかもしれませんが、実際にはそうではありません。
もしこのように機能しないとなると、秘書はphone_number
と同じ列を持ったテーブルを用意して、1日1回データをそこにコピーするかもしれません。
そうなると、データは彼のものですから、誰にアクセス権を与えようが彼の自由です。
GRANT
は「あなたを信用しています」ということです。
信用している誰かがこのようなことを行った場合は、考えを変えてREVOKE
してください。
上に示したような手法を使ってある列の内容を隠すのにビューは使えますが、security_barrier
フラグが設定されていない限り、見えない行にあるデータを隠す信頼できる方法ではない事に注意してください。
例えば、以下のビューは安全ではありません。
CREATE VIEW phone_number AS SELECT person, phone FROM phone_data WHERE phone NOT LIKE '412%';
ルールシステムがphone_number
からのSELECT
をphone_data
からのSELECT
に書き換え、phone
が412で始まらない項目のみが必要だという条件を追加しますので、このビューは安全に見えます。
しかし、ユーザが自身の関数を作成できるのであれば、NOT LIKE
式の前にユーザ定義の関数を実行するようプランナを説得することは難しくありません。
例えば以下の通りです。
CREATE FUNCTION tricky(text, text) RETURNS bool AS $$ BEGIN RAISE NOTICE '% => %', $1, $2; RETURN true; END; $$ LANGUAGE plpgsql COST 0.0000000000000000000001; SELECT * FROM phone_number WHERE tricky(person, phone);
プランナはより高価なNOT LIKE
の前に安価なtricky
関数を実行することを選びますので、phone_data
テーブルの人と電話番号はすべてNOTICE
として表示されます。
たとえユーザが新しい関数を定義できない場合でも、同様の攻撃で組み込み関数が使えます。
(例えば、ほとんどの型変換関数は生成するエラーメッセージに入力値を含んでいます。)
同様の考慮は更新ルールにも適用できます。
前節の例において、データベースのテーブルの所有者はshoelace
ビューに対し、誰かにSELECT
、INSERT
、UPDATE
、DELETE
権限を与えることができます。
しかし、shoelace_log
に対してはSELECT
だけです。
ログ項目を書き込むルールアクションは支障なく実行され、また、他のユーザはログ項目を見ることができます。
しかし、他のユーザは項目を捏造したり、既に存在する項目を操作する、あるいは削除することはできません。
この場合、shoelace_log
を参照しているルールは条件のないINSERT
だけですので、操作の順序を変えるようにプランナを説得することでルールを破壊する可能性はありません。
これはより複雑な状況では正しくないかもしれません。
ビューが行単位セキュリティを提供する場合には、そのビューにはsecurity_barrier
属性を付与するべきです。
これは、悪意を持って選ばれた関数や演算子が、ビューが適用されるより前に渡された行に対して実行されないようにします。
例えば、上記のビューが次のように定義された場合、これは安全です。
CREATE VIEW phone_number WITH (security_barrier) AS SELECT person, phone FROM phone_data WHERE phone NOT LIKE '412%';
security_barrier
を付けて定義されたビューは、このオプションなしのビューよりも性能の劣る問い合わせ実行プランを生成します。一般的に、最も高速だが、セキュリティ上の問題がある問い合わせ実行プランを破棄しなければならないという状況は不可避です。そのため、このオプションはデフォルトでは有効になっていません。
プランナは副作用が無い関数をもう少し柔軟に扱います。
これらの関数はLEAKPROOF
属性を持っており、等価演算子など、単純で広く用いられる演算子も多く含まれます。
利用者に対して不可視な行に対するこれら関数の呼び出しはいかなる情報も漏えいさせないため、プランナはこれらの関数をどのような場所でも安全に実行させる事ができます。
さらに、引数をとらなかったり、セキュリティバリアビューから引数を渡されない関数は、ビューからデータを渡されることは決して無いため、プッシュダウンされるためのLEAKPROOF
をマークする必要はありません。
一方、受け取った引数の値に応じてエラー(例えばオーバーフローやゼロ除算など)を発生させるかもしれない関数は leak-proof ではありません。
これがもしセキュリティ・ビューの条件句でフィルタリングされるより前に適用されたなら、不可視行に関する何か重要な情報を与える事ができてしまいます。
ビューがsecurity_barrier
属性付きで定義されたとしても、それは限定的な意味で安全である、つまり不可視な行の内容が潜在的に安全でない関数に渡される事がないという事を意図しているにすぎません。
利用者には不可視な行に対して何らかの推測を行う他の手段があるかもしれません。例えば、EXPLAIN
を用いて問い合わせ実行プランを見たり、ビューに対する問い合わせ実行時間を計測したりすることです。
悪意の攻撃者は不可視データの量を推測したり、分散や最頻値(なぜなら、これらは統計情報を通じて実行プランの選択、ひいては実行時間に影響するかもしれません)に関する何らかの情報を得る事ができるかもしれません。
もし、この種の"隠れチャネル"に対する対策が必要であれば、とにかくデータに対するアクセスを許可するのは得策ではありません。