解 説

パスワードの保存

なんらかの認証を行う場合、ユーザーにパスワードを入力してもらい、その値とあらかじめ用意していた正解のパスワードと比較をして正しければ認証は成功しますし、正しくなければ認証に失敗します。
ここで問題がおこります。安全に正解のパスワードを保管するにはどうすればよいかということです。
パスワードの正解をドキュメントルート以外に置くこともひとつですが、平文のままでは怖いですね。
そのため、一般的にはハッシュ関数で暗号化してデータベースへ保存する方法が取られています。

例えば、ある会員制のサイトでパスワードの入力でサイトに入れる仕組みだとします。
会員がパスワードを作成するとハッシュ化されてデータベースなどに保存されます。
次に認証の際に会員がパスワードを入力するとそれは再びハッシュ化され最初に作成したパスワードをハッシュ化したデータと照合して認証する仕組みです。

ハッシュ関数とは

ハッシュ化とは、あるデータから乱数を生成するものです。この関数の特徴は任意の値からつねに同じハッシュ化された値を出力し、ハッシュ化した値からもともとのデータを導き出すことは不可能な仕組みです。ハッシュ関数は、ユーザー認証などに利用されます。
そもそもハッシュとは細かく切り刻むとか、めちゃくちゃにするとかの意味があります。たとえば、ハッシュドビーフやハッシュドポテトなど。。。

ハッシュ関数として知られるもので、MD5 や SHA1 といったハッシュアルゴリズムは、今では出力をブルートフォース(力ずく)で調べて元の入力を得ることができます。そのため、現在ではセキュリティ上これらの使用は避けるべきです。

けれども、ハッシュ化の仕組みを知っておくことは有益です。

ハッシュ関数を使用してハッシュ化

MD5、SHA1、SHA256を使用してパスワード「apple」をそれぞれの形式でハッシュ化した例

<?php
// 更新用パスワードを指定する
$password = "apple";
$hash1=sha1($password);
$hash2=md5($password);
$hash3=hash('sha256', $password);
echo 'sha1:'.$hash1.'<br>';
echo 'md5:'.$hash2.'<br>';
echo 'sha256:'.$hash3.'<br>';
?>

次にsha1を使用してハッシュ化したパスワードを使用した認証の例
念のため、パスワード:appleと表示させていますが、テスト環境のためです。実際は削除します。

<?php
// 更新用パスワードを指定する
$master_password = "d0be2dc421be4fcd0172e5afceea3970e2f3d940";
// 投稿があるか?
if (isset($_POST["pass"])) {
    // パスワードが合わない場合は保存しない
    $pass = isset($_POST["pass"]) ? $_POST["pass"] : "";
    if (sha1($pass) !== $master_password) {
        echo "パスワードが違います!!";
        exit;
    }  
    echo "ようこそ!!";
} else {
    // 投稿フォームの表示
    $self = $_SERVER["SCRIPT_NAME"];
    echo <<< __FORM__
     <form method="POST" action="$self">
     パスワード:apple
     <input type="password" name="pass" />
     <input type="submit" value="記録" />
     </form>
__FORM__;
}
?>

ハッシュ処理の際のソルトについて

ソルトとは、ハッシュ処理の際に追加するデータのことです。 事前に計算済みのハッシュとその元入力の対応表 (レインボーテーブル) で出力を解析される可能性を減らすために利用します。
ソルトとはちょっとした追加データのことです。 これを付けるだけで、ハッシュをクラックするのが難しくなるというものです。

ソルトを付けてハッシュ化する例

<?php
// 更新用パスワードを指定する
$password = "apple";
$salt="sderfttyhuji67h5k";
$pass_with_salt=$password.$salt;
$hash=hash("sha512",$pass_with_salt);
echo $hash;
?>

Salt付き認証用サンプル

<?php
// 更新用パスワードを指定する
$master_password = "8c4f0ef490ac16295b6c57274bafe8eea3e0d1ecc75e811e54d17a87cc79f44a63460c0944ad1a6c3c88dfb4600b61d4139931d67ad2afb84c9d77f50367f2b2";
$salt="sderfttyhuji67h5k";
// 投稿があるか?
if (isset($_POST["pass"])) {
    // パスワードが合わない場合は保存しない
    $pass = isset($_POST["pass"]) ? $_POST["pass"] : "";
	$pass_with_salt=$pass.$salt;
	$hash=hash("sha512",$pass_with_salt);
    if ($hash !== $master_password) {
        echo "パスワードが違います!!";
        exit;
    }  
    echo "ようこそ!!";
} else {
    // 投稿フォームの表示
    $self = $_SERVER["SCRIPT_NAME"];
    echo <<< __FORM__
<form method="POST" action="$self">
パスワード:apple
<input type="password" name="pass" />
<input type="submit" value="記録" />
</form>
__FORM__;
}
?>

password_hash() について

セキュリティを考慮するとPHP5.5.0 から導入されたpassword_hash() を使用します。
PHP5.5に対応したサーバーは現時点少ないかもしれません。XAMPPなどで試してみてください。

これにはソルトの仕組みも導入されています。
password_hash() の例

<?php
echo password_hash("rasmuslerdorf", PASSWORD_DEFAULT)."\n";
?>

この例の出力は、 以下のようになります。
この値は毎回変わることに注目してください。
[text]
$2y$10$exfiMppKFNz7PViWi7kP/epSb3navOezZXbDZkadfWH67w8FexyjW
[/text]
認証は次のように行います。
<?php
// 投稿があるか?
if (isset($_POST["pass"])) {
    $pass = isset($_POST["pass"]) ? $_POST["pass"] : "";
      if (!password_verify($pass, '$2y$10$exfiMppKFNz7PViWi7kP/epSb3navOezZXbDZkadfWH67w8FexyjW')) {
      echo "パスワードが違います!!";
        exit;
	  }
		echo "ようこそ!!";
} else {
    // 投稿フォームの表示
    $self = $_SERVER["SCRIPT_NAME"];
    echo <<< __FORM__
     <form method="POST" action="$self">
     パスワード:apple
     <input type="password" name="pass" />
     <input type="submit" value="記録" />
     </form>
__FORM__;
}
?>