勉強記録1-6で、ユーザー認証システムは概ね、かたちになりました。
(ユーザー登録からログイン、ログアウトまで)
ここで一度、これまでのロジックを見直し、セキュリティ対策を見直します。
- PEAR::DBでデータ登録する際のプレースホルダ利用
- セッションIDの再生成(session_regenerate_id)
- hidden値の書き換え
- リンク元のURL照合
今回は上記のうち、1,2について考えます。
1. PEAR::DBでプレースホルダ利用
勉強記録3で、ユーザーデータ、Emailデータの重複確認のために、PEAR::DBを利用して、マッチングを行いました。
このコードの中で、WHERE後の変数取り扱いが、セキュリティホールになりうることに気が付きました。
($usernameなどに悪意あるデータを入れられる可能性があり、かつエスケープ処理していない)
修正前のマッチング処理
/* 入力されたユーザーIDとパスワードを変数に取得 */ $username = $_POST["username"]; $email = $_POST["email"]; /* PEAR::DBを利用してユーザー名とメールアドレスの有無を取得 すでに存在する場合、各変数にユーザーId、メールアドレスのみの配列が生成される require_once("DB.php"); $dsn = "mysqli://pear:peartest@localhost/pear"; $myDB = DB::connect($dsn); $user_exist = $myDB -> getRow("SELECT username FROM auth WHERE username='$username' "); $email_exist = $myDB -> getRow("SELECT email FROM auth WHERE email='$email' ");
この箇所を、PEAR::DBのプレースホルダを利用して対応します。
修正後のマッチング処理
/* 入力されたユーザーIDとパスワードを変数に取得 */ $username = $_POST["username"]; $email = $_POST["email"]; /* PEAR::DBを利用してユーザー名とメールアドレスの有無を取得 すでに存在する場合、各変数にユーザーId、メールアドレスのみの配列が生成される require_once("DB.php"); $dsn = "mysqli://pear:peartest@localhost/pear"; $myDB = DB::connect($dsn); $user_exist = $myDB -> getRow("SELECT username FROM auth WHERE username=? ", array($username)); $email_exist = $myDB -> getRow("SELECT email FROM auth WHERE email=? “, array($email));
プレースホルダについては、書籍「PEAR入門」の02.01章にも詳しく書かれておりました。
最初に、この書籍を見たときには
「プレースホルダってなんだろう?」
と思っておりました(恥)。
自分で書いたコードのセキュリティを確認して、プレースホルダを利用して書き直すと、その重要性が改めてわかりますね。
プレースホルダについては、いつもお世話になっているMTハッカー・ToI企画の天野さんからご指摘いただきました。天野さん、いつもありがとうございます!
オンラインでは、PHPBookさんの「クエリの実行」ページにも、詳しく書かれております。
2. セッションIDの再生成(session_regenerate_id)
今回のユーザー登録ロジックでは、単純にsession_start()関数を利用してセッションを生成しておりました。
ここで、セッションIDの値が何らかの原因で判明した場合、セッションハイジャック攻撃の危険性が発生します。イメージとしてはこんな感じでしょうか。
セッションIDの固定化によるハイジャック攻撃を防ぐために、セッションIDの値を都度書き換える「sesshion_regenerate_id()関数」を利用して、セッションIDの変更を実行することにします。
入力フォームでデータを入力後、確認画面に移動する際に、セッションIDの変更を試みます。
以下「勉強記録6」で作成したロジックの一部と、修正後のロジックになります。
コード作成には、書籍「PHP逆引きレシピ」および「PHPプロ!」さんの記事を参考にさせていただきました。
修正前のregistConfirm.php(冒頭部)
<?php session_start(); ?>
修正後のregistConfirm.php(冒頭部)
<?php session_start(); /* 現在のセッションIDを変数に格納 */ $old_id = session_id(); /* session_regenerate_id関数で、セッションIDを変更 */ session_regenerate_id(TRUE); /* PHPのバージョンが5.1.0未満の場合は、 前セッションIDファイルを消去する */ if(version_compare(PHP_VERSION, '5.1.0', '<')) { unlink(session_save_path() . '/sess_' . $old_session_id); } ?>
次回はセキュリティ対策の続き
次回は
- hidden値の書き換え
- リンク元のURL照合
について考えてみたいと思います。
usualoma
こんにちは、天野です。
全く検証していないので、雑談としての話です。
都度セッションIDを変更するようになっていると、
ユーザーが何かの拍子にダブルクリックした際に、
サーバーに続けて2回のアクセスが発生し、
・2回目のアクセスは以前のIDを送っているので不正とみなされる
・ユーザーはもとのセッションに戻ることができない
という状態になってしまうことはないでしょうか。
にっくからusualomaへの返信
天野さん
にっくです。いつもありがとうございます。
なるほど、ダブルクリックの場合、たしかにその危険性はあるかも・・・。
あとでテスト環境でやってみます。
気がついた点があったら、コメントもしくは新エントリーで報告させていただきます。