Edited at

パスワードをDBに保存する時の基礎の基礎的なこと

More than 3 years have passed since last update.


はじめに

あるところで軽く説明する必要ができたのでこの文書を書きます。


前提

PHPで簡単なシステムを作成している想定です。


大事なこと


DBの中に、パスワードを生テキストのまま保存してはいけません!!


  • なぜならDBのデータがなんらかの方法で盗まれてしまった場合に、パスワードが見た瞬間にわかってしまうからです。

  • パスワードをあるところから取得して、その内容がわかると、同じユーザが別のサービスで同じパスワードを使っていれば不正アクセスできます(最近チラホラ話題にもなりました)。


じゃぁどうしたらいいの?


  • パスワードはなんらかの方法で暗号化してからDBへ保存します。

  • また、パスワードを暗号化する際にSALTを利用することでそれぞれのユーザにとって別の暗号結果を作ることができます。


    • 佐藤さん(SALT:Stt41ou)で「mypassword」を暗号化した結果

    • 鈴木さん(SALT:6rkubel)で「mypassword」を暗号化した結果

    • 上記の2点の暗号化の結果が違う文字列になるということです。こうなっていれば暗号化ロジックの推測が困難になります。




暗号化したら、ログインの時どうやってチェックするの?


  • 同じ暗号化ロジックを使えば同じ結果が返ってくることを利用します。

  • ユーザが入力したパスワードを先に暗号化した方法と同じ方法(SALTも当然同一)で暗号化して、それからDB内で合致するデータを検索します。とにかくパスワードを平文で扱うことはありません。


SALTはどうやって用意するの?


  • 各ユーザごとに別々の値が作られるようにします。

  • SALTが何かの情報で変換して作られる(例えばユーザーレコードの作成日時)ようにしたり、ランダムなSALTを作成してユーザーレコードに一緒に保存するなどが有りえます(保存しないと実際のログインの時に同じ暗号化が使えません)。


例えばどんな風になるの?雰囲気を知りたい。

例えば以下のようにms2satoさんに対してmypasswordで保存します。

$user = 'ms2sato';

$password = 'mypassword';

$salt = createSalt(); //長い何かの文字列を作ります。
$encrypted_password = crypt($password, $salt); // 暗号化される(もっと強くしたければこれだけではダメですが、基本はcryptだと思います)

$insertSql = "insert into user(user_id, encrypted_password, salt) values (?, ?, ?)"
$stmt = $dbh->prepare($insertSql); //PDO利用していると思ってください。
$stmt->execute(array($user,$encrypted_password,$salt));

ログインでアクセスされたら下記のように同じ暗号化をしてからチェックします。

$user = 'ms2sato'; //WEBからPOSTされてきたという想定

$password = 'mypassword'; //WEBからPOSTされてきたという想定

$selectSql = "select * from user where user_id = ?";
$stmt = $dbh->prepare($selectSql); //PDO利用していると思ってください。
$stmt->execute(array($user));

while ($data = $stmt->fetch(PDO::FETCH_ASSOC)){
$salt = $data['salt'];
$encrypted_password = crypt($password, $salt); //保存時と同じ暗号化

// パスワードの一致を調べる
if($data['encrypted_password'] == $encrypted_password){
echo 'ようこそ!!';
} else {
echo 'ID・パスワードが不正です';
}
}