読者です 読者をやめる 読者になる 読者になる

WebEngine

web、プログラミング関係について書いていきます。あなたの優しい眼差しがブログの成長につながりますのでどうぞよろしく。

パスワードをデータベースに保存するときは、一緒に塩を入れよう

セキュアに保存する -> 漏れた場合を想定する

データベース(以下DB)にパスワードを保存するとき、そのまま (平文で)保存してはいけません。データが漏れた場合に、パスワードが 見た瞬間わかってしまうからです。
パスワードを保存する場合、暗号化してからDBへインサートします。

実装例

手法は多種多様ですが、ここではSALTを使って暗号化 してみたいと思います。PHP + MySQLでの想定です。(ちなみにPDO)
SALTはランダムな文字列で、各ユーザごとにそれぞれ異なる ものを用意します。

<?php

/* 暗号化関数 */
function saltAngo($password, $salt) {
    $saltPassword = crypt($password, $salt);
    return $saltPassword;
}

/* ランダムな文字列を生成 */
function mkSalt($length = 8) {
    return substr(str_shuffle(str_repeat('0123456789abcdefghijklmnopqrstuvwxyz', $length)), 0, $length);
}


<?php
/* 上記ファイルの関数をrequire_onceなどで使えるようになっているとします。 */

$user = 'hoge';
$password = 'hogehoge';

$stmt = $pdo->prepare('INSERT INTO userdata SET user = ?, password = ?, salt = ?');

// トランザクション開始
$pdo->beginTransaction();

try {
    $stmt->bindValue(1, $user);
    $salt = mkSalt(); // ランダムな文字列作成
    // パスワードはsaltAngo関数で暗号化
    $password = saltAngo($password, $salt);
    $stmt->bindValue(2, $password);
    $stmt->bindValue(3, $salt); // 使ったSALTもDBへ保存
    $stmt->execute();

    // コミット 
    $pdo->commit();
} catch (PDOException $e) {
    // エラーでロールバック 
    $pdo->rollBack();
    throw $e;
}


ログイン時は、DBに保存された同じSALTを使います。同じ暗号化のロジックを使用すれば、同じ結果が 戻ってくることを利用するのです。

<?php
/* 実際はちゃんと無毒化しましょう */
$user = $_POST['user'];
$password = $_POST['password'];

$stmt = $pdo->prepare('SELECT * FROM userdata WHERE user = ?');
$stmt->bindValue(1, $user);
$stmt->execute();

$row = $stmt->fetch(PDO::FETCH_ASSOC);

if (!empty($row)) {
    $salt = $row['salt'];
    $password = crypt($password, $salt); // 保存時と同じSALTで復号化

    // パスワードが一致しているか
    if ($row['password'] === $password) {
        echo 'ログイン完了';
    } else {
        echo 'ユーザ名かパスワードが違います';
    }
} else {
    echo 'そのようなユーザは登録されていません';
}


ここでは説明を簡単にするため、弱い暗号化で済ませています。実際には、もっと 強いロジックを考えましょう。