次のページ 前のページ 目次へ

6. プログラムのインタフェースと内部構成をきちんとすること

6.1 インタフェースを安全に

インタフェースは、できる限り小さく(限りなくシンプルに)、厳密に(必要な機能 だけ)、そして例外なくそのインタフェースを使うようにする必要があります。 信用できる入力はほとんどないと思ってください。 アプリケーションやデータを見るためのビューアーは、外部で作成されたファイル を表示することが多いと思いますが、それらのファイルをプログラム(自動実行マクロ も含みます)として扱うことを避けてください。安全なサンドボックスを苦労して 作成することをいとわないのであれば、話は別ですが。

6.2 パーミッションを最小限に

すでに触れましたが、この点については大原則が存在しています。それはプログラム には、処理のために必要な最低限のパーミッションしか持たせないということです。 そうすれば万が一プログラムがおかしくなっても、影響範囲が狭まります。 極端を言うと、できるなら安全性が求められるプログラムを作成すること自体を 止める、というのが一番確実なのですが。

Linux ではプロセスのパーミッションは、まずその各種 ID によって決まります。 プロセスはそれぞれ実 ID、実効 ID、ファイルシステム ID、保存 ID をユーザと グループ毎に持っています。 これらの値をうまく使用して、パーミッションを最小限にすることはとても大切 なことです。

別の観点からもパーミッションを最小限に抑える理由をあげられます。

オペレーティングシステムの中には、1 つのプロセスで信頼のレベルを複数 持つものもあります。たとえば Multics のリング保護機構がそれに当たります。 一般的な UNIX や Linux では 1 つのプロセス中で信頼のレベルを複数に分ける 方法はありません。 つまり、カーネルを呼び出すことでパーミッションを上げられますが、プロセスは 単一の信頼レベルしか持てません。 Linux や UNIX ライクなシステムは 1 つのプロセスから複数のプロセスを fork して、そのそれぞれのプロセスにパーミッションを設定することで、この機能を シミュレートすることができます。 これを行なうには、安全に情報を伝達する経路(普通は名前なしパイプが使われます) を確保し、別のプロセスを fork してできる限り多くのパーミッションを落とさ なければいけません。 そして単純なプロトコルを使って信頼性の高いプロセスから低いプロセスに要求を 伝えるようにし、信頼性の高いプロセスは限られた要求しかサポートしないことを 確実に行なわなくてはなりません。

この技術は Java 2 や Fluke が強みをもつ分野の 1 つです。 たとえば Java 2 はある特定のファイルだけをオープンするパーミッションという ような、きめの細かいパーミッションを指定できます。 しかし汎用的なオペレーティングシステムでは、そのような機能は一般的に持って いません。

訳註:Fluke は、Flux プロジェクトの一環として開発されているカーネル とオペレーティングシステムの総称で、Flux μ-kernel Environment の略称 です。 nested process model にもとづき、強力で階層的なリソース管理を行い、より 安全性の高いシステムを目指しています。 詳しくは、 The Flux Research Group を参照してください。

Linux のプロセスには、ファイルシステム ユーザ ID(fsuid)とファイルシステム グループ ID(fsgid)という 2 つの Linux 固有の状態変数があります。 この変数は、ファイルシステムのパーミッションをチェックする時に使われます。 root の権限を持つプログラムは、一般ユーザに代わってファイルにアクセスする 前に fsuid と fsgid を変更することを考慮すべきです。 理由は、プロセスに 実効ユーザ ID を設定すると、そのユーザはそのプロセスに 対してシグナルを送れてしまいますが、fsuid に設定してもそうはなりません。 この方法の欠点は他の POSIX システムではこの機能が使えないことです。

6.3 デフォルトは安全に

プログラムをインストールする時には、ユーザが設定する機会まですべてのアクセス を拒否すべきです。 インストールされたファイルやディレクトリは、誰もが読み書き可能であって は決していけません。 要するに、信頼できるユーザ以外は読めなくしてしまうのが一番です。 設定をするための言語があるならば、ユーザがあえて許可しない限り、デフォルト でのアクセスは拒否すべきです。

6.4 フェイル・オープン

安全なプログラムは常に「フェイル・オープン」であるべきです。つまり、 プログラムが正しく動作しなくなっても、プログラムはすべてのアクセスを拒否 するように設計されている必要があります(「フェイル・セーフ」とも呼ばれて います)。 プログラムが不正と思われる行為(異常な入力や「起こり得ない」状態になる等) を見つけたら、すぐにサービスを拒否すべきです。 「ユーザが意図することを探り出そう」などとはしないでください。ただサービス を拒否するだけでよいのです。 こうすると、時として信頼性や使い勝手が悪くなるかもしれません(ユーザの立場 からすると)。しかし安全性は高まります。

6.5 競合状態は避けましょう

安全が求められるプログラムは、要求を許可すべきかどうかを決めなければ なりません。 そして許可したならば、その要求を実行に移さなければなりません。 プログラムを実行する前に、信頼できないユーザが判定に影響を与えるどのような 変更もできてはいけません。

ファイルシステムにおいては頻繁にこの問題が起こります。 一般的に避けなければいけないことは、プログラムが access(2)を使って要求を 認めるべきかを決定し、その後に open(2)を使うという手法です。これらのシステム コールを発行する間に、ユーザがファイルを移動できてしまうかもしれないからです。 安全が要求されるプログラムではそうするかわりに、実効 ID とファイルシステム ID をセットしてからすぐ、open システムコールを発行すべきです。 安全に access(2)を使う方法もありますが、その場合はユーザがそのファイルや ディレクトリをファイルシステムのルートからパスをたどっていじることができない 時だけです。

6.6 信頼できる経路だけ信じること

一般的に、信頼できない経路からの結果を信じてはいけません。

コンピュータで構成されたネットワーク(インターネット全体にも当てはまります) の大部分では、正当さが証明されていない伝送は信頼することができません。 たとえば、インターネット上ではどんなパケットでもそのヘッダー情報を含めて、 改ざんすることが可能です。したがって、信頼できると確証できるのでなければ、 その情報を第 1 の基準として、セキュリティ上の判断をしないでください。 ローカルのファイアーウォールが外部からスプーフィング(なりすまし)を防いで いるはずなので、本当に「内部」から送られたパケットであると断言できる場合 もあります。しかしファイアーウォールがおかしかったり、別の経路があったり、 モバイル用の接続口があったりすると、この仮定さえも疑わしいものになって しまいます。 同様な感覚で、小さいポート番号(1024 以下)を信頼できるものと決め込まないで ください。大部分のネットワークではそのようなリクエストは改ざん可能ですし、 コンピュータシステムに、小さいポート番号の使用を認めるようにすることも できます。

標準的に使われているが本質的に安全でないプロトコル(たとえば ftp とか rlogin)を実行しているなら、デフォルトを安全にしておき、ドキュメントに は実行に当たっての前提条件を明記しておいてください。

ドメイン・ネーム・サーバー(DNS)は広くインターネット上で利用されており、 コンピュータ名と IP アドレス(数値)の組合せを維持管理しています。 「DNS の逆引き」という方法を使えば、単純なスプーフィング攻撃の一部を 排除できますし、ホスト名を見つける時にも役に立ちます。 しかしこのやり方では認証を決めるほどの信頼性はありません。 つまるところ問題なのは、DNS のリクエストが結局は攻撃者がコントロールしている どこかのシステムに対して送られているかもしれない、というところにあります。 したがって、DNS から得られた結果が入力として正しいことを確認する必要が あり、重要なアクセス制御の手段として信用してはいけません。

パスワードを要求する場合、信頼できる入力をするために、一連の流れを設定 するように心がけてください(たとえば、ログインする前に改ざんできない キーを押すことを要求する、LED を点滅させて、改ざんできないパターン を表示する等)。

電子メール(「From」に書いてあるアドレスを含む)も改ざんできます。 そのような攻撃の多くは、電子署名を使えば防げます。 もっと簡単な防御は、電子メールにランダムに発生させた値を添付してやりとり する方法です。小額の金銭取引きもないような、公開メーリング・リストへの 登録ならば十分利用できます。

信頼できないネットワーク越しに信頼できる経路を必要とするならば、何らかの 暗号作成技術の助けが必要となります(最低限でも暗号的に安全なハッシュ技術)。 下記のセクションにある「暗号アルゴリズムと通信プロトコル」を参照してください。

注意して欲しいのは、CGI があるクライアント/サーバー モデルで、クライアント がどんな値も変更できてしまうことです。サーバー側は常にこの点に気をつけていな ければなりません。 例をあげると、いわゆる「隠れフィールド」、クッキーなどは、CGI プログラムが 値を受け取る前にクライアント側で値を変更できてしまいます。 クライアントが偽造できない方法で署名をするか、サーバーが署名をチェックする のでなければ、これらの値を信用してはいけません。

getlogin(3)や ttyname(3)といった関数が返す値は、ローカルのユーザが制御でき てしまうので、セキュリティの用途としてこれらを信用してはいけません。

6.7 内部の整合性をチェックするコードを使用しましょう

プログラムは、呼び出す時に指定する引数や想定している基本状態が適切であること が保証されているかをチェックすべきです。 C では assert(3)のようなマクロが役に立つでしょう。

6.8 リソースを自主規制しましょう

ネットワーク関連のデーモンでは、過負荷となる要求は拒否するか制限を設けま しょう。 限界値を設定して(setrlimit(2)を使って)使用されてしまうと予想されるリソースを 制限しましょう。 setrlimit(2)を使って「core」ファイルができないぐらいは最低限するようにして ください。 普通 Linux では core ファイルを作って、プログラムが異常終了したらそのすべて のメモリを保存するようにします。しかし core ファイルには、パスワードやその他 の注意が必要なデータがあるかもしれません。


次のページ 前のページ 目次へ