プログラムが何かに対して排他的に実行する権限を持っているかどうかの裏付けを 取らなければならない状況がよくあります。 POSIX システムでは、以前からロック状態を示すファイルを作成することで対処 してきました。それは多くのシステム間で互換性を取るにはよい方法だからです。
しかしこの方法にはいくつか避けなければならない落とし穴があります。 まず root 権限を持っているプログラムは、O_EXCL モード(通常ファイルが存在して いると失敗する)のファイルでもオープンできてしまいます。 そうなると open(2)を使わず link(2)を使用してファイルを作成する必要があります。 マシン上でサーバープログラムが 1 つだけしか実行できないことを確実に行いたい なら、/var/log/名前.pid という中身がそのプログラムの pid が書かれているロック ファイルを作成することを検討してみてください。 この方法は、プログラム予想外に中断して中途半端な状態になってしまう、という 欠点を抱えていますが、一般的に使用されており、他のシステムツールからでも簡単 に利用できます。
次に、ロックファイルが NFS でマウントされたファイルシステム上にある場合、 NFS が通常のファイルの機能を充分にはサポートしていない問題に悩まされること になります。 open(2)のマニュアルにどうしたらこの問題を解決できるかの説明が あります(root 権限のプログラムについての扱い方についても説明があります)
... ロックをかけるのに「open(2)の O_CREAT and O_EXCL フラグ」に依存して いるプログラムは、競合状態に陥る問題を抱えています。 ロックファイルを使ってアトミックなファイルロッキングを行うには、 まず同じファイルシステム上にユニークなファイルを作り(たとえばホス ト名と pid を組み合わせます)、次にこのロックファイルに link(2)でリ ンクを張り、それからユニークなファイルに対して stat(2)を行ってリンク の参照数が 2 に増えていることを確認します。 link(2)の返り値を使うのは止めてください。
できるなら、パスワードを扱うコードは自前で書かないようにしてください。 特にローカルなアプリケーションの場合、通常行うユーザのログイン認証にまかせて しまってください。 アプリケーションが CGI スクリプトの場合、Web サーバーが用意している防御にまか せてください。 アプリケーションをネットワーク経由で利用するのものなら、平文でパスワードを 送ることを止めてください(可能なら)。というのはネットワークを盗聴することで、 いとも簡単に横取りされて後で使われてしまうからです。 ネットワークで利用するなら、少なくともダイジェスト・パスワードの使用を考えて ください(直接しかけてくる攻撃には弱いのですが、ネットワークの盗聴に対しては 有効です)
アプリケーションがパスワードを扱う必要があるなら、使ったらすぐ上書きして しまってください。そうすればパスワードを見られる時間が短くなります。 Java ではパスワードを保存しておくのに String 型を使わないようにしてください。 String 型は内容を変更できない型だからです(String 型は不要メモリ領域の整理や 再利用をしない限り上書きできない仕様で、今後もその仕様のままだと思います) そのかわり char[] を使って保存してください。この方法だとすぐにでも上書きでき ます。
アプリケーションでユーザがパスワードを設定できるのならパスワードをチェック して、「適切な」パスワードだけを許可してください(辞書に載っていない、一定 以上の文字数である、など)。 適切なパスワードの付け方を見つけたいなら、 http://consult.cern.ch/writeup/security/security_3.html を見てはどうでしょうか。
Linux カーネル(1.3.30 以上)には乱数生成機能があります。 乱数生成には周囲で発生するノイズをデバイスドライバや他の情報源から 収集してエントロピー・プールに収めます。 /dev/random にアクセスするとエントロピー・プールにあるノイズから推定 されたビット数の範囲でだけ、ランダムな値が返されてきます(エントロピー・ プールが空の場合は、周囲からノイズが集まってくるまで、呼び出しをブロック します)。 /dev/unrandom でアクセスして、大きな値を要求すると、エントロピー・プール が使い果たされても値が返ってきます。 乱数を暗号化の目的で利用するなら(たとえばキーの生成のため)、/dev/random を 使ってください。 さらに詳しい情報は、システムにあるオンラインマニュアルの random(4)を参照 してください。
暗号アルゴリズムと通信プロトコルは、システムの安全を維持するのに必要で、 特にインターネットのような信頼できないネットワークを経由してやりとりを 行う場合は必須です。 可能ならば通信セッションを暗号化し、セッション乗っ取りの裏をかいてください。 こうすれば認証情報を隠蔽でき、プライバシーの保護にも役立ちます。
きちんとした暗号化アルゴリズムや通信プロトコルを作り上げるには困難がともない ますので、自分で作ろうとはしないでください。 そのかわりに、一般的に信頼性が確立されている既存のプロトコルである、SSL、SSH、 IPSec、GnuPG/PGP や Kerberos を利用してください。 広く公開され、長年の攻撃に耐えてきた符号化アルゴリズムだけを使ってください (たとえばトリプル DES など、特許による利用の妨げがないもの)。 特に、自分が暗号化の専門家で、何をしているかを把握しているのでなければ、 符号化アルゴリズムを作成するようなことはしないでください。 この種のアルゴリズムの作成は、専門家だけに許された作業です。
関連して、どうしても独自に通信プロトコルを開発しなければならないなら、 過去に起きた問題事例の調査をしてください。 Bellovin [1989] に載っている TCP/IP プロトコルにおけるセキュリティについての レビューのような古典的な資料や Bruce Schneier [1998] が役に立つと思います。 また、Mudge 氏によるマイクロソフト社の PPTP の実装破りやその後の推移も 参考になると思います。
訳註:Mudge 氏は、L0pht Heavy Industries という超ハッカー(クラッカー ? 評価が別れているようです)集団の主任科学者でした。現在は @Stake というセキュリティ関連のサービスを提供する会社で研究開発担当副社長をして います。
もちろん新しいプロトコルに対しては、広くレビューを行うべきで、できるなら 再利用してください。
Linux 上のセキュリティに関係したプログラムの中には Java や Java バーチャル・ マシン(JVM)を使って実装されているものがあります。 Java で安全性を要求されるプログラムを書くには Gong [1999] 他の資料で詳細 が述べられています。 下記に Gong [1999] から抜粋したキーポイントをあげておきます。
Linux ディストリビューションのほとんどは、PAM(Pluggable Authentication Modules)を持っており、ユーザの認証に柔軟に対応できるしくみになっています。 カーネルのバージョンが 2.2 系列である Red Hat Linux、Caldera、Debian が採用 しており、FreeBSD のバージョン 3.1 も採用しています。 PAM を使うと、プログラムと認証のしくみ(パスワードやスマート・カード)を独立 したものにできます。 つまり、プログラムは PAM を呼び出し、PAM がローカルシステムの管理者が設定した 内容をチェックし、どの「認証モジュール」が必要かをランタイムに判断します。 認証が必要となるプログラム(たとえばパスワードを入力する)を作成しているなら、 PAM を採用すべきです。 詳細は、Linux-PAM プロジェクトの Web サイトである、 http://www.kernel.org/pub/linux/libs/pam/index.html を参照してください。
前提条件の少なくとも一部は、使う前にプログラムにチェックさせてください (たとえば、プログラムが開始されるところで)。 たとえば、あるディレクトリで「sticky」ビットが立っていることを前提にしている なら、本当にそうなっているかをテストしてください。テストに時間はかからない ですし、それによって深刻な問題を防ぐことができるはずです。 もしそれぞれの呼び出しでテスト実行時間がかかることが気になるなら、インストール 時には最低限行うようにしてください。
監査ログはプログラム起動時やセッション開始時、動作が怪しげな時に書き込むよう にしてください。 情報として考えられるものは、年月日、時刻、uid、euid、gid、egid、端末情報、 プロセス id、コマンドラインの値です。 監査ログを採るに当たっては、syslog(3)関数が参考になると思います。
インストール・スクリプトはできるだけ安全にプログラムをインストールして ください。 デフォルトでは、インストールするファイルすべてを root か他のシステム管理 ユーザの所有にして、他のユーザが書き込みができないようにしてください。 こうすれば root 以外のユーザはウイルスをインストールできません。 root 以外のユーザがインストールできる場所も用意しておいておけば、root の権限 を持たないユーザやインストーラを信じきれない管理者でもプログラムを使えるよう になります。
可能であれば、root に setuid や setgid されたプログラムは作らないでください。 ユーザには root でログインするようにさせてください。
コードに電子署名をしてください。そうすれば利用者は送られてきたものが利用できる ものかどうかをチェックできます。
安全性が求められるプログラムを作成する場合に静的にリンクを行うことを検討して みてください。 安全性が求められるプログラムが動的リンクを使わないようにすれば、動的なライブ ラリのリンク機能を狙った攻撃に対抗できます。
コードを眺めている時には条件にマッチしないケースすべてを検討してください。 たとえば switch 文があった場合、どのケースにもマッチしなかった場合どうなる のか? 「if」文があって条件が偽だった場合、どのように処理されるか?
プログラムをコンパイルする時や実行する時にチェック機能を確実に働かせるように して、本番で動かす時もそうしておいてください。 Perl のプログラムなら警告フラグ(-w)をつけるべきです。そうすると危険になる かもしれないコードやすでに古い文法になってしまった式に対して警告を出してくれ ます。また汚染予防フラグ(-T)をつけると、何らかのフィルタリングをかけない限り、 信頼できない入力を直接使用できなくなります。 セキュリティ関連のプログラムは警告をすべて有効にして警告が出ないように コンパイルすべきです。 gcc を使って C や C++ をコンパイルするには、少なくとも下記のコンパイル時の フラグを使用してください(多数の警告メッセージを有効にして、警告すべてを 潰してください)。
gcc -Wall -Wpointer-arith -Wstrict-prototypes