【PHP8.x】hash_pbkdf2関数の使い方

作成日: 更新日:

hash_pbkdf2関数は、PBKDF2 (Password-Based Key Derivation Function 2) アルゴリズムを使用して、パスワードから安全なキー(ハッシュ値)を導出する関数です。この関数は、主にユーザーが入力したパスワードをデータベースなどに安全に保存する目的で利用されます。元のパスワードそのものを保存するのではなく、この関数で生成されたハッシュ値を保存することで、万が一データベースが不正アクセスを受けた場合でも、攻撃者がパスワードを容易に特定することを困難にします。

PBKDF2アルゴリズムは、指定されたハッシュアルゴリズム(例:SHA256など)と、パスワード、ランダムなソルト値、そして重要な「イテレーション回数」というパラメータを用いてキーを生成します。イテレーション回数を高く設定するほど、ハッシュ計算にかかる時間が増加し、これにより総当たり攻撃(ブルートフォースアタック)や辞書攻撃といったパスワード推測手法に対するセキュリティ強度が高まります。

この関数は、使用するハッシュアルゴリズム、ハッシュ化するパスワード、セキュリティを高めるためのソルト、計算の繰り返し回数であるイテレーションカウント、そして生成するキーのバイト長、結果を生バイナリ形式で出力するかどうかを指定するフラグを引数として受け取ります。関数が成功すると、導出された安全なキーを表す文字列が返され、失敗した場合は false が返されます。セキュリティ確保のため、イテレーションカウントは常に適切な高い値に設定することが推奨されます。

基本的な使い方

構文(syntax)

hash_pbkdf2(string $algo, string $password, string $salt, int $iterations, int $length = 0, bool $binary = false): string

引数(parameters)

string $algo, string $password, string $salt, int $iterations, int $length = 0, bool $binary = false, array $options = []

  • string $algo: 使用するハッシュアルゴリズム名(例: 'sha256', 'sha512')
  • string $password: ハッシュ化する対象のパスワード文字列
  • string $salt: パスワードに付加するランダムな文字列(セキュリティ強化のため必須)
  • int $iterations: ハッシュ化を繰り返す回数(数値が大きいほど安全性が高まる)
  • int $length: 出力するハッシュ値のバイト長。0を指定すると、アルゴリズムのデフォルト長になる。
  • bool $binary: trueを指定するとバイナリ形式で、false(デフォルト)を指定すると16進数文字列で返す。
  • array $options: アルゴリズム固有のオプションを指定する連想配列(例: ['cost' => 10])

戻り値(return)

string|false

PBKDF2アルゴリズムを使用して生成されたパスワードベースのキー導出関数(PBKDF2)のハッシュ値を文字列で返します。指定されたパラメータが不正な場合、falseを返します。

サンプルコード

PHP hash_pbkdf2で鍵を導出し暗号化する

<?php

/**
 * hash_pbkdf2 を使用してパスワードから鍵を導出し、その鍵でデータを暗号化・復号化する例。
 * hash_pbkdf2 自体はデータを復号化する関数ではありませんが、
 * 暗号化・復号化のプロセスで必要となる「鍵」を、パスワードから安全に導出するために使用されます。
 * これにより、パスワードそのものを保存せず、代わりにハッシュ化された鍵を(適切に)使用できます。
 */
function demonstratePbkdf2KeyDerivationForEncryption() {
    // ユーザーのパスワード (実際にはユーザー入力から取得し、直接コードに書かない)
    $password = 'MySecretPassword123!';

    // 各パスワードハッシュに対して固有の、ランダムで十分に長いソルトを生成します。
    // ソルトは、パスワードハッシュと共に保存される必要があります。
    $salt = random_bytes(16); // 16バイト (128ビット) のランダムなソルト

    // PBKDF2の反復回数。高いほどブルートフォース攻撃に強くなりますが、処理時間も長くなります。
    // 実際のプロダクション環境では、より高い値 (例: 100,000以上) を使用することを推奨します。
    $iterations = 10000;

    // 導出する鍵の長さ (バイト単位)。AES-256暗号化に必要な32バイト (256ビット) を指定します。
    $keyLength = 32; // 32バイト = 256ビット

    // 鍵の導出に使用するハッシュアルゴリズム
    $algo = 'sha256';

    // hash_pbkdf2 を使用してパスワードとソルトから鍵を導出します。
    // 最後の引数 (binary) を true にすると、バイナリ形式の鍵が返されます。
    // これは暗号化関数に適しています。
    $derivedKey = hash_pbkdf2($algo, $password, $salt, $iterations, $keyLength, true);

    if ($derivedKey === false) {
        echo "エラー: hash_pbkdf2 による鍵の導出に失敗しました。\n";
        return;
    }

    echo "導出された鍵 (hex): " . bin2hex($derivedKey) . "\n";
    echo "鍵の長さ: " . strlen($derivedKey) . " バイト\n";
    echo "ソルト (hex): " . bin2hex($salt) . "\n";
    echo "反復回数: " . $iterations . "\n\n";

    // --- 導出された鍵を暗号化・復号化に使用するデモンストレーション ---

    $originalData = "これはPBKDF2で導出した鍵を使って暗号化される秘密のメッセージです。";
    $cipherAlgo = 'aes-256-cbc'; // 使用する対称暗号化アルゴリズム

    // IV (Initialization Vector) を生成します。
    // 各暗号化操作ごとに一意である必要があり、暗号文と一緒に保存または送信されます。
    $ivLength = openssl_cipher_iv_length($cipherAlgo);
    if ($ivLength === false) {
        echo "エラー: " . $cipherAlgo . " のIV長を取得できませんでした。\n";
        return;
    }
    $iv = random_bytes($ivLength);

    // データを暗号化します。
    // OPENSSL_RAW_DATA フラグは、生データとして暗号化することを指定します。
    $encryptedData = openssl_encrypt($originalData, $cipherAlgo, $derivedKey, OPENSSL_RAW_DATA, $iv);

    if ($encryptedData === false) {
        echo "エラー: データの暗号化に失敗しました。\n";
        return;
    }

    echo "元のデータ: " . $originalData . "\n";
    // 暗号化されたデータは通常、Base64などでエンコードして保存・転送します。
    echo "暗号化されたデータ (base64): " . base64_encode($encryptedData) . "\n";
    echo "IV (hex): " . bin2hex($iv) . "\n\n";

    // データを復号化します。
    // 復号化には、暗号化時と同じ鍵、IV、アルゴリズムが正確に必要です。
    $decryptedData = openssl_decrypt($encryptedData, $cipherAlgo, $derivedKey, OPENSSL_RAW_DATA, $iv);

    if ($decryptedData === false) {
        echo "エラー: データの復号化に失敗しました。\n";
        // デバッグ情報としてOpenSSLのエラーメッセージを出力することもできます
        // echo "OpenSSLエラー: " . openssl_error_string() . "\n";
        return;
    }

    echo "復号化されたデータ: " . $decryptedData . "\n";

    if ($originalData === $decryptedData) {
        echo "\n暗号化と復号化が成功しました。データは一致します。\n";
    } else {
        echo "\nエラー: 復号化されたデータが元のデータと一致しません。\n";
    }
}

// 関数を実行してデモンストレーションを開始
demonstratePbkdf2KeyDerivationForEncryption();

?>

PHPのhash_pbkdf2関数は、PBKDF2(Password-Based Key Derivation Function 2)アルゴリズムを用いて、ユーザーのパスワードから安全な暗号化鍵を導出するために利用されます。この関数自体はデータを直接暗号化したり復号化したりするものではありませんが、暗号化・復号化プロセスで必要となる「鍵」を、パスワードから安全に生成する役割を担います。これにより、パスワードそのものをシステムに保存するリスクを軽減し、セキュリティを高めることが可能です。

サンプルコードでは、ユーザーのパスワード($password)と一意のソルト($salt)、そして高い反復回数($iterations)を指定して、hash_pbkdf2により特定の長さ($keyLength)の鍵を導出しています。引数$algoは鍵導出に用いるハッシュアルゴリズムを、$binaryは戻り値がバイナリ形式か否かを指定します。導出された鍵($derivedKey)は、openssl_encryptopenssl_decrypt関数で実際にデータを暗号化・復号化するために使用される具体的な例が示されており、導出に失敗した場合はfalseが戻り値となります。このように、hash_pbkdf2はパスワードから堅牢な鍵を生み出すための重要な手段です。

hash_pbkdf2は、パスワードを直接復号化するものではなく、データを暗号化・復号化するための安全な「鍵」をパスワードから導出する関数です。パスワードはコードに直接記述せず、安全に取得するよう注意してください。ソルトは各パスワードごとにランダムに生成し、導出された鍵と共に保存する必要があります。反復回数(iterations)はセキュリティ強度に大きく影響するため、実運用では十分高い値を設定してください。導出した鍵をopenssl_encrypt等で暗号化に利用する際は、暗号化ごとに一意のIV(初期化ベクトル)を生成し、暗号文とセットで管理することが重要です。また、関数が失敗するとfalseを返すため、必ず戻り値をチェックしエラーハンドリングを行ってください。

PHP hash_pbkdf2 でパスワードを安全にハッシュ化する

<?php

/**
 * hash_pbkdf2 関数の使用例です。
 * PBKDF2 (Password-Based Key Derivation Function 2) は、パスワードのような機密情報を
 * 安全にハッシュ化(キーを導出)するために設計された関数です。
 * ブルートフォース攻撃に対する耐性を高めるために、計算コストを意図的に高くすることができます。
 *
 * システムエンジニアを目指す初心者の方へ:
 * この関数はパスワードの安全な保存と検証に役立ちますが、現代のPHPでは
 * password_hash() と password_verify() 関数を使うことが推奨されています。
 * それらはPBKDF2などの適切なアルゴリズムを自動的に選択し、ソルトの生成や
 * 反復回数の管理をより簡単に、かつ安全に行えるように設計されています。
 * ただし、hash_pbkdf2 の理解は、パスワードハッシュの基本的な仕組みを学ぶ上で非常に重要です。
 */

// --- 1. パスワードハッシュのための設定 ---
$algo = 'sha256';     // 使用するハッシュアルゴリズム。'sha256' や 'sha512' など。
$iterations = 10000;   // 反復回数。この値を大きくすると、ハッシュ化に時間がかかり、
                       // ブルートフォース攻撃に対する耐性が向上します。
                       // 通常、10,000 から 100,000 の範囲が推奨されます。
$length = 0;           // 出力するハッシュのバイト長。0 を指定すると、
                       // アルゴリズムのデフォルトの長さ(例: sha256は32バイト)になります。
$binary = false;       // true の場合、生バイナリデータが返されます。
                       // false の場合、16進数文字列が返されます。
                       // データベースに保存する場合は16進数文字列(false)が一般的です。

// --- 2. 新しいパスワードをハッシュ化して保存する例 (ユーザー登録時) ---

$userPassword = 'mySecretPassword123!'; // ユーザーが入力したパスワード

// パスワードごとにユニークなソルトを生成します。
// ソルトは、レインボーテーブル攻撃を防ぎ、同じパスワードでも異なるハッシュを生成するために重要です。
// random_bytes() で暗号学的に安全なランダムバイト列を生成し、bin2hex() で16進数文字列に変換します。
$salt = bin2hex(random_bytes(16)); // 16バイトのソルト(32文字の16進数文字列)

echo "--- 新しいパスワードのハッシュ化(登録時) ---\n";
echo "元のパスワード: " . $userPassword . "\n";
echo "生成されたソルト: " . $salt . "\n";

// hash_pbkdf2 を使ってパスワードをハッシュ化
$hashedPassword = hash_pbkdf2($algo, $userPassword, $salt, $iterations, $length, $binary);

if ($hashedPassword === false) {
    echo "エラー: パスワードのハッシュ化に失敗しました。\n";
    exit(1);
}

echo "ハッシュ化されたパスワード: " . $hashedPassword . "\n";
echo "--- 注意: データベースには、このソルトとハッシュ化されたパスワードの両方を保存してください。---\n\n";


// --- 3. ログイン時にパスワードを検証する例 ---

// データベースから取得したと仮定するソルトとハッシュ化されたパスワード
// (ここでは、上記で生成した値を使用)
$storedSalt = $salt;
$storedHashedPassword = $hashedPassword;

$enteredPassword = 'mySecretPassword123!'; // ユーザーがログイン時に入力したパスワード
$wrongPassword = 'notTheRightPassword!';   // 間違ったパスワードの例

echo "--- 入力されたパスワードの検証(ログイン時) ---\n";
echo "入力されたパスワード (正しい): " . $enteredPassword . "\n";
echo "入力されたパスワード (間違い): " . $wrongPassword . "\n";
echo "保存されているソルト: " . $storedSalt . "\n";
echo "保存されているハッシュ化パスワード: " . $storedHashedPassword . "\n\n";

// ユーザーが入力したパスワードを、保存されているソルトと全く同じパラメータで再度ハッシュ化します。
$enteredPasswordHash = hash_pbkdf2($algo, $enteredPassword, $storedSalt, $iterations, $length, $binary);

if ($enteredPasswordHash === false) {
    echo "エラー: パスワードの検証中にハッシュ化に失敗しました。\n";
    exit(1);
}

// 新しく生成されたハッシュと、データベースに保存されているハッシュを比較します。
// hash_equals() は、タイミング攻撃を防ぐために一定時間で文字列を比較する安全な関数です。
if (hash_equals($storedHashedPassword, $enteredPasswordHash)) {
    echo "検証結果 (正しいパスワード): 成功!パスワードが一致しました。\n";
} else {
    echo "検証結果 (正しいパスワード): 失敗!パスワードが一致しませんでした。\n";
}

// 間違ったパスワードでの検証例
$wrongPasswordHash = hash_pbkdf2($algo, $wrongPassword, $storedSalt, $iterations, $length, $binary);
if ($wrongPasswordHash === false) {
    echo "エラー: 間違ったパスワードの検証中にハッシュ化に失敗しました。\n";
    exit(1);
}

if (hash_equals($storedHashedPassword, $wrongPasswordHash)) {
    echo "検証結果 (間違ったパスワード): 失敗!(これは発生すべきではありません)\n";
} else {
    echo "検証結果 (間違ったパスワード): 成功!パスワードは一致しませんでした。(期待通り)\n";
}

?>

PHP 8.4で利用できるhash_pbkdf2関数は、パスワードのような機密情報を安全にハッシュ化(キーを導出)するために設計されています。この関数はPBKDF2アルゴリズムに基づき、反復回数$iterationsを増やすことで計算コストを高め、ブルートフォース攻撃に対する耐性を向上させることができます。システムエンジニアを目指す初心者の方へ、現代のPHPではpassword_hash()password_verify()関数が推奨されますが、hash_pbkdf2の理解はパスワードハッシュの基本的な仕組みを学ぶ上で重要です。

引数には、使用するハッシュアルゴリズムを示す$algo、ハッシュ化したい元の$password、セキュリティ強化のためにパスワードごとにユニークに生成する$salt、そしてセキュリティ強度を決める$iterations(反復回数)などを指定します。$lengthで出力するハッシュのバイト長を、$binaryで出力形式(生バイナリか16進数文字列)を決定します。処理が成功するとハッシュ化された文字列(またはバイナリデータ)が返され、失敗した場合はfalseが返ります。

サンプルコードでは、ユーザー登録時にパスワードをハッシュ化し、ログイン時にはユーザーが入力したパスワードを、保存されているソルトと全く同じパラメータで再度ハッシュ化し、保存済みのハッシュとhash_equals()関数で安全に比較して検証する流れが示されています。この際、データベースにはハッシュ化されたパスワードと、その生成に使われたソルトの両方を保存する必要があります。

hash_pbkdf2関数はパスワードハッシュの基礎を理解するのに有用ですが、現代PHPではpassword_hash()password_verify()の利用が推奨されます。これらはソルトの自動生成や適切な反復回数の管理により、より簡単に安全なパスワード処理を実現します。hash_pbkdf2を使う際は、必ずrandom_bytes()でパスワードごとにユニークなソルトを生成し、ハッシュ値と共にデータベースに保存してください。iterationsはブルートフォース攻撃への耐性を高めるため、10,000回以上に設定が必要です。パスワードの検証時は、タイミング攻撃防止のためhash_equals()関数を使って比較し、関数が失敗した場合はfalseを返すため戻り値の確認も忘れないでください。

【PHP8.x】hash_pbkdf2関数の使い方 | いっしー@Webエンジニア