[PHP]password_algos関数を完全解説!利用可能なハッシュアルゴリズムを確認する方法

PHP

こんにちは!今回は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定数説明推奨度
2yPASSWORD_BCRYPTBCrypt (デフォルト)★★★★☆
argon2iPASSWORD_ARGON2IArgon2i★★★★★
argon2idPASSWORD_ARGON2IDArgon2id (推奨)★★★★★

アルゴリズムの詳細

<?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以降で使用可能: 古いバージョンでは利用不可
  • セキュリティ監査に活用: 定期的なチェックで環境の安全性を確認
  • マイグレーション計画: 既存システムのアップグレードに活用

推奨アルゴリズムの優先順位:

  1. PASSWORD_ARGON2ID (argon2id) – 最推奨
  2. PASSWORD_ARGON2I (argon2i) – 推奨
  3. PASSWORD_BCRYPT (2y) – 標準

ベストプラクティス:

  • ✅ デプロイ前に必ずpassword_algos()で環境を確認
  • ✅ 最も安全なアルゴリズムを自動選択する仕組みを実装
  • ✅ 定期的なセキュリティ監査を実施
  • ✅ ユーザーログイン時に古いハッシュを自動更新

セキュアなパスワード管理は、Webアプリケーションのセキュリティの要です。password_algosを活用して、常に最適なアルゴリズムを使用しましょう!


関連記事

  • password_hash() – パスワードをハッシュ化
  • password_verify() – パスワードを検証
  • password_needs_rehash() – 再ハッシュの必要性をチェック
  • password_get_info() – ハッシュの情報を取得
タイトルとURLをコピーしました