私のパソコン雑記帖

crypt 関数の振舞い (続き)

カテゴリー: PHP
20Sep2006

crypt 関数の怪

crypt 関数に取り組んで大分理解が深まったと実感。ところが、ここでどうしても理解に苦しむスクリプトに遭遇。Crypt Manual の最初にあげられているサンプルスクリプトがそれ。

$password = crypt("My1stPassword");
if (crypt($user_input, $password) == $password) {
echo "Password verified!"; }

ちなみに $user_input に "My1stPassword" という文字列を与えると "Password verified!" となります。このことを日本語の平常文で記述すると・・・。

ある文字列を第2引数なしでハッシュしたものと、そのハッシュ文字列を第2引数にしてハッシュしたものとは等しい。

どうしてこうなるのか腑に落ちない。まさに crypt 関数の怪、と自分には思えます。crypt 関数の仕組みを良く理解している人にとっては自明のことなのでしょうけど。なおこの例で $password は第2引数なしで MD5 ハッシュしているが、英数字で始まる引数を与えて STD_DES ハッシュしても同じ結果が得られました。


腑に落ちなくて役立つことに変わりない・・・

腑には落ちないけれど、このコードの有用性はよく分かります。.htaccess による Basic 認証と同じ事を php 実装する手段として使えるからです。早速応用してみました。

certify というテーブルに userid と pwd をあらかじめロード(管理する userid/pwd が少なければテキスト・ファイルで十分)。ロードしておく pwd は引数なしで crypt されたMD5 モードハッシュ(参照


<?php
$userid=$_POST['userid'];//フォームから入力
$pwd=$_POST['pwd'];//フォームから入力
if (certification($userid, $pwd)) {
print "認証されました<br> ";
$pwd1=crypt($pwd); //pwd 更新
  $db=mysql_connect("$host", "$user", "$passwd");
if (!$db) { print ("DB open failed."); exit; }
$sql="select pass from certify where id='$userid'";
$rs=mysql_db_query($dbname,$sql);
if (!$rs) { print ("select pass failed.<br> "); exit; }
$sql1="update certify set pass='$pwd1' where id='$userid'"; //更新された pwd で書き換え
$rs1=mysql_query ($sql1);
if (!$rs1) { print "update pass failed.<br> "; exit; }
}
function certification($userid, $pwd) {
$db=mysql_connect("$host", "$user", "$passwd");
if (!$db) { print ("DB open failed."); exit; }
$sql="select id, pass from certify";
$rs=mysql_db_query($dbname,$sql);
if (!$rs) { print ("select failed.<br> "); exit; }
$accounts=array();
while($row=mysql_fetch_array($rs)) {
$accounts[$row[0]]=$row[1];
}
print_r($accounts);
if (!isset($accounts[$userid])) { print "そのIDは登録されていません<br> "; exit; }
if (crypt($pwd, $accounts[$userid]) != $accounts[$userid])
{ print "パスワードが間違っています<br> "; exit; }
return TRUE;
}
?>

朱記部分は最初に例示したサンプルスクリプトからの引用。その2行上の print_r($accounts); は userid と pwd を配列表示。認証が成功する毎に pwd が新しく生成・書き換えられる様子を確認するために挿入(実コードでは不要)。

最終的にはこのスクリプトをコアに、更に手を加えて自分用のツールとして使用しています。当初の目的達成!


傍受のリスクはどうなる?

crypt 関数を使ってサーバー側は手が打てるようになりました。しかしブラウザ・サーバー間通信における傍受のリスクは解消されていません。このリスクを軽減するために、ブラウザ・サーバー間もハッシュや暗号で流す方法が色々あるようです。例えば Basic 認証にかわる Digest 認証。しかし私のサーバー環境 xrea ではサポートされていませんでした。それでなくてもこの方法はリスクを解消しないと解説されています。

傍受のリスクを最小限にしようとするなら公開鍵暗証を使うしかないという。それなら SSL を使うに限ると割り切ることにしました。幸い xrea では SSL サーバーも使用できます。

ただしブラウザが対応しているかどうか?今は殆ど対応しているとはいっても、全てとはいいきれません。しかしこれも割り切るしかないと思います。リスクの程度によっては non-SSL サーバーへの切替えを提供してもいいのですが、原則として SSL 非対応ブラウザからの通信は受け付けないということ。

リスクがクライアント側にしか及ばない場合は、注意を促した上で non-SSL サーバーへの切替えも可能にする。リスクがサーバー側にも及んだり管理責任に及ぶ可能性があるなら、SSL 非対応のブラウザからのエントリーは丁重に断るしかないと思います。



コメント