こんにちは!今回はPHPのパスワードハッシュ関連関数の一つ、「password_algos」について詳しく解説していきます。
password_algos関数とは?
password_algosは、現在のPHP環境で利用可能なパスワードハッシュアルゴリズムの一覧を取得する関数です。PHP 7.4.0で追加された比較的新しい関数で、セキュアなパスワード管理を実装する際に役立ちます。
基本的な使い方
構文
password_algos(): array
パラメータ
なし(引数を取りません)
戻り値
- array: 利用可能なパスワードハッシュアルゴリズムのID配列
基本的な使用例
<?php
// 利用可能なアルゴリズムを取得
$algorithms = password_algos();
print_r($algorithms);
/*
出力例:
Array
(
[0] => 2y
[1] => argon2i
[2] => argon2id
)
*/
// 人間が読みやすい形式で表示
foreach ($algorithms as $algo) {
echo "利用可能: " . $algo . "\n";
}
?>
パスワードハッシュアルゴリズムの種類
主要なアルゴリズム
| アルゴリズムID | 定数 | 説明 | 推奨度 |
|---|---|---|---|
| 2y | PASSWORD_BCRYPT | BCrypt (デフォルト) | ★★★★☆ |
| argon2i | PASSWORD_ARGON2I | Argon2i | ★★★★★ |
| argon2id | PASSWORD_ARGON2ID | Argon2id (推奨) | ★★★★★ |
アルゴリズムの詳細
<?php
// アルゴリズム定数と文字列IDの対応
$algoMap = [
PASSWORD_BCRYPT => '2y',
PASSWORD_ARGON2I => 'argon2i',
PASSWORD_ARGON2ID => 'argon2id'
];
foreach ($algoMap as $constant => $id) {
echo "{$id}: ";
echo in_array($id, password_algos()) ? "利用可能" : "利用不可";
echo "\n";
}
?>
実践的な使用例
例1: 環境チェック機能の実装
<?php
class PasswordSecurityChecker {
/**
* パスワードハッシュ環境をチェック
*/
public static function checkEnvironment() {
$availableAlgos = password_algos();
$report = [
'php_version' => PHP_VERSION,
'available_algorithms' => $availableAlgos,
'recommendations' => []
];
// Argon2idの確認
if (in_array('argon2id', $availableAlgos)) {
$report['recommendations'][] = "✓ Argon2id利用可能(最も推奨)";
$report['recommended_algo'] = PASSWORD_ARGON2ID;
} elseif (in_array('argon2i', $availableAlgos)) {
$report['recommendations'][] = "⚠ Argon2i利用可能(推奨)";
$report['recommended_algo'] = PASSWORD_ARGON2I;
} else {
$report['recommendations'][] = "! BCryptのみ利用可能";
$report['recommended_algo'] = PASSWORD_BCRYPT;
}
// BCryptの確認
if (in_array('2y', $availableAlgos)) {
$report['recommendations'][] = "✓ BCrypt利用可能(後方互換性)";
}
return $report;
}
/**
* レポートを表示
*/
public static function displayReport() {
$report = self::checkEnvironment();
echo "=== パスワードセキュリティ環境レポート ===\n\n";
echo "PHPバージョン: {$report['php_version']}\n\n";
echo "利用可能なアルゴリズム:\n";
foreach ($report['available_algorithms'] as $algo) {
echo " - {$algo}\n";
}
echo "\n推奨事項:\n";
foreach ($report['recommendations'] as $rec) {
echo " {$rec}\n";
}
echo "\n推奨アルゴリズム定数: ";
echo self::getAlgorithmName($report['recommended_algo']);
echo "\n";
}
/**
* アルゴリズム定数から名前を取得
*/
private static function getAlgorithmName($algo) {
$names = [
PASSWORD_BCRYPT => 'PASSWORD_BCRYPT',
PASSWORD_ARGON2I => 'PASSWORD_ARGON2I',
PASSWORD_ARGON2ID => 'PASSWORD_ARGON2ID'
];
return $names[$algo] ?? 'UNKNOWN';
}
}
// 使用例
PasswordSecurityChecker::displayReport();
?>
例2: 最適なアルゴリズムの自動選択
<?php
class PasswordManager {
/**
* 環境に応じた最適なアルゴリズムを選択
*/
public static function getBestAlgorithm() {
$available = password_algos();
// 優先順位: Argon2id > Argon2i > BCrypt
if (in_array('argon2id', $available)) {
return PASSWORD_ARGON2ID;
} elseif (in_array('argon2i', $available)) {
return PASSWORD_ARGON2I;
} else {
return PASSWORD_BCRYPT;
}
}
/**
* パスワードをハッシュ化
*/
public static function hashPassword($password) {
$algo = self::getBestAlgorithm();
$options = [];
if ($algo === PASSWORD_ARGON2ID || $algo === PASSWORD_ARGON2I) {
$options = [
'memory_cost' => 65536, // 64MB
'time_cost' => 4,
'threads' => 2
];
} elseif ($algo === PASSWORD_BCRYPT) {
$options = [
'cost' => 12
];
}
return password_hash($password, $algo, $options);
}
/**
* 使用されているアルゴリズムを確認
*/
public static function getHashAlgorithm($hash) {
$info = password_get_info($hash);
return $info['algoName'];
}
}
// 使用例
$password = "my_secure_password123!";
$hash = PasswordManager::hashPassword($password);
echo "使用アルゴリズム: " . PasswordManager::getHashAlgorithm($hash) . "\n";
echo "ハッシュ値: " . $hash . "\n";
?>
例3: アルゴリズムの互換性チェック
<?php
class AlgorithmCompatibilityChecker {
/**
* 特定のアルゴリズムが利用可能かチェック
*/
public static function isAlgorithmAvailable($algorithmId) {
return in_array($algorithmId, password_algos());
}
/**
* 複数のアルゴリズムをチェック
*/
public static function checkMultipleAlgorithms($algorithms) {
$results = [];
foreach ($algorithms as $name => $id) {
$results[$name] = self::isAlgorithmAvailable($id);
}
return $results;
}
/**
* 互換性レポートを生成
*/
public static function generateCompatibilityReport() {
$algorithms = [
'BCrypt' => '2y',
'Argon2i' => 'argon2i',
'Argon2id' => 'argon2id'
];
$results = self::checkMultipleAlgorithms($algorithms);
echo "=== アルゴリズム互換性レポート ===\n\n";
foreach ($results as $name => $available) {
$status = $available ? "✓ 利用可能" : "✗ 利用不可";
echo "{$name}: {$status}\n";
}
// 推奨設定の提案
echo "\n=== 推奨設定 ===\n";
if ($results['Argon2id']) {
echo "推奨: PASSWORD_ARGON2ID を使用してください\n";
echo "理由: 最新かつ最も安全なアルゴリズムです\n";
} elseif ($results['Argon2i']) {
echo "推奨: PASSWORD_ARGON2I を使用してください\n";
echo "理由: Argon2idが利用できない場合の次善策です\n";
} else {
echo "推奨: PASSWORD_BCRYPT を使用してください\n";
echo "理由: 他のアルゴリズムが利用できません\n";
echo "注意: PHP 7.4以降へのアップグレードを推奨します\n";
}
}
}
// 使用例
AlgorithmCompatibilityChecker::generateCompatibilityReport();
// 個別チェック
if (AlgorithmCompatibilityChecker::isAlgorithmAvailable('argon2id')) {
echo "\nArgon2idを使用できます!\n";
}
?>
例4: マイグレーション計画の作成
<?php
class PasswordMigrationPlanner {
/**
* 既存ハッシュのアルゴリズムを分析
*/
public static function analyzeExistingHashes($hashes) {
$analysis = [
'total' => count($hashes),
'algorithms' => [],
'needs_rehash' => 0
];
$bestAlgo = self::getBestAvailableAlgorithm();
foreach ($hashes as $hash) {
$info = password_get_info($hash);
$algo = $info['algoName'];
// アルゴリズムごとにカウント
if (!isset($analysis['algorithms'][$algo])) {
$analysis['algorithms'][$algo] = 0;
}
$analysis['algorithms'][$algo]++;
// 再ハッシュが必要かチェック
if (password_needs_rehash($hash, $bestAlgo)) {
$analysis['needs_rehash']++;
}
}
return $analysis;
}
/**
* 最適なアルゴリズムを取得
*/
private static function getBestAvailableAlgorithm() {
$available = password_algos();
if (in_array('argon2id', $available)) {
return PASSWORD_ARGON2ID;
} elseif (in_array('argon2i', $available)) {
return PASSWORD_ARGON2I;
} else {
return PASSWORD_BCRYPT;
}
}
/**
* マイグレーション計画を表示
*/
public static function displayMigrationPlan($hashes) {
$analysis = self::analyzeExistingHashes($hashes);
echo "=== パスワードマイグレーション計画 ===\n\n";
echo "総パスワード数: {$analysis['total']}\n\n";
echo "現在のアルゴリズム分布:\n";
foreach ($analysis['algorithms'] as $algo => $count) {
$percentage = round(($count / $analysis['total']) * 100, 1);
echo " {$algo}: {$count}件 ({$percentage}%)\n";
}
echo "\n再ハッシュが必要: {$analysis['needs_rehash']}件\n";
if ($analysis['needs_rehash'] > 0) {
$percentage = round(($analysis['needs_rehash'] / $analysis['total']) * 100, 1);
echo "全体の {$percentage}% のパスワードを更新する必要があります\n\n";
echo "推奨アクション:\n";
echo "1. ユーザーがログインした際に自動的に再ハッシュ\n";
echo "2. バッチ処理での一括更新は避ける(パスワード生文字列が必要)\n";
echo "3. 新規ユーザーには最新アルゴリズムを使用\n";
} else {
echo "すべてのパスワードが最新のアルゴリズムを使用しています!\n";
}
}
}
// 使用例(サンプルデータ)
$existingHashes = [
password_hash("password1", PASSWORD_BCRYPT),
password_hash("password2", PASSWORD_BCRYPT),
password_hash("password3", PASSWORD_BCRYPT, ['cost' => 10]),
];
// 環境でArgon2idが使える場合、いくつか追加
if (in_array('argon2id', password_algos())) {
$existingHashes[] = password_hash("password4", PASSWORD_ARGON2ID);
}
PasswordMigrationPlanner::displayMigrationPlan($existingHashes);
?>
例5: セキュリティ監査ツール
<?php
class PasswordSecurityAuditor {
/**
* セキュリティ監査を実行
*/
public static function performAudit() {
$audit = [
'timestamp' => date('Y-m-d H:i:s'),
'php_version' => PHP_VERSION,
'available_algorithms' => password_algos(),
'checks' => []
];
// チェック1: PHP バージョン
if (version_compare(PHP_VERSION, '7.4.0', '>=')) {
$audit['checks']['php_version'] = [
'status' => 'PASS',
'message' => 'PHP 7.4以降を使用しています'
];
} else {
$audit['checks']['php_version'] = [
'status' => 'WARN',
'message' => 'PHP 7.4以降へのアップグレードを推奨'
];
}
// チェック2: Argon2サポート
if (in_array('argon2id', $audit['available_algorithms'])) {
$audit['checks']['argon2_support'] = [
'status' => 'PASS',
'message' => 'Argon2id が利用可能です'
];
} elseif (in_array('argon2i', $audit['available_algorithms'])) {
$audit['checks']['argon2_support'] = [
'status' => 'WARN',
'message' => 'Argon2i のみ利用可能。Argon2id の有効化を推奨'
];
} else {
$audit['checks']['argon2_support'] = [
'status' => 'FAIL',
'message' => 'Argon2 が利用できません。--with-password-argon2 でPHPを再コンパイル'
];
}
// チェック3: アルゴリズム数
$algoCount = count($audit['available_algorithms']);
if ($algoCount >= 3) {
$audit['checks']['algorithm_count'] = [
'status' => 'PASS',
'message' => "{$algoCount}種類のアルゴリズムが利用可能"
];
} else {
$audit['checks']['algorithm_count'] = [
'status' => 'WARN',
'message' => "{$algoCount}種類のみ。追加アルゴリズムの有効化を推奨"
];
}
return $audit;
}
/**
* 監査レポートを表示
*/
public static function displayAuditReport() {
$audit = self::performAudit();
echo "=== パスワードセキュリティ監査レポート ===\n\n";
echo "実行日時: {$audit['timestamp']}\n";
echo "PHP バージョン: {$audit['php_version']}\n\n";
echo "利用可能なアルゴリズム:\n";
foreach ($audit['available_algorithms'] as $algo) {
echo " • {$algo}\n";
}
echo "\n";
echo "セキュリティチェック結果:\n";
foreach ($audit['checks'] as $checkName => $result) {
$icon = [
'PASS' => '✓',
'WARN' => '⚠',
'FAIL' => '✗'
][$result['status']];
echo "{$icon} [{$result['status']}] {$result['message']}\n";
}
// 総合評価
$failCount = array_reduce($audit['checks'], function($carry, $check) {
return $carry + ($check['status'] === 'FAIL' ? 1 : 0);
}, 0);
$warnCount = array_reduce($audit['checks'], function($carry, $check) {
return $carry + ($check['status'] === 'WARN' ? 1 : 0);
}, 0);
echo "\n=== 総合評価 ===\n";
if ($failCount === 0 && $warnCount === 0) {
echo "優良: すべてのチェックに合格しています\n";
} elseif ($failCount === 0) {
echo "良好: 改善の余地がありますが、基本的な要件は満たしています\n";
} else {
echo "要改善: 重大な問題が {$failCount} 件検出されました\n";
}
}
/**
* JSON形式でレポートを出力
*/
public static function generateJsonReport() {
$audit = self::performAudit();
return json_encode($audit, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
}
}
// 使用例
PasswordSecurityAuditor::displayAuditReport();
echo "\n\n=== JSON形式のレポート ===\n";
echo PasswordSecurityAuditor::generateJsonReport();
?>
各アルゴリズムの特徴と選択基準
BCrypt (2y)
<?php
// BCryptの使用例
$options = ['cost' => 12]; // costが高いほど安全だが処理時間も増加
$hash = password_hash("password", PASSWORD_BCRYPT, $options);
echo "BCrypt特徴:\n";
echo "• 広くサポートされている\n";
echo "• 実績のある安全性\n";
echo "• メモリ使用量が少ない\n";
echo "• GPU攻撃に対してやや脆弱\n";
?>
Argon2i
<?php
if (in_array('argon2i', password_algos())) {
$options = [
'memory_cost' => 65536, // 64MB
'time_cost' => 4, // 反復回数
'threads' => 2 // 並列度
];
$hash = password_hash("password", PASSWORD_ARGON2I, $options);
echo "Argon2i特徴:\n";
echo "• サイドチャネル攻撃に強い\n";
echo "• メモリを大量に使用\n";
echo "• GPU攻撃に強い\n";
echo "• パスワードハッシュコンペティション優勝\n";
}
?>
Argon2id (推奨)
<?php
if (in_array('argon2id', password_algos())) {
$options = [
'memory_cost' => 65536,
'time_cost' => 4,
'threads' => 2
];
$hash = password_hash("password", PASSWORD_ARGON2ID, $options);
echo "Argon2id特徴:\n";
echo "• Argon2iとArgon2dのハイブリッド\n";
echo "• 最も推奨されるアルゴリズム\n";
echo "• サイドチャネルとGPU攻撃の両方に強い\n";
echo "• 現代的なセキュリティ要件に最適\n";
}
?>
トラブルシューティング
Argon2が利用できない場合
<?php
// Argon2サポートの確認と対処
$algos = password_algos();
if (!in_array('argon2id', $algos) && !in_array('argon2i', $algos)) {
echo "Argon2が利用できません。\n\n";
echo "対処方法:\n";
echo "1. PHPをArgon2サポート付きで再コンパイル\n";
echo " ./configure --with-password-argon2\n\n";
echo "2. パッケージマネージャーで適切なパッケージをインストール\n";
echo " Ubuntu/Debian: apt-get install php-sodium\n";
echo " CentOS/RHEL: yum install php-sodium\n\n";
echo "3. 一時的にBCryptを使用\n";
// BCryptで代替
$hash = password_hash("password", PASSWORD_BCRYPT, ['cost' => 12]);
echo "\nBCryptハッシュ: " . $hash . "\n";
}
?>
まとめ
password_algos関数は、PHP環境で利用可能なパスワードハッシュアルゴリズムを確認するための重要な関数です。
重要ポイント:
- 環境チェックに不可欠: 実装前に利用可能なアルゴリズムを確認
- PHP 7.4以降で使用可能: 古いバージョンでは利用不可
- セキュリティ監査に活用: 定期的なチェックで環境の安全性を確認
- マイグレーション計画: 既存システムのアップグレードに活用
推奨アルゴリズムの優先順位:
- PASSWORD_ARGON2ID (argon2id) – 最推奨
- PASSWORD_ARGON2I (argon2i) – 推奨
- PASSWORD_BCRYPT (2y) – 標準
ベストプラクティス:
- ✅ デプロイ前に必ず
password_algos()で環境を確認 - ✅ 最も安全なアルゴリズムを自動選択する仕組みを実装
- ✅ 定期的なセキュリティ監査を実施
- ✅ ユーザーログイン時に古いハッシュを自動更新
セキュアなパスワード管理は、Webアプリケーションのセキュリティの要です。password_algosを活用して、常に最適なアルゴリズムを使用しましょう!
関連記事
password_hash()– パスワードをハッシュ化password_verify()– パスワードを検証password_needs_rehash()– 再ハッシュの必要性をチェックpassword_get_info()– ハッシュの情報を取得
