【PHP8.x】random_bytes()関数の使い方
random_bytes関数の使い方について、初心者にもわかりやすく解説します。
基本的な使い方
『random_bytes関数は、暗号学的に安全なランダムなバイト列を生成する関数です』この関数は、オペレーティングシステムが提供する信頼性の高い乱数ソースを利用して、予測することが極めて困難な質の高い乱数を生成します。そのため、セッションID、パスワードリセット用のトークン、APIキー、暗号化キーといった、セキュリティが重要となる場面で推測不可能な値を生成するのに最適です。従来のrand()やmt_rand()といった関数も乱数を生成しますが、これらは統計的な目的で設計されており予測可能であるため、セキュリティ関連の用途には絶対に使用してはいけません。random_bytes関数は、引数に生成したいバイトの長さを整数で指定すると、その長さのランダムなバイト列を文字列として返します。返される値はバイナリデータであり、必ずしも人間が読める文字で構成されているとは限りません。引数に0以下の整数を指定するなど、不正な値が渡された場合にはValueError例外が発生します。この関数は、現代のPHPアプリケーションにおいて、安全なランダムデータを生成するための標準的な方法です。
構文(syntax)
1$bytes = random_bytes(int $length);
引数(parameters)
int $length
- int $length: 生成するランダムなバイト列の長さをバイト単位で指定する整数
戻り値(return)
string
指定されたバイト数分の、暗号学的に安全なランダムなバイト列を文字列として返します。
サンプルコード
PHP random_bytes 代替関数で乱数生成する
1<?php 2 3/** 4 * OpenSSL拡張モジュールが利用できない環境で、`random_bytes`関数の代替として、 5 * `random_bytes`と同じように暗号論的に安全な擬似乱数バイト列を生成します。 6 * 7 * @param int $length 生成するバイト数 8 * @return string 生成されたランダムなバイト列、エラーが発生した場合はnull 9 */ 10function random_bytes_alternative(int $length): ?string 11{ 12 $result = ''; 13 try { 14 // `/dev/urandom` が利用可能であればそれを使用 15 if (is_readable('/dev/urandom')) { 16 $fp = fopen('/dev/urandom', 'rb'); 17 if ($fp) { 18 $result = fread($fp, $length); 19 fclose($fp); 20 } 21 } 22 23 // `/dev/urandom` が利用できない場合は `openssl_random_pseudo_bytes` を使用 24 if (empty($result)) { 25 $result = openssl_random_pseudo_bytes($length, $strong); 26 if (!$strong) { 27 // openssl_random_pseudo_bytes が暗号学的に安全な乱数を生成できなかった場合 28 throw new Exception("openssl_random_pseudo_bytes did not generate cryptographically strong random bytes."); 29 } 30 } 31 32 if (strlen($result) !== $length) { 33 throw new Exception("Could not gather sufficient entropy for requested number of bytes."); 34 } 35 36 return $result; 37 38 } catch (Exception $e) { 39 error_log("random_bytes_alternative failed: " . $e->getMessage()); 40 return null; 41 } 42} 43 44// 使用例 45$randomBytes = random_bytes_alternative(16); 46 47if ($randomBytes !== null) { 48 echo bin2hex($randomBytes) . PHP_EOL; // 16進数で表示 49} else { 50 echo "Failed to generate random bytes." . PHP_EOL; 51} 52
このサンプルコードは、PHPの組み込み関数 random_bytes が利用できない環境における代替手段として、暗号学的に安全な擬似乱数バイト列を生成する random_bytes_alternative 関数を定義しています。
random_bytes 関数は、指定されたバイト数のランダムな文字列を生成しますが、このサンプルコードでは、まず /dev/urandom という特殊なファイルからの読み込みを試みます。これは、Linux系システムで利用できる乱数生成器です。もし /dev/urandom が利用できない場合は、openssl_random_pseudo_bytes 関数を使用して乱数を生成します。
openssl_random_pseudo_bytes 関数は、OpenSSL拡張モジュールの一部であり、擬似乱数を生成します。この関数は、第2引数 $strong に真偽値を設定し、生成された乱数が暗号学的に安全かどうかを判断できます。安全でないと判断された場合は、例外をスローします。
random_bytes_alternative 関数は、引数 $length で生成するバイト数を指定します。戻り値は、生成されたランダムなバイト列を含む文字列です。エラーが発生した場合は null を返します。生成されたバイト列の長さが指定された $length と異なる場合も例外をスローします。
このサンプルコードの利用例では、16バイトのランダムなバイト列を生成し、bin2hex 関数を使って16進数形式で表示しています。乱数の生成に失敗した場合は、エラーメッセージを表示します。random_bytes 関数が利用できない環境でも、同等の安全性を目指した乱数生成処理を実装する際の参考になります。
random_bytes_alternative関数は、random_bytes関数の代替として、暗号学的に安全な乱数を生成することを目的としています。/dev/urandomまたはopenssl_random_pseudo_bytesを利用しますが、環境によっては期待通りの動作をしない可能性があります。openssl_random_pseudo_bytesで生成された乱数が、暗号学的に安全かどうかを$strong変数で確認することが重要です。生成に失敗した場合(nullが返された場合)のエラー処理を必ず実装してください。生成されたバイト数が要求された長さと一致するか確認し、不一致の場合は例外をスローすることで、不完全な乱数によるセキュリティリスクを回避できます。
PHP random_bytesで安全なバイト列を生成する
1<?php 2 3/** 4 * 指定された長さの暗号学的に安全なランダムなバイト列を生成するサンプル 5 * 6 * @param int $length 生成するバイト列の長さ 7 * @return string 生成されたランダムなバイト列 8 */ 9function generateRandomBytes(int $length): string 10{ 11 try { 12 $bytes = random_bytes($length); 13 return $bytes; 14 } catch (Exception $e) { 15 // 例外処理:ランダムなバイト列の生成に失敗した場合 16 error_log("ランダムなバイト列の生成に失敗しました: " . $e->getMessage()); 17 return ''; // または、エラー処理に応じた値を返す 18 } 19} 20 21// 使用例:16バイトのランダムなバイト列を生成する 22$randomBytes = generateRandomBytes(16); 23 24if ($randomBytes !== '') { 25 echo "生成されたランダムなバイト列 (16進数): " . bin2hex($randomBytes) . PHP_EOL; 26} else { 27 echo "ランダムなバイト列の生成に失敗しました。" . PHP_EOL; 28}
PHPのrandom_bytes関数は、指定された長さの暗号学的に安全なランダムなバイト列を生成するために使用されます。この関数は、パスワードの生成、セッションIDの作成、暗号化キーの生成など、セキュリティが重要な場面で利用されます。
引数$lengthは整数型で、生成するランダムなバイト列の長さをバイト単位で指定します。例えば、$lengthに16を指定すると、16バイトのランダムなバイト列が生成されます。
戻り値は文字列型で、生成されたランダムなバイト列が返されます。もしランダムなバイト列の生成に失敗した場合(例えば、システムが十分なエントロピーを持っていない場合)、例外がスローされる可能性があります。サンプルコードでは、この例外をtry-catchブロックで捕捉し、エラーログにメッセージを記録するようにしています。
サンプルコードでは、generateRandomBytesという関数を定義し、random_bytes関数を呼び出してランダムなバイト列を生成しています。生成されたバイト列は、bin2hex関数を使って16進数の文字列に変換され、画面に表示されます。bin2hex関数は、バイナリデータを16進数の文字列に変換する便利な関数です。生成に失敗した場合は、エラーメッセージが表示されます。このように、random_bytes関数と例外処理を組み合わせることで、安全で信頼性の高いランダムなバイト列を生成することができます。
random_bytes関数は、暗号学的に安全な乱数を生成する関数です。引数$lengthで生成するバイト数を指定します。生成に失敗した場合、例外が投げられる可能性があるため、try-catchブロックで囲み例外処理を行うのが安全です。生成されたバイト列はバイナリデータなので、bin2hex関数などで16進数に変換して表示すると扱いやすくなります。エラー処理では、詳細なエラー内容をログに出力すると、問題発生時の原因特定に役立ちます。生成された乱数は、パスワードの生成や暗号化キーの生成など、セキュリティが重要な場面で利用できます。
PHP random_bytes でランダムな16進数文字列を生成する
1<?php 2 3/** 4 * 指定された長さの暗号学的に安全なランダムなバイト列を生成し、 5 * それを16進数文字列に変換して返します。 6 * 7 * この関数は、セッションID、トークン、パスワードソルトなど、 8 * セキュリティが重要な場面でのランダムな値の生成に適しています。 9 * 10 * @param int $length 生成するランダムなバイト列の長さ(バイト単位)。 11 * @return string 生成されたランダムな16進数文字列。 12 * @throws Exception random_bytes() の実行中にエラーが発生した場合。 13 */ 14function generateRandomHex(int $length): string 15{ 16 // random_bytes() は、指定された長さの暗号学的に安全なバイト列を生成します。 17 // 例外が発生した場合、適切なエラー処理が必要です。 18 $bytes = random_bytes($length); 19 20 // bin2hex() は、バイナリ文字列(バイト列)を16進数表現の文字列に変換します。 21 // 例えば、1バイトが "A" (ASCII 65) の場合、"41" に変換されます。 22 // 結果の文字列の長さは、$length の2倍になります。 23 $hex = bin2hex($bytes); 24 25 return $hex; 26} 27 28// -------------------------------------------------------------------------- 29// サンプルコードの実行部分 30// この部分は、関数がどのように使用されるかを示すためのものです。 31// -------------------------------------------------------------------------- 32try { 33 // 目的のバイト長を指定します。 34 // 例えば、16バイトのランダムなデータを生成します。 35 // これは32文字の16進数文字列になります。 36 $desiredLength = 16; 37 $randomHexString = generateRandomHex($desiredLength); 38 39 echo "生成されたランダムな16進数文字列 (バイト長: " . $desiredLength . "):\n"; 40 echo $randomHexString . "\n"; 41 echo "文字列の長さ (16進数文字数): " . strlen($randomHexString) . "\n"; 42} catch (Exception $e) { 43 // random_bytes() が失敗した場合、ここでエラーを捕捉して処理します。 44 echo "エラーが発生しました: " . $e->getMessage() . "\n"; 45}
このサンプルコードは、セキュリティ上重要な場面で利用できる、暗号学的に安全なランダムな16進数文字列を生成する方法を示しています。
generateRandomHex関数は、指定された長さのランダムなバイト列を生成し、それを16進数文字列に変換して返します。まず、PHPの標準関数であるrandom_bytesを使用します。この関数は、引数$lengthで指定されたバイト数だけ、予測されにくい安全なバイト列(文字列型)を生成します。これは、セッションIDやパスワードのソルト、トークンなど、セキュリティが求められるランダムな値の生成に適しています。
次に、random_bytesによって生成されたバイト列は、bin2hex関数によって16進数表現の文字列に変換されます。bin2hexはバイナリデータを読みやすい16進数表記に変換する役割があり、1バイトが2文字の16進数に変換されるため、結果として得られる文字列の長さは元のバイト数の2倍になります。例えば、16バイトを指定すると32文字の16進数文字列が生成されます。
この一連の処理をgenerateRandomHex関数としてまとめることで、再利用しやすい形になっています。また、random_bytes関数は失敗した場合に例外を発生させる可能性があるため、サンプルコードの実行部分ではtry-catchブロックを用いて、エラーが発生した場合でも安全に処理できるよう配慮されています。
このサンプルコードは、暗号学的に安全なランダムなバイト列を生成し、それを16進数文字列に変換する安全な方法を示しています。特に注意すべき点は、random_bytes関数がセキュリティが重要な用途(セッションID、トークン、パスワードソルトなど)向けである点です。一般的なランダム関数とは異なり、予測困難な値を生成します。引数の$lengthは生成したい「バイト数」であり、bin2hexで16進数文字列に変換された後の文字数は、指定したバイト数の「2倍」になりますので混同しないようご注意ください。また、random_bytesはシステム資源の枯渇などで失敗する可能性があり、その場合は例外(Exception)をスローします。そのため、本コードのようにtry-catchブロックでエラーを適切に処理し、プログラムの安定性を確保することが非常に重要です。
PHP random_bytesでCSRFトークンを生成・検証する
1<?php 2 3/** 4 * CSRFトークンを安全に生成・検証するためのユーティリティクラス。 5 * 6 * このクラスは、暗号学的に安全な乱数生成器 `random_bytes` を使用して 7 * Cross-Site Request Forgery (CSRF) 攻撃を防ぐためのトークンを扱います。 8 * `random_bytes` を使用しない場合、予測可能なトークンが生成され、 9 * CSRF攻撃の脆弱性 (exploit) に繋がり、ユーザーのアクションが不正に実行される危険性があります。 10 */ 11class CsrfTokenManager 12{ 13 /** 14 * セッションにトークンを保存するためのキー。 15 */ 16 private const SESSION_TOKEN_KEY = 'csrf_token'; 17 18 /** 19 * 生成するトークンのバイト長。 20 * (hex文字列に変換するとこの値の2倍の長さになる) 21 */ 22 private const TOKEN_LENGTH_BYTES = 32; 23 24 /** 25 * CSRFトークンを生成し、セッションに保存して返します。 26 * 27 * @return string 生成されたCSRFトークン(hex形式の文字列)。 28 * @throws RuntimeException `random_bytes` が安全なトークンを生成できない場合。 29 */ 30 public function generateToken(): string 31 { 32 // セッションが開始されていない場合は開始 33 if (session_status() === PHP_SESSION_NONE) { 34 session_start(); 35 } 36 37 // random_bytesで暗号学的に安全なランダムバイト列を生成 38 // この関数を使用しないと予測可能なトークンになり、CSRF攻撃のexploitにつながります。 39 try { 40 $bytes = random_bytes(self::TOKEN_LENGTH_BYTES); 41 $token = bin2hex($bytes); // バイト列を16進数文字列に変換 42 } catch (Exception $e) { 43 // 例外発生時(例: 十分なエントロピーがない、OSの乱数生成器が利用できない) 44 error_log("Failed to generate CSRF token using random_bytes: " . $e->getMessage()); 45 throw new RuntimeException("Could not generate a secure CSRF token."); 46 } 47 48 // 生成したトークンをセッションに保存 49 $_SESSION[self::SESSION_TOKEN_KEY] = $token; 50 51 return $token; 52 } 53 54 /** 55 * 送信されたCSRFトークンを検証します。 56 * 検証成功後、セッションからトークンを削除し、一度きりの使用を強制します。 57 * 58 * @param string $submittedToken ユーザーから送信されたトークン。 59 * @return bool 検証に成功した場合はtrue、それ以外はfalse。 60 */ 61 public function verifyToken(string $submittedToken): bool 62 { 63 // セッションが開始されていない場合は開始 64 if (session_status() === PHP_SESSION_NONE) { 65 session_start(); 66 } 67 68 // セッションに保存されたトークンを取得 69 $storedToken = $_SESSION[self::SESSION_TOKEN_KEY] ?? null; 70 71 // トークンが存在し、かつ一致するかを検証 72 // hash_equalsはタイミング攻撃を防ぐために、常に文字列全体を比較します。 73 // 単純な '===' 演算子を使うと、比較が途中で打ち切られる可能性があり、 74 // 攻撃者が比較にかかる時間からトークンの一部を推測する手がかりを与えることがあります。 75 if ($storedToken && hash_equals($storedToken, $submittedToken)) { 76 // 検証成功後、トークンを無効化(一度きりの使用を推奨) 77 unset($_SESSION[self::SESSION_TOKEN_KEY]); 78 return true; 79 } 80 81 return false; 82 } 83} 84 85// --- サンプル利用シナリオ --- 86 87// CsrfTokenManagerインスタンスを作成 88$manager = new CsrfTokenManager(); 89 90echo "--- PHP random_bytes for CSRF Token Security Example ---\n\n"; 91echo "The random_bytes function is crucial for generating unpredictable tokens. "; 92echo "Without it, tokens could be guessed, leading to security exploits like CSRF attacks.\n\n"; 93 94// 1. フォーム表示時にCSRFトークンを生成 95// このトークンはHTMLフォームの隠しフィールドに埋め込まれることを想定しています。 96$csrfToken = $manager->generateToken(); 97echo "Generated CSRF Token (for form submission): " . $csrfToken . "\n"; 98echo "This token helps prevent Cross-Site Request Forgery (CSRF) attacks.\n\n"; 99 100// 2. シミュレーション: ユーザーがフォームを送信 101// 例えば、$_POST['csrf_token'] にトークンがセットされたと仮定します。 102$simulatedSubmittedToken = $csrfToken; // 正しいトークンを送信するケースをシミュレート 103 104echo "Simulating form submission with submitted token: " . $simulatedSubmittedToken . "\n"; 105 106// 3. フォーム送信時にCSRFトークンを検証 107if ($manager->verifyToken($simulatedSubmittedToken)) { 108 echo "Verification SUCCESS! The submitted token is legitimate. Proceed with the request.\n"; 109 // ここに、トークン検証が成功した場合に行うべき本来の処理を記述します。 110 // 例: データベースの更新、ユーザー設定の変更など 111} else { 112 echo "Verification FAILED! Possible CSRF attempt detected. Reject the request.\n"; 113 // 攻撃の可能性があるので、処理を中断しエラーを返すべきです。 114} 115 116// 注意: verifyToken はトークンを一度しか使用できないようにセッションから削除します。 117// そのため、同じトークンで再度検証しようとすると失敗します。 118echo "\nAttempting to verify the same token again (expected to fail, as it's for single use):\n"; 119if ($manager->verifyToken($simulatedSubmittedToken)) { 120 echo " Verification SUCCESS (unexpected)! This indicates a potential issue or misuse.\n"; 121} else { 122 echo " Verification FAILED (expected). The token was already consumed.\n"; 123} 124 125// 攻撃者が不正なトークンを送信した場合のシミュレーション 126echo "\nSimulating an attempt with a malicious/invalid token:\n"; 127// random_bytesを使って、ランダムだが不正な(セッションにない)トークンを生成します。 128try { 129 $maliciousToken = bin2hex(random_bytes(CsrfTokenManager::TOKEN_LENGTH_BYTES)); 130} catch (Exception $e) { 131 // エラー時のフォールバックとして固定文字列を使用(本番環境では適切ではない) 132 $maliciousToken = 'random_malicious_string_fallback'; 133} 134echo " Malicious token: " . $maliciousToken . "\n"; 135 136if ($manager->verifyToken($maliciousToken)) { 137 echo " Verification SUCCESS (unexpected)! This is a critical security flaw.\n"; 138} else { 139 echo " Verification FAILED (expected). Malicious token was rejected.\n"; 140} 141 142?>
PHPのrandom_bytes関数は、暗号学的に安全なランダムなバイト列を生成するために利用されます。引数$lengthには生成したいバイト列の長さを整数で指定し、戻り値として指定された長さのバイナリ文字列が返されます。この関数は予測不可能な値を生成できるため、パスワードのソルトや暗号鍵、そしてセキュリティトークンの生成など、特にセキュリティが求められる場面で非常に重要です。
このサンプルコードでは、random_bytes関数を利用して、ウェブアプリケーションの主要な脆弱性の一つであるCSRF (Cross-Site Request Forgery) 攻撃から保護するためのトークンを安全に生成・検証するCsrfTokenManagerクラスを実装しています。generateTokenメソッド内でrandom_bytes(32)を呼び出し、32バイトの暗号学的に安全なランダムなバイト列を取得し、それを16進数文字列に変換してCSRFトークンとして使用しています。
もしrandom_bytesを使用せず、予測可能な方法でトークンを生成すると、攻撃者がトークンを推測しやすくなり、その予測されたトークンを用いてユーザーの意図しない操作を強制するCSRF攻撃(exploit)が成功してしまう危険性があります。random_bytesを使うことで、トークンの予測を極めて困難にし、ウェブアプリケーションのセキュリティを大幅に向上させています。verifyTokenメソッドでは、セッションに保存されたトークンとユーザーから送信されたトークンを安全に比較し、トークンの一度限りの利用を強制しています。
random_bytes関数は、予測不可能な暗号学的に安全な値を生成するため、CSRFトークンなどセキュリティが重要な場面で必ず使用してください。これを使わないと、トークンが予測され、システムが攻撃される危険性があります。万が一、random_bytesで安全な値が生成できない場合に備え、必ず例外処理を実装してください。また、トークンを比較する際は、タイミング攻撃を防ぐためにhash_equals関数を使いましょう。生成したトークンは、一度使用したらセッションから削除し、使い捨てにすることで安全性を高めることが大切です。セッションの利用には、必ずsession_start()関数を呼び出してセッションを開始してください。