第3回 アクセス制御の実装は“巧妙”かつ“大胆”に


海外 浩平
日本SELinuxユーザ会
2007/10/12


 行レベルのアクセス制御

 さて、SE-PostgreSQLの主要機能の1つに行レベルのアクセス制御があったことを思い出してください。

 先ほどの例では行レベルアクセス制御に言及していませんでしたが、これには理由があります。それは、SQLクエリーの検査がクエリーの実行前、即ち「エグゼキュータ」より前の処理フェイズで行われているのに対して、どの行がアクセスされるのかはクエリーの実行段階にならないと確定しないという問題があるからです。

 この問題を回避するため、SE-PostgreSQLは行レベルのアクセス制御を、SQLクエリーの検査ではなく、書き換えによって実現しています。下記の例を見てください。

【書き換え前】
SELECT name, price * 1.05 FROM drink WHERE id in (3,4);

【書き換え後】
SELECT name, price * 1.05 FROM drink WHERE id in (3,4)
   AND sepgsql_tuple_perms(drink.security_context, ...);

 WHERE句に条件が追加されており、これによって、検索対象の行が絞り込まれることになります。ここでのポイントは、SE-PostgreSQLがSQLクエリーの検査と併せてSQLクエリーの書き換えも行っているものの、条件句に追加された関数は「エグゼキュータ」の実行中に呼び出されるということです。従って、SQLクエリーの検査時にはどの行(タプル)にアクセスするのか確定できないという問題は解決します。

 sepgsql_tuple_perms関数は、各行のセキュリティコンテキストを引数に取り、クライアントがその行にアクセス可能かどうかを判定する関数です。この関数によってアクセス不可能な行がフィルタリングされた結果、クライアントにはあたかもアクセス不可能な行が存在しないかのように見えます。

 SQLクエリーが書き換えられている様子は、クエリーの実行プランを確認する EXPLAIN 文を使って確認することができます。

kaigai=# EXPLAIN SELECT * FROM drink WHERE id = 3;
                                           QUERY PLAN
---------------------------------------------------------------
  Seq Scan on drink (cost=0.00..1.09 rows=1 width=17)
    Filter: (pg_catalog.sepgsql_tuple_perms(tableoid, security_context, 12, drink.*) AND (id = 3))
(2 rows)

 このように、本来は「id = 3」という条件だけのはずなのに、実行プランにはsepgsql_tuple_perms関数の呼び出しが含まれています。

 クライアントにはあたかもアクセス不可能な行が存在しないかのように見えるという点で、SE-PostgreSQLは徹底しています。

 複数のテーブルを結合(JOIN)する際にも、まず両側のテーブルに sepgsql_tuple_perms関数でフィルタリングを行い、その結果を結合するのと等価であるように条件句の書き換えを行います。

【書き換え前】
SELECT * FROM t1 JOIN t2 ON t1.a = t2.x;

【書き換え後】
SELECT * FROM t1 JOIN t2 ON t1.a = t2.x
   AND sepgsql_tuple_perms(t1.security_context, ...)
   AND sepgsql_tuple_perms(t2.security_context, ...);


 特殊ケース(OUTER JOIN)への対応

 行レベルアクセス制御の特殊なケースとして、外部結合(OUTER JOIN)の利用があります。

 以下はPostgreSQL 8.2.5文書[和訳]からの引用ですが、左外部結合(LEFT OUTER JOIN)について以下のように述べています。

T1 LEFT OUTER JOIN T2 ON <結合条件>

まず、内部結合が行われます。その後、T2のどの行との結合条件も満たさないT1の各行については、T2の列をNULL値として結合した行が追加されます。したがって、連結されたテーブルは無条件にT1の行それぞれに少なくとも1つの行があります。


【注3】
右外部結合(RIGHT OUTER JOIN)、完全外部結合(FULL OUTER JOIN)の場合は適宜T1/T2を読み替えてください。

 いうまでもなく、「無条件にT1の行が少なくとも1個」外部結合の結果に含まれてしまうと、行レベルのアクセス制御は破たんします。そこで、SE-PostgreSQLは次のような大胆なSQLクエリーの書き換えを行います。

【書き換え前】
SELECT * FROM t1 LEFT OUTER JOIN t2 ON t1.a = t2.x;

【書き換え後】
SELECT * FROM (SELECT * FROM t1 WHERE sepgsql_tuple_perms(t1.security_context, ...)) AS t1
   LEFT OUTER JOIN t2
   ON t1.a = t2.x
   AND sepgsql_tuple_perms(t2.security_context, ...);

 書き換え前にはt1テーブルとt2テーブルを左外部結合していたものが、書き換え後は「t1テーブルに対するフィルタリング条件付きサブクエリーの結果」とt2テーブルの左外部結合となっています。

 外部結合の対象となるテーブルをサブクエリーへと書き換えることによって、クライアントが権限を持たない行があたかも存在しないかのように扱うことができるのです。

 しかし、これには副作用もあります。上の例で、t1テーブルへの参照がサブクエリーに書き換えられたことで、t1テーブルへのアクセスは常にシーケンシャルスキャンを伴うようになってしまい、パフォーマンスの面ではマイナスです。

 SE-PostgreSQLを利用することでセキュリティ上のメリットを得ることができますが、一方で、このようなデメリットも存在することには留意しておくべきでしょう。


 今回の記事では、SE-PostgreSQLアクセス制御の内側を紹介してみました。少しいじってみる程度であれば、その実装にまで気を配る必要はないかもしれません。しかし、SE-PostgreSQLがどのようにSQLクエリーの検査を行い、どのようにSQLクエリーを書き換えているのかを知っておくことで、適切なデータベース設計やSQLクエリの記述が可能になるでしょう。

 次回はいよいよ連載の最終回、SE-PostgreSQLの運用についてご紹介したいと思います。

参考情報:Fedora 8 以降での SE-PostgreSQL

 連載第1回で、Fedora 7へのSE-PostgreSQLのインストール方法を紹介しましたが、Fedora 8以降ではインストール手順が非常に簡単になる予定です。

 というのも、Fedora 8以降では、SELinuxの標準セキュリティポリシーにSE-PostgreSQL対応のルールが含まれるようになり、SE-PostgreSQL自体もFedoraパッケージの1つとして取り込まれました。

 ですので、これまでのようにパッケージを個別にダウンロードしてインストールする必要はなくなり、yumコマンドを実行するだけで、すべての依存関係を解決したうえで SE-PostgreSQL をインストールできるようになりました。

(Fedora 8 以降での SE-PostgreSQL インストールのイメージ)

[root@masu ~]# yum install sepostgresql
Setting up Install Process
Parsing package install arguments
Resolving Dependencies
--> Running transaction check
---> Package sepostgresql.i386 0:8.2.5-1.23.fc8 set to be updated

Dependencies Resolved

========================================================
  Package           Arch      Version            Repository        Size
========================================================
Installing:
  sepostgresql     i386       8.2.5-1.23.fc8    development    3.2 M

Transaction Summary
========================================================
Install 1 Package(s)
Update 0 Package(s)
Remove 0 Package(s)

Total download size: 3.2 M
Is this ok [y/N]: y
Downloading Packages:
(1/1): sepostgresql-8.2.5 100% |=========================| 3.2 MB 00:12
Running rpm_check_debug
--> Populating transaction set with selected packages. Please wait.
---> Package sepostgresql.i386 0:8.2.5-1.23.fc8 set to be updated
Running Transaction Test
warning: sepostgresql-8.2.5-1.23.fc8: Header V3 DSA signature: NOKEY, key ID 30c9ecf8
Finished Transaction Test
Transaction Test Succeeded
Running Transaction
Installing: sepostgresql ######################### [1/1]

Installed: sepostgresql.i386 0:8.2.5-1.23.fc8
Complete!
[root@masu ~]#


3/3
 

Index
アクセス制御の実装は“巧妙”かつ“大胆”に
  Page1
PostgreSQLのクエリー処理手順を理解する
SE-PostgreSQLによる拡張
  Page2
SE-PostgreSQLによるSQLクエリーの検査

例1:シンプルなSQLに必要な権限を検査する
例2:UPDATE文の実行に必要な権限を検査する
Page3
行レベルのアクセス制御
特殊ケース(OUTER JOIN)への対応
参考情報:Fedora 8 以降での SE-PostgreSQL


Profile
海外 浩平(かいがい こうへい)

日本SELinuxユーザ会

某社でLinuxカーネルの開発・サポートを行うかたわら、SELinux並列実行性能の改善や、組み込み向けファイルシステムのSELinux対応などを行っている。

現在は本業に加えて、IPA未踏ソフトウェア創造事業の支援を受けて、SE-PostgreSQL開発という二足のわらじを履いている日々。

「アンチ事なかれ主義」がモットー、好きな言葉は「前代未聞」。

SE-PostgreSQLによるセキュアDB構築 連載インデックス


Security&Trust フォーラム 新着記事
@ITメールマガジン 新着情報やスタッフのコラムがメールで届きます(無料)

注目のテーマ

Security & Trust 記事ランキング

本日 月間