日記
おまけ: 認証処理の基本Edit

本書では認証関連の記述は、「ひとまずBASIC認証を使っておきましょうね」でお茶を濁してしまったが、ここでもうちょっと細かい認証周りの話について説明しておく。

一般的な認証では、秘密のパスワードを入力し、それがサーバーに記録されているパスワードと合致する→認証を通る、ということになる。複数のユーザーを判別する場合は、ユーザーを特定するIDとそれに対応したパスワードの組み合わせで認証することになる(ユーザーが1人しかいないならばパスワードだけで認証できる)。

もっとも単純な認証のサンプルを書くと、以下のようになるだろう。

login.html

<form method="POST" action="login.php">
ID: <input type="text" name="userid" size="15"><br>
パスワード: <input type="password" name="pwd" size="12"><br>
<input type="submit" value="ログイン">
</form>

login.php

<?php
$userid
= isset($_POST['userid']) ? $_POST['userid'] : '';
$pwd = isset($_POST['pwd']) ? $_POST['pwd'] : '';

if (
check_login($userid, $pwd)) {
  echo
'認証OK!';
} else {
  echo
'認証できませんでした';
}

function
check_login($userid, $pwd)
{
  
$accounts = array(
    
'test' => 'testpwd',
    
'test2' => 'test2pwd',
  );
  if (!isset(
$accounts[$userid])) {return FALSE;} //そのIDは登録されていません
  
if ($accounts[$userid] != $pwd) {return FALSE;} //パスワードが間違っています
  
return TRUE;
}
?>

login.htmlでユーザーIDとパスワードを入力し「ログイン」ボタンを押すと、認証を通った場合は「認証OK!」と表示され、何らかの理由で認証を通らなかった場合は「認証できませんでした」と表示される。

ここでまず注意する点としては、IDやパスワードを入力するフォームでの投稿には、必ずPOSTメソッドを利用することだ。もしもGETメソッドを利用した場合、IDやパスワードがURLのQUERY_STRINGとして現れてしまい、HTTP_REFERERなどを通して第三者に漏れてしまう危険性が増す。

また、パスワード入力欄には<input type="password">を使用することで、ブラウザ上でその入力内容が平文では見えないようになることも気をつけておこう。これは、横で見ている第三者等にパスワード文字列が漏れないようにするために重要だ。<input type="text">にしておいたのでは、入力されている文字列がそのままブラウザ上で表示されてしまう。

※ちなみにここではSSLの利用は考慮しない。もちろん本当にインターネット上での認証情報の安全なやりとりを考えるならばSSLは必須となるが、個人レベルでのblogツールの認証にSSLを利用するのは希だろう。逆に言うとSSLを利用しないWebアプリケーションでは、それ相応の情報しか取り扱わないようにするべきだ。

ただし、本当は上記のようなサンプルには大きな問題がある。それはソースコード上に平文でパスワード情報が記録されていることだ。ソースコードは(特に共用サーバーでは)第三者によって見られる可能性が高い。また、そうでなくてもパスワードが平文で書かれたデータは、できるだけ持たない方がいい。

パスワードを平文で持たずにどうやって認証処理を行うのかというと、よく使われる仕組みとしては、一方向ハッシュ関数を利用する。一方向ハッシュ関数とは、ある文字列(データ)を元に、非常にランダム性の高い結果文字列(値)を導き出す関数だ。一方向ハッシュ関数で導き出された結果から、その元となった文字列は予測することができず、また非常に似通った(ちょっとしか違わない)文字列を元に計算した場合でも、その結果はまったく異なったものとなる、という特徴がある。一方向ハッシュ関数を使った認証処理は、具体的には以下のようなコードとなる。

login.php

<?php
$userid
= isset($_POST['userid']) ? $_POST['userid'] : '';
$pwd = isset($_POST['pwd']) ? $_POST['pwd'] : '';

if (
check_login($userid, $pwd)) {
  echo
'認証OK!';
} else {
  echo
'認証できませんでした';
}

function
check_login($userid, $pwd)
{
  
$accounts = array(
    
'test' => '342df5b036b2f28184536820af6d1caf',
    
'test2' => '145799acd781cf81c354c4398ae5ac53',
  );
  if (!isset(
$accounts[$userid])) {return FALSE;} //そのIDは登録されていません
  
if ($accounts[$userid] != md5($pwd)) {return FALSE;} //パスワードが間違っています
  
return TRUE;
}
?>

$accountsでは、先ほど平文でパスワードを記録していたところに、なにやら16進数文字列のようなものが記述されている。これはmd5関数を使って、「testpwd」および「test2pwd」という文字列のMD5ハッシュ値を計算した結果だ。一方向ハッシュ関数を使って導き出した結果からは、通常その元となった文字列を得ることが非常に難しい。

つまり、上記のような状態でソースコードに記述しておくぶんには、ソースコードを見られても実際のパスワード文字列がばれることはなく、しかも認証チェック時には、「$accounts[$userid] != md5($pwd)」とすることで確実にそのパスワードが正しいかどうかを確認することができる。

しかし、上記のようなやり方では一つのパスワードから必ず同じMD5ハッシュが生成されてしまうため、もしも同じMD5ハッシュ値が存在した場合、それらは同じパスワードを元にしている(可能性が高い)ことが簡単にばれてしまう。そこで、パスワード認証処理は、crypt関数を使って次のように書いた方がより良い。

login.php

<?php
$userid
= isset($_POST['userid']) ? $_POST['userid'] : '';
$pwd = isset($_POST['pwd']) ? $_POST['pwd'] : '';

if (
check_login($userid, $pwd)) {
  echo
'認証OK!';
} else {
  echo
'認証できませんでした';
}

function
check_login($userid, $pwd)
{
  
$accounts = array(
    
'test' => '$1$vb2.Mc0.$ULZpR42LRQlquCawYCV7A/',
    
'test2' => '$1$3x5.OO4.$VCbPWeaHToMbg6TUwk0sM1',
  );
  if (!isset(
$accounts[$userid])) {return FALSE;} //そのIDは登録されていません
  
if (crypt($pwd, $accounts[$userid]) != $accounts[$userid]) {return FALSE;} //パスワードが間違っています
  
return TRUE;
}
?>

今度は、md5をつかった例では16進数文字列が書かれていたところに、なにやら記号のようなものが書かれている。これは、「crypt('testpwd')」および「crypt('test2pwd')」を実行した結果だ。crypt関数では、第2引数を省略して呼び出した場合、ランダムなsalt値を生成し、それを利用して一方向ハッシュ関数(どのアルゴリズムを使うかは設定による)を実行する。そして結果としてsalt値とハッシュ値を足した結果を出力する。試してみるとわかるが、第2引数を省略して実行したcrypt関数は呼び出すたびに違う結果(違うsalt値を使って計算した結果)を返す。

cryptを使った場合のパスワード認証コードはサンプルのように、cryptの第2引数にsalt値を含む認証コードを渡して実行した結果が、認証コードと合致するかどうかで行う。つまりcrypt関数を使うことで、同じパスワードを元にした場合でも、(salt値が違えば)認証用のコードはまったく別の内容となり、同じパスワードを使っているもの同士でもばれることはなくなる。

上記の説明ではそのロジックがうまく理解できないかもしれないが、このようなコードを使うことで、より安全なパスワード認証が実現できるものだと、パターンとして覚えておけばいいだろう。

『自分で作るblogツール(PHP編)』サポートエントリーに戻る

Published At2005-04-11 15:18Updated At2005-04-11 15:18