日記
おまけ: 認証状態を保存する方法Edit

認証処理の基本」では、どのようにすれば安全に認証処理を実装できるのか説明した。しかし、実際には認証処理を通ったあとに、その状態を持続する必要がある。ここでは認証の結果をどのようにして保存するか説明する。

認証状態を保存する方法としては、Cookieを利用する方法と、セッションを利用する方法の二つが一般的だ。セッションといっても、実際には内部でCookieを使っていたりする(使わない方法もある)のだが、ひとまず細かいことは気にしないでおこう。まずCookieを利用して認証状態を保存する方法を説明する。

Cookieを利用して認証状態を保存する一番シンプルな方法は、以下のようなコードとなる。

<?php

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

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

?>

2回目以降のアクセス時

<?php

if (isset($_COOKIE['userid'])) {
  echo
'認証OK!';
} else {
  echo
'認証できませんでした';
}

?>

認証を通らない限りは、CookieにユーザーIDは書き込まれないはずなので、CookieにユーザーIDがある→認証OKというロジックだ。ちなみにCookieの有効範囲(パスやドメイン、有効期間など)は環境に合わせて適切に(必要最低限の狭い範囲で有効になるように)設定して欲しい。

一見このような処理で問題ないように思うかもしれないが、実際にはこのような処理には問題がある。それは本書で紹介した格言「Webブラウザから渡された値は決して信用してはならない」に違反しているからだ。Cookieはブラウザが自由に設定できるパラメータであり、その内容を未検証のままに信用してはいけない。

上記のような仕様で認証を行った場合、ログインIDを知っていれば、誰でもその人の認証状態と同じ状態になることができる。要するに、誰でも簡単に騙りができてしまうのだ。

そこで、今度は簡単に騙りができないようにしてみよう。

フォームからのログイン時の処理

<?php

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

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

?>

2回目以降のアクセス時

<?php

$userid
= isset($_COOKIE['userid']) ? $_COOKIE['userid'] : '';
$pwd = isset($_COOKIE['pwd']) ? $_COOKIE['pwd'] : '';

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

?>

今度は、フォームからの認証に成功した場合、Cookieにそのとき投稿されたユーザーIDとパスワードを保存する。そして、以降のアクセスではCookieに記録されたユーザーID、パスワードを使って認証を行う。ログインIDは簡単に騙れても、パスワードまで騙ることは難しいので、これならば騙りは防げる。

しかし、このような方法もお薦めしない。というのは、Cookieは秘密の情報を保存する場所としては、非常に信頼おけない場所だからだ。

Cookieはその有効範囲にアクセスする際には、毎回HTTPヘッダとして送信されるため、Cookieにパスワードを持たせた場合、パスワードが何度もインターネット上をそのまま流れることになり、盗聴などで傍受される危険性が増す。また、CookieはJavaScriptからも簡単に読み取ることができるので、クロスサイトスクリプティングなどによってパスワードが盗まれる危険性が高い。

特に後者の危険性に対処するために、第三者がJavaScriptを記述できるようなWebアプリケーション(あるいはドメイン)上では、Cookieに秘密の情報を保存してはいけない。

そこで、たとえば以下のような形で、平文パスワードをCookieに記録せずに認証状態を維持してみよう。

フォームからのログイン時の処理

<?php

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

if (
check_login($userid, $pwd)) {
  
setcookie('userid', $userid);
  
$now = time();
  
setcookie('authtime', $now);
  
setcookie('authcode', crypt($userid.$now.'秘密の文字列'));
  echo
'認証OK!';
} else {
  echo
'認証できませんでした';
}

?>

2回目以降のアクセス時

<?php

$userid
= isset($_COOKIE['userid']) ? $_COOKIE['userid'] : '';
$authtime = isset($_COOKIE['authtime']) ? $_COOKIE['authtime'] : '';
$authcode= isset($_COOKIE['authcode']) ? $_COOKIE['authcode'] : '';

if (
crypt($userid.$authtime.'秘密の文字列', $authcode) == $authcode) {
  echo
'認証OK!';
} else {
  echo
'認証できませんでした';
}

?>

このやり方ならば、少なくともパスワードをそのままCookieに入れるよりは安全だ。認証コード生成ロジックがばれても、「秘密の文字列」をある程度の頻度で変えることによって、安全性を高めることができる。また、authtimeをチェックすることで古い認証を強制的に破棄するようなコードも簡単に書ける。

そのほかにも平文パスワードを保存せずに認証状態を維持する方法はいろいろあるので、自分なりの方法を考えてみるといいだろう。

さて、上記のやり方はあくまでもCookie自体に認証情報を含ませていたが、より安全な状態の保存方法としては通常セッション機能が用いられる。PHPではセッション機能が標準で用意されているので、手軽に利用できる。セッションを利用した場合のサンプルは以下のようになる。

<?php

session_start
();

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

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

?>

2回目以降のアクセス時

<?php

session_start
();

if (isset(
$_SESSION['userid'])) {
  echo
'認証OK!';
} else {
  echo
'認証できませんでした';
}

?>

Cookieを使ったサンプルの最初の例を、セッションを使うように変更しただけだ。セッションに保存されたデータはCookieと違ってクライアントサイドでは偽造できないので、基本的にそのまま信用して使用することができる。またセッションはCookieのように保存できる容量の制限がないので、ある程度大きなデータも保存しておくことができる。

と、とても便利なセッション機能だが、弱点もある。セッションは通常Cookieに保存されたセッションIDによって、クライアント(ブラウザ)とサーバーを結びつける。セッションIDは非常にランダム性が高く、それ単体では特に意味がないデータだが、それを盗まれることでそのセッションIDに結びつけられた、サーバー上のセッションデータが危険にさらされることになる。

つまりセッションを使った場合は、セッションIDの安全性にすべてがかかっていることになる。セッションにデータを保存する場合には、特にセッションIDが盗まれることがないように気をつける必要がある。セッションIDは設定によっては、QUERY_STRINGに付与する形で使用することもできる。その場合、HTTP_REFERERなどを通してセッションIDが漏洩する危険性があることにも注意しておこう。

さらに、セッションにはもう一つ危険なポイントがある。それは、共用サーバーなどでは、サーバー上に保存されるセッションデータが、他のユーザーによって盗み見られる危険性があることだ。共用サーバーを使う場合は、セッションデータの保存場所(session.save_path)などに気を遣いつつ、この点にも注意しておいた方がいい。

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

Published At2005-04-12 15:19Updated At2005-04-12 15:19