サーバーを自由に弄れない環境でのセッションID生成2006年06月20日 10時50分51秒

あらまし

頼まれ仕事なのですが、こんな感じの Web スペース上で、「管理者だけが閲覧できるページ」を含む Web アプリを実現せなあかんちうことになりまして、もちろん基本認証とかも使えないので、CGI だけで認証機構を用意しないといけないのですが、その実現方法をどうしようかなぁというお話。

実は、既にプログラムは Perl で途中まで組まれていて、認証機構の実現で幾分か行き詰っている状態なのであります。重要なのは、信頼性の高いハッシュ関数と、暗号理論上安全な乱数の生成であり、これらは Perl の場合、前者は Digest::SHA かもしくは MD5、後者は Math::TrulyRandom といったモジュールを追加してあげれば確実に実現可能なわけですが、自分で設定できるサーバーを使うわけではないため、これらのモジュールを組み込んで使うことは不可能だったりするわけです。

# gcc をサポート、とあるので、各モジュールのソースをダウンロードして make まではできるのかもしれないけどね。それができれば CGI プログラムと同じ場所においておくことでとりあえず動かすことはできるけど。。。そもそも telnet や ssh が使えるわけでもないのにどうやって make を走らせるんだろう?

んで、一体どうしたもんかなぁという辺りをぐだぐだと考えてみるテストなのです。

ハッシュ関数

PHP3 が使えるので、PHP の md5() 関数を使うという手がある。Perl から PHP の関数を利用するには、例えばこんな風に書けばいいんじゃないかな。

my $digest = `php -r 'echo md5("$text");'`;

むしろプログラム自体を PHP で書け、という話もあるのですが。。。まぁ拡張子を「.php3」にすれば確かに PHP3 環境のテストもできるけどさぁ。

PHP を外から使うのがまどろっこしいと思うのであれば、あとは crypt 関数を利用する、という手もある。この手の無料 Web スペースや安価なレンサバなんかの場合、OS は大概 Linux だろうと思われるので、crypt がサポートする暗号方式もおそらく 64 bits DES 方式かと思われる。その場合、ハッシュ化できる文字列は最大 8 文字までなので、crypt 関数自体を複数回に分けで呼び出してあげる必要があると思う。あーマンドクセ('A`)

乱数

セッション ID を生成する基本的なやり方は、状況を識別できる情報 (ユーザー ID、時刻、その他必要に応じてシチュエーションを識別する記号) と、暗号理論上安全な乱数とを混ぜ合わせた文字列*1を、ハッシュ関数によって符号化*2する、というもの。十分に識別可能であり、また、十分に予測不可能であることが必要とされるわけです。

で、その肝心の乱数ですが、仮にサーバーの OS が Linux ならば、/dev/random/dev/urandom を使用することができます。例えば Perl で 64 bits (= 8 Bytes) の乱数列を得るには、以下のように記述することになります。

my $text;
open randin, "</dev/random"  or die 'Cannot use /dev/random';
read randin, $text, 8;
close randin;

Manpage を見ると、/dev/random はより安全だが、エントロピー・プールが空になるとブロックされてしまう、とある。実際に使ってみると分かるのだが、キーボードやマウスからの入力がないと、数バイト読み出しただけで処理はブロックされてしまう。ホスティングサーバーなどはヒューマン・インタフェースが接続すらされていない可能性が高いため、これでは使い物にならない。かといって、/dev/urandom では、ブロックはされないが、環境ノイズが十分に反映されない可能性があるため、決して安全であるとはいえないかもしれない。

ちなみに、PHP には mt_rand() という関数があり、普通に rand() 関数とか使うよりはマシっぽそうに見えるのだが、これが採用している擬似乱数生成法である Mersenne Twister暗号論的な擬似乱数ではないため、これだけでは安全ではない。でも Mersenne Twister の公式サイトには 従来存在した「線形疑似乱数(LFSRなど)プラスSHA」の 線形擬似乱数の代わりに MT を使うことは有効 なんて書かれてるんだよなぁ。。。いくら非可逆圧縮かましたところで、予測可能な数列であることに変わりはないと思うんだけどなぁ。

*1 認証がらみの場合は更にパスフレーズを含めてしまうことで、ユーザーが確かに認証を通過していることを担保する、という考え方もある。つか、むしろそうすべきなのかな。
*2 「暗号化」ではなく「符号化」である、ってのがミソ。MD5 や SHA が生成するハッシュは、必ずしも元となるテキストのみを識別するわけではなく、まったく違うテキストからでも同じハッシュ値が生成される可能性もあるため、厳密には暗号ではない。ちなみに、同じハッシュ値が生成されるテキストのことをコリジョンなどと呼んだりする。コリジョンが法則的に見つけられるようになると、これらのハッシュ関数の暗号としての強度はかなり落ちてしまうことになる。で、こんなニュースが業界で騒がれたりするわけです。つっても、こいつが弊害となるシチュエーションってのは、例えばハッシュ関数をダウンロードしたファイルの一意性 (つまり偽者のファイルだったり、ウィルスとかに感染してたりしていないこと) を証明する目的で使用する場合などであって、セッション ID (ワンタイムトークン) として使う分にはそんなに気にする必要は無かったりするわけですが。。。