FIO16-C. ジェイル(監獄)を作成してファイルへのアクセスを制限する
ジェイルを作成することで、プログラムをファイルシステムの他の部分から切り離すことができる。プログラムの通常の操作ではアクセスする必要のない要素をアクセス不可とするために、サンドボックスを作成するという考え方である。これは、システムが無制限に侵害されるおそれのあるあらゆる脆弱性の悪用を困難にするため、 defense-in-depth(多重防御)の役割を果たす。ジェイルは、システム上に存在するリソースよりも少ないリソースで実行可能な、すべてのユーザが閲覧できるプログラム群で構成される。プログラムに障害が発生したときに権限を昇格する仕組みがほかにない場合に限り、ジェイルは有効である。
さらに、必要なすべてのリソース(ライブラリ、ファイルなど)が jail ディレクトリ内に複製され、このディレクトリ内からファイルシステムの他の部分への参照が行われないようにする必要がある。すべてのユーザが閲覧できるプログラム群で構成され、また、jail ディレクトリとリソースに対しては、プログラムの権限要件に基づき、制限付きの読み書き権限を設定することが望ましい。ジェイルの作成は正しく行うことで効果的なセキュリティ対策になるが、他のルールやレコメンデーションへの適合を保証するものではない。
違反コード
以下のコード例はファイルパスを適切に正規化していないため、セキュリティ上の欠陥が存在する。これによって攻撃者はファイルシステムを渡り歩き、場合によっては脆弱なプログラムの権限を使用して任意のファイルへの書き込みを行うことができる。たとえば、パスワードファイルの上書き(POSIX ベースのシステムでは一般的な /etc/passwd など)やマウスなどのデバイスファイルの上書きによってさらなる攻撃を引き起こしたりサービス運用妨害を引き起こしたりすることが可能になる。
enum { array_max = 100 }; /* * ユーザから argv[1] と argv[2] が渡され、 * 昇格した権限で実行されるプログラム */ char x[array_max]; FILE *fp = fopen(argv[1], "w"); strncpy(x, argv[2], array_max); x[array_max - 1] = '\0'; /* * /etc/passwd のような意図しないファイルへの書き込み操作が * 実行される */ if (fwrite(x, sizeof(x[0]), sizeof(x)/sizeof(x[0]), fp) < sizeof(x)/sizeof(x[0])) { /* エラー処理 */ }
攻撃者は argv[1] の値を制御し、結果的にファイルシステム上の任意のリソースにアクセスできる。
このコード例は、「FIO02-C. 信頼できない情報源から取得したファイル名は正規化する」と「FIO03-C. fopen() を使用したファイル作成時に何らかの仮定をしない」にも違反している。
適合コード (UNIX)
UNIX ベースシステムの中には(OpenBSD など)、chroot() ジェイルを作成してファイルシステムへのアクセスを制限できるものもある。chroot ジェイルは安全に実装する必要がある [Wheeler 03]。そのためには、chroot() の引数として、あらかじめ定義されたディレクトリ名を渡す。chroot() を呼び出すにはスーパーユーザの権限が必要になる。しかし、期待に反してこの呼び出しはジェイル内部にプロセスを移動しない。ジェイル内部にアクセスを制限するには、続いて chdir() を実行する必要がある。
もう 1 つ必要なのは、これらを呼び出した後、スーパーユーザ権限を永久に破棄する手順である(「POS02-C. 最小権限の原則に従う」を参照)。chroot() システムコールだけでは、ルートディレクトリを変更しているスーパーユーザのファイルシステムへのアクセスを制限できない(権限が下げられていない場合)。jail の作成に成功すると、攻撃者がコマンドライン引数などによって、悪意のある入力を行ったとしても、ファイルシステムへの意図しないアクセスを防止できる。
/* * chroot/jail ディレクトリが * 現在の作業ディレクトリ内にあることを確認すること。また、適切な * 権限をディレクトリに割り当てて、アクセスを制限する。外部リソース * へのファイルシステム記述子を閉じて、 * jail から出ないようにすること。 */ if (setuid(0) == -1) { /* エラー処理 */ } if (chroot("chroot/jail") == -1) { /* エラー処理 */ } if (chdir("/") == -1) { /* エラー処理 */ } /* root 権限を永久に破棄する */ if (setgid(getgid()) == -1) { /* エラー処理 */ } if (setuid(getuid()) == -1) { /* エラー処理 */ } /* 制限された権限での操作の実行 */ enum {array_max = 100}; FILE *fp = fopen(argv[1], "w"); char x[array_max]; strncpy(x, argv[2], array_max); x[array_max - 1] = '\0'; /* ジェイル内部では書き込み操作の安全性を確保 */ if (fwrite(x, sizeof(x[0]), sizeof(x)/sizeof(x[0]), fp) < sizeof(x)/sizeof(x[0])) { /* エラー処理 */ }
別の方法として、最初に chdir("chroot/jail") を呼び出し、次に chroot(".") を呼び出すこともできる。ただし、chdir("/some/path") を呼び出したあとで chroot("/some/path") を呼び出すことは、競合状態を引き起こしやすいので避けるべきである。この実行順序では、十分な権限を持つ攻撃者は 2 つのシステムコールで別々のディレクトリを参照するように /some/path を操作できる。結果として、プログラムは現在の作業ディレクトリを新しいルートディレクトリに設定できなくなる。chdir("/") を chroot() のあとに使用するか、chroot(".") を chdir() のあとに使用することで、現在の作業ディレクトリが新しいルートと同じディレクトリであることが保証される。
リスク評価
このレコメンデーションに違反すると、ファイルシステムの脆弱性が発見されて攻撃された場合にシステム全体の侵害につながることがある。
レコメンデーション | 深刻度 | 可能性 | 修正コスト | 優先度 | レベル |
---|---|---|---|---|---|
FIO16-C | 中 | 中 | 高 | P4 | L3 |
参考情報
- [Wheeler 03] Section 7.4, "Minimize Privileges"
翻訳元
これは以下のページを翻訳したものです。