2    トラステッド・プログラミング技法

この章では,トラステッド・プログラムを設計するための技法を紹介しています。この章では,次の内容について説明しています。

2.1    SUID プログラムと SGID プログラムの書き方

SUID (セット・ユーザ ID) プログラムと SGID (セット・グループ ID) プログラムでは,プロセスの実効 UID または GID が,プログラムの UID または GID に変更されます。これらのプログラムでは,ファイルの所有者のアクセス権がプロセスに与えられるため,システム・レベルのファイルやディレクトリへのアクセス制御に関する問題のソリューションとなります。

ユーザ ID に root が設定されるプログラムや,グループ ID にシステム・レベルのファイルへの書き込みアクセスが可能なグループが設定されるプログラムでは,セキュリティが脅かされる可能性が高くなります。作業を実行する方法が他にない場合以外は,ユーザ ID を root に設定するプログラムは作成しないでください。

chown システム・コールでは,実行しているプロセスの RUID にゼロが設定されていない限り,ファイルの SUID や SGID ビットが自動的に削除されます。これにより,root アカウントが所有する SUID プログラムまたは SGID プログラムを誤って作成することを防止できます。詳細については, chown(2) を参照してください。

以下は,さらに安全な SUID プログラムおよび SGID プログラムを作成するためのアドバイスです。

可能であれば,SUID プログラムではなく SGID プログラムとしてください。この理由の 1 つとして,ユーザのファイル・アクセスよりもグループのファイル・アクセスの方が一般的に制限されていることがあげられます。SGID プログラムが傷つけられた場合,ファイル・アクセスが制限されているため,攻撃者が行える動作の範囲が狭くなります。

別の理由として,SGID プログラムを実行したユーザが所有するファイルへのアクセスが比較的簡単であることがあげられます。ユーザが SUID プログラムを実行した場合,元の実効 UID をファイル・アクセスに使用することができなくなります。しかし,ユーザが SGID プログラムを実行した場合,ユーザの 1 次 GID はグループ・アクセス・リストの一部として,利用できる状態のままとなります。このため,SGID プロセスは,ユーザがアクセスできるファイルへのグループ・アクセス権を持ったままとなります。

すべての SUID プログラムのスタックは,省略時は実行可能ではありません。スタックが実行可能であることを前提としているユーザ・アプリケーションは失敗します。どうしても必要な場合は,省略時の設定を変更することができます。これにより,SUID プログラムのスタックを実行可能にすることができます。省略時のゼロ (実行不可) を実行可能に変更するには,次のコマンドを使います。

# sysconfig -r proc executable_stack=1

リブートしても変更が適用されたままにするには,sysconfigdb コマンドを使って,エントリを /etc/sysconfigtab ファイルに追加します。

2.2    エラー処理

ほとんどのシステム・コールとライブラリ・ルーチンは,呼び出しの成功または失敗を示す,整数のリターン・コードを戻します。必ずリターン・コードをチェックして,ルーチンが正常終了したことを確認してください。呼び出しが失敗した場合は,グローバル変数 errno を調べて,失敗の原因を見つけてください。

errno 変数が設定されるのは,システム・コールでエラーが発生したときです。この値を使用すると,エラー状態の詳細を知ることができます。この情報は,プログラムでの対応方法を決定したり,より詳しい診断メッセージを作成するのに役立ちます。このエラー・コードは,<errno.h> 内のエラー名に対応しています。詳細については, errno(2) を参照してください。

次の errno 値は,セキュリティ侵害の可能性を示します。

EPERM

ファイルの所有者またはスーパユーザだけが許されている方法で,所有者以外のユーザがファイルを変更しようとしたことを示します。また,スーパユーザだけが許されている方法で,ユーザが何らかの操作を行おうとしていたことを示す場合もあります。

EACCES

許可されていないファイルをユーザがアクセスしようとしたことを示します。

EROFS

マウントされたファイル・システムのアクセス許可が無効になっているときに,そのファイル・システム上のファイルにアクセスしようとしたことを示します。

特権システム・コールを使用するプログラムは,生成された実行可能プログラムにスーパユーザ特権がない場合,特権システム・コールを実行しようとすると失敗します。セキュリティ管理者が特権システム・コールの実行失敗を記録するように監査システムを設定している場合は,失敗が記録されます。

プログラムがセキュリティの侵害と考えられる現象を検出した場合,攻撃者がこのプログラムを攻撃するために役立つような診断メッセージを表示しないでください。たとえば,攻撃者の実ユーザ ID (UID) がアクセス対象ファイルの UID と一致しなかったためにプログラムが終了するというメッセージを表示したり,アクセス対象ファイルの名前を表示したりしないでください。この情報は,SUID root プログラムの場合は audgen() ルーチンを使い,その他のプログラムの場合は syslog を使うことで制限してください。この他,メッセージを表示する前に遅延を少し入れることで,さまざまな入力データを系統的に試してプログラムに侵入する,プログラム化された侵入操作を防ぐことができます。

2.3    ファイルの保護

プログラムが永続ファイル (たとえば,データベース) を使用する場合は,これらのファイルのアクセス許可を制限し,プログラムでアクセスを制御するようにしてください。これらの注意事項は,共用メモリ・セグメント,セマフォ,およびプロセス間通信のメカニズムに対しても適用されます。これらのすべてのオブジェクトには,制限された許可を設定してください。

プログラムは,実行中にデータを格納するため,一時ファイルを作成することがあります。一時ファイルを使う場合は,次の注意事項に従ってください。

一般的な方法は,一時ファイルを作成し,そのファイルがまだオープンされている状態でリンクを解除 (unlink) することです。これにより,アクセスは,リンクを解除する前にファイルをオープンしたプロセスだけに限定されます。プロセスの終了時には,i ノードが解放されます。

NFS マウントされたファイル・システム上で unlink をこのように使用すると,少し異なる動作になります。クライアントのカーネルはファイルの名前を変更し,プロセスの終了時に NFS にのみリンク解除が送られます。他者からそのファイルにアクセスできなくすることはできません。ただし,プロセスが終了したときには,そのファイルは確実にアクセスできなくなります。いずれにしても必ず明示的に,プロセス終了時後には一時ファイルが残らないようにしてください。

2.4    安全な探索パスの指定

popensystem,または exec*p ルーチンを使用して /bin/sh または /sbin/sh を実行する場合は,パス名の指定やシェル変数 PATH の定義を慎重に行ってください。 PATH 変数は,システム上のコマンドとスクリプトを実行するための探索パスを指定するため,セキュリティ上重要な変数です。詳細については, environ(7)popen(3),および system(3) を参照してください。

安全な探索パスを作成する方法を,次に示します。

execve システム・コールにはパス名を指定する必要があるため,exec*p ルーチンではなく,execve を使用する方が良い場合もあります。詳細については, execve(2) を参照してください。

2.5    シグナルへの応答

Tru64 UNIX オペレーティング・システムは,特定のイベントに対する応答としてシグナルを生成します。イベントは,ユーザの端末 (たとえば,終了,割り込み,または停止),プログラムのエラー (たとえば,バス・エラー),または別のプログラム (たとえば,kill) により起動されます。

特に指定しなければ,ほとんどのシグナルで受信プロセスが終了します。しかし,いくつかのシグナルでは受信プロセスは停止するだけです。SIGQUIT や SIGTRAP などの多数のシグナルでは,デバッグの目的でコア・イメージがファイルへ書き込まれます。コア・イメージ・ファイルには,パスワードのような機密情報が含まれている可能性があります。

コア・イメージ・ファイル内の機密情報を保護し,キーボードからの入力による割り込みからプログラムを保護するには,SIGQUIT,SIGTRAP,または SIGTSTP などのシグナルを捕捉するプログラムを書きます。

signal ルーチンを使って,シグナルに対するプロセスの応答を変えるようにします。これらのルーチンによってプロセスは,シグナルが送信されたときにそれを無視したり,サブルーチンを呼び出したりといったことが可能です。(SIGKILL シグナルおよび SIGSTOP シグナルのキャッチ,無視,ブロックはできません。これらのシグナルは必ず,受信プロセスに渡されます)。詳細については, signal(3) および sigvec(2) を参照してください。

子プロセスは,親プロセスが fork を呼び出す前に設定したシグナル・マスクを継承します。execve システム・コールは,捕捉済みのすべてのシグナルを省略時の動作にリセットします。無視されるシグナルはそのまま無視されます。 このため,fork または execve を呼び出す前に,プロセスが適切にシグナルを処理するようにしてください。詳細については, fork(2) および execve(2) のリファレンス・ページを参照してください。

2.6    子プロセスでのオープン・ファイル記述子の使用

子プロセスは,その親プロセスのオープン・ファイル記述子すべてを継承することができます。このため,同じようにファイルにアクセスすることができます。この関係により,セキュリティ上の考慮が必要となります。

たとえば,次の処理を行う,セット・ユーザ ID (SUID) プログラムを作成するとします。

親の SUID プロセスは書き込み用にファイルをオープンするため,子プロセス (または,子プロセスを実行しているすべてのユーザ) は,重要なファイルに書き込みを行うことができます。

機密性の高い特権ファイルを子プロセスのユーザから守るには,子プロセスを作成する前に,子プロセスに不要なファイル記述子をすべてクローズします。 子プロセスを作成する前にファイル記述子をクローズする効果的な方法は,fcntl システム・コールを使うことです。 この呼び出しを使用すると,ファイルをオープンした後で,そのファイルに close-on-exec フラグを設定することができます。このフラグが設定されているファイル記述子は,exec システム・コールで新たなプログラムをプロセスが開始するときに,自動的にクロースされます。

詳細については,リファレンス・ページの fcntl(2) を参照してください。

2.7    X 環境でのセキュリティ上の考慮事項

以降の項では,X プログラミング環境でセキュリティを強化する方法について説明しています。

2.7.1    キーボード入力の保護

アクセス制御リストにリストされたホストへログインしたユーザは,XGrabKeyboard 関数を呼び出してキーボードの制御を行うことができます。クライアントがこの関数を呼び出すと,X サーバはすべてのキーボード・イベントをそのクライアントだけに送ります。攻撃者はこの呼び出しを使用して,あるウィンドウからの入力ストリームを取得し,別のウィンドウへ転送することができます。攻撃者は,シミュレートされたキーストロークをウィンドウへ戻し,このウィンドウを実行しているユーザをだますことができます。このためユーザは,不正が行われていることに気付かない可能性があります。

攻撃者がユーザのキーストロークを取り込むことができると,ワークステーション上に格納されたデータの機密性が脅かされます。

X ウィンドウには,セキュア・キーボード・モードがあります。このモードでは,ワークステーションのキーボードからのユーザの入力がすべて,1 つの安全なウィンドウへ送られます。ユーザがこのモードを設定するには,X ウィンドウの [Command] メニューから [Secure Keyboard] 項目を選択します。

機密データを扱うプログラムには,セキュア・キーボード・モードを含めてください。この予防策は,プログラムがユーザにパスワードの入力を求める場合に特に重要です。

セキュア・キーボード・モードを実現するためのガイドラインを,次に示します。

2.7.2    キーボードおよびマウスのイベントのブロック

アクセス制御リストにリストされたホストは,ウィンドウの ID を知っていれば,どのウィンドウにもイベントを送ることができます。XSendEvent 呼び出しを使用すると,呼び出し側アプリケーションは,キーボードまたはマウスのイベントを指定のウィンドウへ送ることができます。攻撃者はこの呼び出しを使用して,破壊を行うためのデータをウィンドウへ送ることができます。たとえばこのデータにより,rm -rf * コマンドを実行したり,機密ファイルの内容をテキスト・エディタを使って変更したりすることがあります。端末がアイドル状態の場合,ユーザはこれらのコマンドが実行されていることに気付かない可能性があります。

攻撃者が破壊を行うためのデータをワークステーションのウィンドウへ送ることができると,ワークステーション上に格納されたデータの完全性が脅かされます。

.Xdefaults ファイル内の allowSendEvents リソースに False が設定されている場合,X ウィンドウは,別のクライアントから送られて来たキーボードおよびマウスのイベントをブロックします。

別のクライアントから送られて来たイベントをブロックするプログラムを書くこともできます。XSendEvent 呼び出しは,指定されたウィンドウへイベントを送り,イベント構造体の send_event フラグに True を設定します。プログラムでは,受け付けた各キーボードおよびマウスのイベントについて,このフラグをテストします。このフラグに False が設定されている場合,イベントはキーボードで生成されたものなので,受け付けても安全です。

2.7.3    デバイス関連イベントの保護

キーボードおよびマウスのイベントのようなデバイス関連イベントは,次の条件のいずれかを満たすまで,ソース・ウィンドウから祖先のウィンドウへと上方へ伝えられます。

XReparentWindow 関数を使って,ウィンドウの親を変更することができます。この呼び出しは,ウィンドウの親を,同じ画面上の別のウィンドウに変更します。ウィンドウの親を変更する上で知っておく必要があるのは,ウィンドウ ID だけです。子のウィンドウ ID を使って,その親のウィンドウ ID を見つけることができます。

XReparentWindow の呼び出しを誤って使用すると,ウィンドウ・システムのセキュリティが脅かされます。新しい親ウィンドウは,子ウィンドウが選択しなかったイベントを選択することができます。

この種の悪用から守るためには,次のような予防策をとってください。

2.8    シェル・スクリプトの保護

機密データを処理するシェル・スクリプトを書くときは,スクリプトの本体より前で PATH 変数を設定してエクスポートします。シェル・スクリプトには SUID や SGID は設定しないでください。