これは素晴らしいツールであり、長い間登場していたはずです。 これはPostgreSQL 9.4では行われませんでしたが、自動的に更新されるsecurity_barrierビューを使用して行われました。 これらとLEAKPROOF関数は、文字列セキュリティが構築される基盤を形成します。 宣言ポリシーをサポートせずにこれらのコンポーネントを使用して、9.4の行の安全性を実現できます。
以前にsecurity_barrierビューについて説明しました 。 この投稿には、ビューからの情報漏えいがどのように発生し、security_barrierビューがそのような漏えいを防ぐ方法の例が含まれています。 この記事の残りの部分で説明されている原則に精通していることを前提とし、提出などから情報がどのように漏洩するかのデモを再編成することはありません。
テーブルの行と行のセキュリティポリシーに関して同様の効果を得るには、自分のビューへのアクセスを許可する特権(スーパーユーザーではない)ロール以外のすべてに対するテーブルへのアクセスを拒否する必要があります。 その後、この特権ロールに属するsecurity_barrierビューを作成する必要があります。WHERE句は、選択した述語に基づいて文字列を表示する他のユーザーの能力を制限します。current_userを使用したり、current_settingを呼び出したりできます。
例:
CREATE ROLE secret_manager; CREATE ROLE bob; CREATE ROLE sid; CREATE TABLE user_secrets( secret_id integer primary key, owner text not null, secret text not null ); ALTER TABLE user_secrets OWNER TO secret_manager; INSERT INTO user_secrets (secret_id, owner, secret) VALUES (1, 'bob', 'pancakes'), (2, 'fred', 'waffles'), (3, 'anne', 'cake'), (4, 'sid', 'fraud'); REVOKE ALL ON user_secrets FROM public; CREATE VIEW filtered_user_secrets WITH (security_barrier) AS SELECT * FROM user_secrets WHERE owner = current_user WITH CHECK OPTION; ALTER VIEW filtered_user_secrets OWNER TO secret_manager; GRANT ALL ON filtered_user_secrets TO public; RESET ROLE;
それでは、その仕組みを見てみましょう。
test=# SET ROLE bob; SET test=> select * from filtered_user_secrets ; secret_id | owner | secret -----------+-------+---------- 1 | bob | pancakes (1 row) test=> SET ROLE sid; SET test=> select * from filtered_user_secrets ; secret_id | owner | secret -----------+-------+-------- 4 | sid | fraud (1 row) test=> SELECT * FROM filtered_user_secrets WHERE owner = 'bob'; secret_id | owner | secret -----------+-------+-------- (0 rows) test=> INSERT INTO filtered_user_secrets (secret_id, owner, secret) VALUES (5, 'sid', 'larceny'); INSERT 0 1 test=> select * from filtered_user_secrets ; secret_id | owner | secret -----------+-------+--------- 4 | sid | fraud 5 | sid | larceny (2 rows) test=> INSERT INTO filtered_user_secrets (secret_id, owner, secret) VALUES (6, 'joe', 'impersonation'); ERROR: new row violates WITH CHECK OPTION for view "filtered_user_secrets" DETAIL: Failing row contains (secret_id, owner, secret) = (6, joe, larceny).
動作は文字列セキュリティポリシーに非常に似ていますが、いくつかの注意事項があります。
- 基になるテーブルにALTERを使用しても、ビューに目に見える変更は加えられません。 これを行うには、ビューを削除して再作成します。
- アプリケーションに対して透過的ではありません。 基になるテーブルではなく、ビュー自体を使用する必要があります。
最後の段落は、スキーマとsearch_pathを使用してある程度解決できます。たとえば、次のとおりです。
CREATE SCHEMA filtered_tables; ALTER TABLE user_secrets SET SCHEMA filtered_tables; -- Leave the view in the public schema and just rename it ALTER TABLE filtered_user_secrets RENAME TO user_secrets;
顧客は、これが元のテーブルの単なるシェルであると心配することなく、ビューと対話できるようになりました。
current_userを使用する代わりに、current_setting( 'myapp.active_user')を使用できます。 それだけを行う場合、ベースレベルで空のデフォルト値を設定して、設定が定義されていない場合にcurrent_settingがエラーを出さないようにする必要があります(バージョン9.5では、current_setting( 'myapp.active_user'、 't')を使用して、欠落しているエントリを無視できます)。 例:
ALTER DATABASE mydatabase SET myapp_active_user = '';
重要: current_settingをプレゼンテーションの述語として使用する場合、セキュリティポリシーはユーザー定義の構成設定に適用されないため、任意のSQLクエリを実行できるユーザーは設定を変更できることに注意してください。 すべての要求が実行された要求を完全に制御してアプリケーションを通過する場合、これは依然として有用なツールのままですが、データベースに直接アクセスするユーザーのアクションを制限するには適していません。 すべてのユーザーが単純にRESET ROLEを実行できるため、アプリケーションが統合接続を使用する場合、SET ROLEを使用してアクティブユーザーを切り替える場合も同様です。
バージョン9.5のラインレベルセキュリティの利便性を使用する方がはるかに簡単ですが、今すぐに同様の機能が必要な場合は、すでに実現できています。
PostgreSQLの古いバージョンでも、基になるテーブルのフィルタービュークエリを返すSECURITY DEFINER plpgsql関数を使用できます。 ユーザーに表示されるすべての行を最初に選択してからフィルターする必要があるため、ほとんどのインデックスを使用できないため、パフォーマンスはひどくなります。 さらに、ユーザーに独自の機能を定義する機能を提供しない場合は、通常のビューを使用できるため、リークが発生する可能性は低くなります。
文字列セキュリティとプレゼンテーションベースのアプローチは、一度に設定でき、リセットできない、または特定のロールによってのみ設定できる安全な変数を定義する機能から大きな恩恵を受けます(おそらく、チェックするSECURITY DEFINER機能を使用して正気で)。 このような機能は9.5には現れませんが、いくつかの拡張機能の助けを借りて可能になる可能性があります。これについては後で学びたいと思っています。