[PHP]session_get_cookie_params関数を完全解説!セッションクッキーパラメータを取得する方法

PHP

こんにちは!今回は、PHPの標準関数であるsession_get_cookie_params()について詳しく解説していきます。セッションクッキーの設定を取得できる、セキュリティ確認とデバッグに重要な関数です!

session_get_cookie_params関数とは?

session_get_cookie_params()関数は、現在のセッションクッキーパラメータを取得する関数です。

セッションIDを保存するクッキーの有効期限、パス、ドメイン、セキュア設定、HttpOnly設定などを確認できます。セキュリティ監査や設定の検証に使用されます!

基本的な構文

session_get_cookie_params(): array
  • 引数: なし
  • 戻り値: クッキーパラメータを含む配列

返される配列の構造

// 返される配列の内容(PHP 7.3.0以降)
$params = session_get_cookie_params();

// 配列の構造
[
    'lifetime' => int,    // クッキーの有効期限(秒)、0=ブラウザ終了まで
    'path' => string,     // クッキーが有効なパス
    'domain' => string,   // クッキーが有効なドメイン
    'secure' => bool,     // HTTPS接続でのみ送信(true/false)
    'httponly' => bool,   // JavaScriptからアクセス不可(true/false)
    'samesite' => string  // SameSite属性('Lax', 'Strict', 'None', '')PHP 7.3.0以降
]

// 各パラメータの詳細
echo "Lifetime: {$params['lifetime']}秒\n";
echo "Path: {$params['path']}\n";
echo "Domain: {$params['domain']}\n";
echo "Secure: " . ($params['secure'] ? 'Yes' : 'No') . "\n";
echo "HttpOnly: " . ($params['httponly'] ? 'Yes' : 'No') . "\n";
echo "SameSite: {$params['samesite']}\n";  // PHP 7.3.0以降

重要な注意点

// PHP 7.3.0未満ではsameSiteキーは含まれない
if (PHP_VERSION_ID >= 70300) {
    $params = session_get_cookie_params();
    echo $params['samesite'];  // OK
} else {
    $params = session_get_cookie_params();
    // sameSiteキーは存在しない
}

// session_start()の前後どちらでも使用可能
$params1 = session_get_cookie_params();  // session_start()前
session_start();
$params2 = session_get_cookie_params();  // session_start()後
// 両方とも同じ値

// 設定を確認するだけ(変更はしない)
$params = session_get_cookie_params();
// パラメータを変更したい場合はsession_set_cookie_params()を使用

// デフォルト値
// lifetime: 0(ブラウザ終了まで)
// path: '/'
// domain: ''(現在のホスト)
// secure: false
// httponly: false
// samesite: ''(PHP 7.3.0以降)

session_set_cookie_params()との関係

// 設定を変更
session_set_cookie_params([
    'lifetime' => 3600,
    'path' => '/app',
    'domain' => 'example.com',
    'secure' => true,
    'httponly' => true,
    'samesite' => 'Strict'
]);

// 変更された設定を確認
$params = session_get_cookie_params();
echo "Lifetime: {$params['lifetime']}\n";  // 3600
echo "Path: {$params['path']}\n";          // /app
echo "Domain: {$params['domain']}\n";      // example.com
echo "Secure: {$params['secure']}\n";      // 1 (true)
echo "HttpOnly: {$params['httponly']}\n";  // 1 (true)
echo "SameSite: {$params['samesite']}\n";  // Strict

基本的な使用例

デフォルトパラメータの確認

// デフォルト設定を確認
$params = session_get_cookie_params();

echo "=== セッションクッキーパラメータ ===\n";
echo "Lifetime: {$params['lifetime']}秒\n";
echo "Path: {$params['path']}\n";
echo "Domain: {$params['domain']}\n";
echo "Secure: " . ($params['secure'] ? 'Yes' : 'No') . "\n";
echo "HttpOnly: " . ($params['httponly'] ? 'Yes' : 'No') . "\n";

if (isset($params['samesite'])) {
    echo "SameSite: {$params['samesite']}\n";
}

パラメータの詳細表示

$params = session_get_cookie_params();

echo "=== 詳細情報 ===\n";

// Lifetime
if ($params['lifetime'] === 0) {
    echo "有効期限: ブラウザ終了まで\n";
} else {
    echo "有効期限: {$params['lifetime']}秒 (" . 
         round($params['lifetime'] / 3600, 2) . "時間)\n";
}

// Path
echo "有効パス: {$params['path']}\n";
echo "  → このパス以下のページでクッキーが送信されます\n";

// Domain
if (empty($params['domain'])) {
    echo "有効ドメイン: 現在のホストのみ\n";
} else {
    echo "有効ドメイン: {$params['domain']}\n";
    echo "  → サブドメインを含む場合があります\n";
}

// Secure
if ($params['secure']) {
    echo "セキュア: HTTPS接続でのみ送信\n";
} else {
    echo "セキュア: HTTPでも送信(非推奨)\n";
}

// HttpOnly
if ($params['httponly']) {
    echo "HttpOnly: JavaScriptからアクセス不可(推奨)\n";
} else {
    echo "HttpOnly: JavaScriptからアクセス可能(XSSリスク)\n";
}

// SameSite
if (isset($params['samesite'])) {
    switch ($params['samesite']) {
        case 'Strict':
            echo "SameSite: Strict(最も厳格、CSRF完全防止)\n";
            break;
        case 'Lax':
            echo "SameSite: Lax(バランス型、推奨)\n";
            break;
        case 'None':
            echo "SameSite: None(制限なし、Secure必須)\n";
            break;
        default:
            echo "SameSite: 未設定\n";
    }
}

設定前後の比較

// 変更前
echo "=== 変更前 ===\n";
$before = session_get_cookie_params();
print_r($before);

// 設定を変更
session_set_cookie_params([
    'lifetime' => 7200,
    'path' => '/secure',
    'domain' => '.example.com',
    'secure' => true,
    'httponly' => true,
    'samesite' => 'Strict'
]);

// 変更後
echo "\n=== 変更後 ===\n";
$after = session_get_cookie_params();
print_r($after);

// 差分を表示
echo "\n=== 変更点 ===\n";
foreach ($after as $key => $value) {
    if ($before[$key] !== $value) {
        echo "{$key}: {$before[$key]} → {$value}\n";
    }
}

セキュリティチェック

$params = session_get_cookie_params();

echo "=== セキュリティチェック ===\n";

$issues = [];

// Secureチェック
if (!$params['secure']) {
    $issues[] = "WARNING: Secure属性が無効です(HTTPS推奨)";
}

// HttpOnlyチェック
if (!$params['httponly']) {
    $issues[] = "WARNING: HttpOnly属性が無効です(XSS対策必須)";
}

// SameSiteチェック
if (isset($params['samesite'])) {
    if (empty($params['samesite'])) {
        $issues[] = "WARNING: SameSite属性が未設定です(CSRF対策推奨)";
    } elseif ($params['samesite'] === 'None' && !$params['secure']) {
        $issues[] = "ERROR: SameSite=NoneにはSecure属性が必須です";
    }
}

if (empty($issues)) {
    echo "✓ セキュリティ設定は適切です\n";
} else {
    foreach ($issues as $issue) {
        echo "✗ {$issue}\n";
    }
}

実践的な使用例

例1: クッキーパラメータ検査システム

class CookieParamsInspector {
    private $params;
    
    /**
     * インスペクターを初期化
     */
    public function __construct() {
        $this->params = session_get_cookie_params();
    }
    
    /**
     * 完全な検査レポートを生成
     */
    public function inspect() {
        return [
            'parameters' => $this->params,
            'security_score' => $this->calculateSecurityScore(),
            'security_issues' => $this->findSecurityIssues(),
            'recommendations' => $this->getRecommendations(),
            'compatibility' => $this->checkCompatibility(),
            'summary' => $this->generateSummary()
        ];
    }
    
    /**
     * セキュリティスコアを計算(0-100)
     */
    public function calculateSecurityScore() {
        $score = 0;
        $maxScore = 100;
        
        // Secure属性(30点)
        if ($this->params['secure']) {
            $score += 30;
        }
        
        // HttpOnly属性(30点)
        if ($this->params['httponly']) {
            $score += 30;
        }
        
        // SameSite属性(25点)
        if (isset($this->params['samesite'])) {
            if ($this->params['samesite'] === 'Strict') {
                $score += 25;
            } elseif ($this->params['samesite'] === 'Lax') {
                $score += 20;
            } elseif ($this->params['samesite'] === 'None' && $this->params['secure']) {
                $score += 10;
            }
        }
        
        // Lifetime設定(15点)
        if ($this->params['lifetime'] > 0 && $this->params['lifetime'] <= 86400) {
            $score += 15;  // 適切な有効期限
        } elseif ($this->params['lifetime'] === 0) {
            $score += 10;  // ブラウザ終了まで
        } elseif ($this->params['lifetime'] <= 604800) {
            $score += 5;   // 1週間以内
        }
        
        return [
            'score' => $score,
            'max_score' => $maxScore,
            'percentage' => round(($score / $maxScore) * 100, 2),
            'grade' => $this->getSecurityGrade($score)
        ];
    }
    
    /**
     * セキュリティグレードを取得
     */
    private function getSecurityGrade($score) {
        if ($score >= 90) return 'A';
        if ($score >= 75) return 'B';
        if ($score >= 60) return 'C';
        if ($score >= 40) return 'D';
        return 'F';
    }
    
    /**
     * セキュリティ問題を検出
     */
    public function findSecurityIssues() {
        $issues = [];
        
        // Secure属性チェック
        if (!$this->params['secure']) {
            $issues[] = [
                'severity' => 'high',
                'type' => 'secure_disabled',
                'message' => 'Secure属性が無効です。HTTPS環境では必須です。',
                'impact' => 'Man-in-the-Middle攻撃のリスク'
            ];
        }
        
        // HttpOnly属性チェック
        if (!$this->params['httponly']) {
            $issues[] = [
                'severity' => 'high',
                'type' => 'httponly_disabled',
                'message' => 'HttpOnly属性が無効です。',
                'impact' => 'XSS攻撃によるセッションハイジャックのリスク'
            ];
        }
        
        // SameSite属性チェック
        if (isset($this->params['samesite'])) {
            if (empty($this->params['samesite'])) {
                $issues[] = [
                    'severity' => 'medium',
                    'type' => 'samesite_missing',
                    'message' => 'SameSite属性が未設定です。',
                    'impact' => 'CSRF攻撃のリスク'
                ];
            } elseif ($this->params['samesite'] === 'None' && !$this->params['secure']) {
                $issues[] = [
                    'severity' => 'critical',
                    'type' => 'samesite_none_insecure',
                    'message' => 'SameSite=NoneにはSecure属性が必須です。',
                    'impact' => 'クッキーが正しく機能しません'
                ];
            }
        }
        
        // Lifetime チェック
        if ($this->params['lifetime'] > 2592000) {  // 30日以上
            $issues[] = [
                'severity' => 'low',
                'type' => 'long_lifetime',
                'message' => 'クッキーの有効期限が長すぎます(' . 
                            round($this->params['lifetime'] / 86400) . '日)。',
                'impact' => 'セキュリティリスクの増加'
            ];
        }
        
        // Domain チェック
        if (!empty($this->params['domain']) && strpos($this->params['domain'], '.') === 0) {
            $issues[] = [
                'severity' => 'info',
                'type' => 'wildcard_domain',
                'message' => 'ワイルドカードドメインが使用されています({$this->params['domain']})。',
                'impact' => 'すべてのサブドメインでクッキーが共有されます'
            ];
        }
        
        return $issues;
    }
    
    /**
     * 推奨事項を取得
     */
    public function getRecommendations() {
        $recommendations = [];
        
        if (!$this->params['secure']) {
            $recommendations[] = [
                'priority' => 'high',
                'action' => 'Secure属性を有効化',
                'code' => "session_set_cookie_params(['secure' => true, ...]);"
            ];
        }
        
        if (!$this->params['httponly']) {
            $recommendations[] = [
                'priority' => 'high',
                'action' => 'HttpOnly属性を有効化',
                'code' => "session_set_cookie_params(['httponly' => true, ...]);"
            ];
        }
        
        if (isset($this->params['samesite']) && empty($this->params['samesite'])) {
            $recommendations[] = [
                'priority' => 'medium',
                'action' => 'SameSite属性を設定(Lax推奨)',
                'code' => "session_set_cookie_params(['samesite' => 'Lax', ...]);"
            ];
        }
        
        if ($this->params['lifetime'] === 0) {
            $recommendations[] = [
                'priority' => 'low',
                'action' => '明示的な有効期限を設定',
                'code' => "session_set_cookie_params(['lifetime' => 3600, ...]);"
            ];
        }
        
        return $recommendations;
    }
    
    /**
     * 互換性チェック
     */
    public function checkCompatibility() {
        $compatibility = [
            'php_version' => PHP_VERSION,
            'samesite_support' => PHP_VERSION_ID >= 70300,
            'issues' => []
        ];
        
        // SameSite対応チェック
        if (PHP_VERSION_ID < 70300 && isset($this->params['samesite'])) {
            $compatibility['issues'][] = 'PHP 7.3.0未満ではSameSite属性が正しく機能しない可能性があります';
        }
        
        // Secure + SameSite=None チェック
        if (isset($this->params['samesite']) && 
            $this->params['samesite'] === 'None' && 
            !$this->params['secure']) {
            $compatibility['issues'][] = 'SameSite=NoneにはSecure=trueが必須です';
        }
        
        return $compatibility;
    }
    
    /**
     * サマリーを生成
     */
    public function generateSummary() {
        $score = $this->calculateSecurityScore();
        $issues = $this->findSecurityIssues();
        
        $criticalCount = count(array_filter($issues, function($i) {
            return $i['severity'] === 'critical';
        }));
        
        $highCount = count(array_filter($issues, function($i) {
            return $i['severity'] === 'high';
        }));
        
        return [
            'security_grade' => $score['grade'],
            'security_percentage' => $score['percentage'],
            'total_issues' => count($issues),
            'critical_issues' => $criticalCount,
            'high_issues' => $highCount,
            'status' => $criticalCount > 0 ? 'critical' : 
                       ($highCount > 0 ? 'warning' : 'ok')
        ];
    }
    
    /**
     * レポートを表示
     */
    public function displayReport() {
        $report = $this->inspect();
        
        echo "=== Cookie Parameters Inspection Report ===\n\n";
        
        // サマリー
        echo "Security Grade: {$report['summary']['security_grade']} ";
        echo "({$report['summary']['security_percentage']}%)\n";
        echo "Status: " . strtoupper($report['summary']['status']) . "\n";
        echo "Total Issues: {$report['summary']['total_issues']}\n\n";
        
        // パラメータ
        echo "Current Parameters:\n";
        foreach ($report['parameters'] as $key => $value) {
            $displayValue = is_bool($value) ? ($value ? 'true' : 'false') : $value;
            echo "  {$key}: {$displayValue}\n";
        }
        echo "\n";
        
        // 問題点
        if (!empty($report['security_issues'])) {
            echo "Security Issues:\n";
            foreach ($report['security_issues'] as $issue) {
                echo "  [{$issue['severity']}] {$issue['message']}\n";
                echo "    Impact: {$issue['impact']}\n";
            }
            echo "\n";
        }
        
        // 推奨事項
        if (!empty($report['recommendations'])) {
            echo "Recommendations:\n";
            foreach ($report['recommendations'] as $rec) {
                echo "  [{$rec['priority']}] {$rec['action']}\n";
                echo "    {$rec['code']}\n";
            }
        }
    }
}

// 使用例
echo "=== クッキーパラメータ検査 ===\n";

$inspector = new CookieParamsInspector();

// レポート表示
$inspector->displayReport();

// 詳細な検査結果
$report = $inspector->inspect();
echo "\n=== セキュリティスコア詳細 ===\n";
echo "スコア: {$report['security_score']['score']}/{$report['security_score']['max_score']}\n";
echo "グレード: {$report['security_score']['grade']}\n";

例2: セキュリティ監査ツール

class SessionSecurityAuditor {
    private $auditResults = [];
    
    /**
     * 完全なセキュリティ監査を実行
     */
    public function audit() {
        $this->auditResults = [];
        
        // 各項目を監査
        $this->auditCookieParams();
        $this->auditSessionConfig();
        $this->auditSecurityHeaders();
        $this->auditBestPractices();
        
        return $this->generateAuditReport();
    }
    
    /**
     * クッキーパラメータを監査
     */
    private function auditCookieParams() {
        $params = session_get_cookie_params();
        $findings = [];
        
        // Secure属性の監査
        $findings[] = [
            'category' => 'Cookie Security',
            'check' => 'Secure Attribute',
            'status' => $params['secure'] ? 'pass' : 'fail',
            'value' => $params['secure'] ? 'Enabled' : 'Disabled',
            'recommendation' => $params['secure'] ? 
                'Good' : 'Enable Secure attribute for HTTPS',
            'severity' => $params['secure'] ? 'none' : 'high'
        ];
        
        // HttpOnly属性の監査
        $findings[] = [
            'category' => 'Cookie Security',
            'check' => 'HttpOnly Attribute',
            'status' => $params['httponly'] ? 'pass' : 'fail',
            'value' => $params['httponly'] ? 'Enabled' : 'Disabled',
            'recommendation' => $params['httponly'] ? 
                'Good' : 'Enable HttpOnly to prevent XSS',
            'severity' => $params['httponly'] ? 'none' : 'high'
        ];
        
        // SameSite属性の監査
        if (isset($params['samesite'])) {
            $sameSiteOk = in_array($params['samesite'], ['Strict', 'Lax']);
            $findings[] = [
                'category' => 'Cookie Security',
                'check' => 'SameSite Attribute',
                'status' => $sameSiteOk ? 'pass' : 'warning',
                'value' => $params['samesite'] ?: 'Not set',
                'recommendation' => $sameSiteOk ? 
                    'Good' : 'Set SameSite to Lax or Strict',
                'severity' => $sameSiteOk ? 'none' : 'medium'
            ];
        }
        
        // Lifetime の監査
        $lifetimeOk = $params['lifetime'] > 0 && $params['lifetime'] <= 86400;
        $findings[] = [
            'category' => 'Cookie Security',
            'check' => 'Cookie Lifetime',
            'status' => $lifetimeOk ? 'pass' : 'info',
            'value' => $params['lifetime'] === 0 ? 
                'Browser session' : $params['lifetime'] . ' seconds',
            'recommendation' => $lifetimeOk ? 
                'Appropriate' : 'Consider setting explicit lifetime',
            'severity' => 'low'
        ];
        
        $this->auditResults['cookie_params'] = $findings;
    }
    
    /**
     * セッション設定を監査
     */
    private function auditSessionConfig() {
        $findings = [];
        
        // session.use_strict_mode
        $strictMode = ini_get('session.use_strict_mode');
        $findings[] = [
            'category' => 'Session Configuration',
            'check' => 'Strict Mode',
            'status' => $strictMode ? 'pass' : 'fail',
            'value' => $strictMode ? 'Enabled' : 'Disabled',
            'recommendation' => $strictMode ? 
                'Good' : 'Enable session.use_strict_mode',
            'severity' => $strictMode ? 'none' : 'high'
        ];
        
        // session.use_only_cookies
        $onlyCookies = ini_get('session.use_only_cookies');
        $findings[] = [
            'category' => 'Session Configuration',
            'check' => 'Use Only Cookies',
            'status' => $onlyCookies ? 'pass' : 'fail',
            'value' => $onlyCookies ? 'Enabled' : 'Disabled',
            'recommendation' => $onlyCookies ? 
                'Good' : 'Enable session.use_only_cookies',
            'severity' => $onlyCookies ? 'none' : 'critical'
        ];
        
        // session.cookie_httponly
        $cookieHttpOnly = ini_get('session.cookie_httponly');
        $findings[] = [
            'category' => 'Session Configuration',
            'check' => 'Cookie HttpOnly (ini)',
            'status' => $cookieHttpOnly ? 'pass' : 'fail',
            'value' => $cookieHttpOnly ? 'Enabled' : 'Disabled',
            'recommendation' => $cookieHttpOnly ? 
                'Good' : 'Enable session.cookie_httponly',
            'severity' => $cookieHttpOnly ? 'none' : 'high'
        ];
        
        // session.cookie_secure
        $cookieSecure = ini_get('session.cookie_secure');
        $findings[] = [
            'category' => 'Session Configuration',
            'check' => 'Cookie Secure (ini)',
            'status' => $cookieSecure ? 'pass' : 'warning',
            'value' => $cookieSecure ? 'Enabled' : 'Disabled',
            'recommendation' => $cookieSecure ? 
                'Good' : 'Enable session.cookie_secure for HTTPS',
            'severity' => $cookieSecure ? 'none' : 'high'
        ];
        
        $this->auditResults['session_config'] = $findings;
    }
    
    /**
     * セキュリティヘッダーを監査
     */
    private function auditSecurityHeaders() {
        $findings = [];
        
        // この実装例では実際のヘッダー送信はしないため、
        // 推奨事項のみを記録
        $findings[] = [
            'category' => 'Security Headers',
            'check' => 'X-Frame-Options',
            'status' => 'info',
            'value' => 'Not checked',
            'recommendation' => 'Set X-Frame-Options: DENY or SAMEORIGIN',
            'severity' => 'medium'
        ];
        
        $findings[] = [
            'category' => 'Security Headers',
            'check' => 'X-Content-Type-Options',
            'status' => 'info',
            'value' => 'Not checked',
            'recommendation' => 'Set X-Content-Type-Options: nosniff',
            'severity' => 'low'
        ];
        
        $findings[] = [
            'category' => 'Security Headers',
            'check' => 'Strict-Transport-Security',
            'status' => 'info',
            'value' => 'Not checked',
            'recommendation' => 'Set HSTS header for HTTPS',
            'severity' => 'medium'
        ];
        
        $this->auditResults['security_headers'] = $findings;
    }
    
    /**
     * ベストプラクティスを監査
     */
    private function auditBestPractices() {
        $findings = [];
        
        $params = session_get_cookie_params();
        
        // セキュアな設定の組み合わせ
        $hasSecureCombo = $params['secure'] && 
                         $params['httponly'] && 
                         isset($params['samesite']) && 
                         !empty($params['samesite']);
        
        $findings[] = [
            'category' => 'Best Practices',
            'check' => 'Secure Cookie Combination',
            'status' => $hasSecureCombo ? 'pass' : 'fail',
            'value' => $hasSecureCombo ? 
                'All security attributes enabled' : 
                'Missing security attributes',
            'recommendation' => $hasSecureCombo ? 
                'Excellent' : 
                'Enable Secure, HttpOnly, and SameSite together',
            'severity' => $hasSecureCombo ? 'none' : 'high'
        ];
        
        // Domain設定の適切性
        $appropriateDomain = empty($params['domain']) || 
                           !str_starts_with($params['domain'], '.');
        
        $findings[] = [
            'category' => 'Best Practices',
            'check' => 'Domain Scope',
            'status' => $appropriateDomain ? 'pass' : 'warning',
            'value' => empty($params['domain']) ? 
                'Current host only' : $params['domain'],
            'recommendation' => $appropriateDomain ? 
                'Appropriate' : 
                'Be cautious with wildcard domains',
            'severity' => 'low'
        ];
        
        $this->auditResults['best_practices'] = $findings;
    }
    
    /**
     * 監査レポートを生成
     */
    private function generateAuditReport() {
        $allFindings = [];
        foreach ($this->auditResults as $category => $findings) {
            $allFindings = array_merge($allFindings, $findings);
        }
        
        // 統計を計算
        $stats = [
            'total_checks' => count($allFindings),
            'passed' => 0,
            'failed' => 0,
            'warnings' => 0,
            'info' => 0,
            'critical' => 0,
            'high' => 0,
            'medium' => 0,
            'low' => 0
        ];
        
        foreach ($allFindings as $finding) {
            // ステータス
            switch ($finding['status']) {
                case 'pass':
                    $stats['passed']++;
                    break;
                case 'fail':
                    $stats['failed']++;
                    break;
                case 'warning':
                    $stats['warnings']++;
                    break;
                case 'info':
                    $stats['info']++;
                    break;
            }
            
            // 深刻度
            switch ($finding['severity']) {
                case 'critical':
                    $stats['critical']++;
                    break;
                case 'high':
                    $stats['high']++;
                    break;
                case 'medium':
                    $stats['medium']++;
                    break;
                case 'low':
                    $stats['low']++;
                    break;
            }
        }
        
        // 総合評価
        $overallScore = 100;
        $overallScore -= $stats['critical'] * 30;
        $overallScore -= $stats['high'] * 20;
        $overallScore -= $stats['medium'] * 10;
        $overallScore -= $stats['low'] * 5;
        $overallScore = max(0, $overallScore);
        
        return [
            'findings' => $this->auditResults,
            'statistics' => $stats,
            'overall_score' => $overallScore,
            'overall_grade' => $this->calculateGrade($overallScore),
            'timestamp' => time()
        ];
    }
    
    /**
     * グレードを計算
     */
    private function calculateGrade($score) {
        if ($score >= 90) return 'A';
        if ($score >= 80) return 'B';
        if ($score >= 70) return 'C';
        if ($score >= 60) return 'D';
        return 'F';
    }
    
    /**
     * レポートを表示
     */
    public function displayReport() {
        $report = $this->audit();
        
        echo "=== Session Security Audit Report ===\n\n";
        echo "Overall Score: {$report['overall_score']}/100 (Grade: {$report['overall_grade']})\n";
        echo "Timestamp: " . date('Y-m-d H:i:s', $report['timestamp']) . "\n\n";
        
        echo "Summary:\n";
        echo "  Total Checks: {$report['statistics']['total_checks']}\n";
        echo "  Passed: {$report['statistics']['passed']}\n";
        echo "  Failed: {$report['statistics']['failed']}\n";
        echo "  Warnings: {$report['statistics']['warnings']}\n";
        echo "  Critical Issues: {$report['statistics']['critical']}\n";
        echo "  High Issues: {$report['statistics']['high']}\n\n";
        
        foreach ($report['findings'] as $category => $findings) {
            echo strtoupper(str_replace('_', ' ', $category)) . ":\n";
            foreach ($findings as $finding) {
                $status = strtoupper($finding['status']);
                echo "  [{$status}] {$finding['check']}\n";
                echo "    Value: {$finding['value']}\n";
                echo "    Recommendation: {$finding['recommendation']}\n";
                if ($finding['severity'] !== 'none') {
                    echo "    Severity: {$finding['severity']}\n";
                }
            }
            echo "\n";
        }
    }
    
    /**
     * JSON形式でエクスポート
     */
    public function exportJson() {
        $report = $this->audit();
        return json_encode($report, JSON_PRETTY_PRINT);
    }
}

// 使用例
echo "=== セキュリティ監査ツール ===\n";

$auditor = new SessionSecurityAuditor();

// レポート表示
$auditor->displayReport();

// JSON エクスポート
echo "\n=== JSON Export ===\n";
$json = $auditor->exportJson();
echo substr($json, 0, 500) . "...\n";

例3: クッキー設定管理システム

class CookieParamsManager {
    private $profiles = [];
    
    /**
     * マネージャーを初期化
     */
    public function __construct() {
        $this->initializeProfiles();
    }
    
    /**
     * デフォルトプロファイルを初期化
     */
    private function initializeProfiles() {
        // 開発環境プロファイル
        $this->profiles['development'] = [
            'lifetime' => 0,
            'path' => '/',
            'domain' => '',
            'secure' => false,
            'httponly' => true,
            'samesite' => 'Lax'
        ];
        
        // 本番環境プロファイル
        $this->profiles['production'] = [
            'lifetime' => 7200,  // 2時間
            'path' => '/',
            'domain' => '',
            'secure' => true,
            'httponly' => true,
            'samesite' => 'Strict'
        ];
        
        // API用プロファイル
        $this->profiles['api'] = [
            'lifetime' => 3600,  // 1時間
            'path' => '/api',
            'domain' => '',
            'secure' => true,
            'httponly' => true,
            'samesite' => 'None'  // クロスドメインAPI用
        ];
        
        // 管理画面プロファイル
        $this->profiles['admin'] = [
            'lifetime' => 1800,  // 30分
            'path' => '/admin',
            'domain' => '',
            'secure' => true,
            'httponly' => true,
            'samesite' => 'Strict'
        ];
    }
    
    /**
     * プロファイルを適用
     */
    public function applyProfile($profileName) {
        if (!isset($this->profiles[$profileName])) {
            throw new Exception("Profile not found: {$profileName}");
        }
        
        $before = session_get_cookie_params();
        $profile = $this->profiles[$profileName];
        
        session_set_cookie_params($profile);
        
        $after = session_get_cookie_params();
        
        return [
            'profile' => $profileName,
            'before' => $before,
            'after' => $after,
            'applied' => $profile
        ];
    }
    
    /**
     * カスタムプロファイルを作成
     */
    public function createProfile($name, $params) {
        // バリデーション
        $this->validateParams($params);
        
        $this->profiles[$name] = $params;
        
        return [
            'name' => $name,
            'params' => $params,
            'created' => true
        ];
    }
    
    /**
     * パラメータをバリデート
     */
    private function validateParams($params) {
        $required = ['lifetime', 'path', 'domain', 'secure', 'httponly'];
        
        foreach ($required as $key) {
            if (!array_key_exists($key, $params)) {
                throw new Exception("Missing required parameter: {$key}");
            }
        }
        
        // 型チェック
        if (!is_int($params['lifetime']) || $params['lifetime'] < 0) {
            throw new Exception("Invalid lifetime: must be non-negative integer");
        }
        
        if (!is_string($params['path'])) {
            throw new Exception("Invalid path: must be string");
        }
        
        if (!is_bool($params['secure'])) {
            throw new Exception("Invalid secure: must be boolean");
        }
        
        if (!is_bool($params['httponly'])) {
            throw new Exception("Invalid httponly: must be boolean");
        }
        
        // SameSite値チェック
        if (isset($params['samesite'])) {
            $validSameSite = ['', 'Lax', 'Strict', 'None'];
            if (!in_array($params['samesite'], $validSameSite)) {
                throw new Exception("Invalid samesite: must be one of " . implode(', ', $validSameSite));
            }
        }
        
        // セキュリティチェック
        if (isset($params['samesite']) && 
            $params['samesite'] === 'None' && 
            !$params['secure']) {
            throw new Exception("SameSite=None requires Secure=true");
        }
    }
    
    /**
     * プロファイル一覧を取得
     */
    public function listProfiles() {
        $list = [];
        
        foreach ($this->profiles as $name => $params) {
            $list[$name] = [
                'name' => $name,
                'params' => $params,
                'security_level' => $this->assessSecurityLevel($params)
            ];
        }
        
        return $list;
    }
    
    /**
     * セキュリティレベルを評価
     */
    private function assessSecurityLevel($params) {
        $score = 0;
        
        if ($params['secure']) $score += 30;
        if ($params['httponly']) $score += 30;
        if (isset($params['samesite']) && $params['samesite'] === 'Strict') $score += 25;
        elseif (isset($params['samesite']) && $params['samesite'] === 'Lax') $score += 15;
        
        if ($params['lifetime'] > 0 && $params['lifetime'] <= 3600) $score += 15;
        
        if ($score >= 85) return 'high';
        if ($score >= 60) return 'medium';
        return 'low';
    }
    
    /**
     * 現在の設定を取得
     */
    public function getCurrentSettings() {
        $params = session_get_cookie_params();
        
        return [
            'params' => $params,
            'security_level' => $this->assessSecurityLevel($params),
            'profile_match' => $this->findMatchingProfile($params)
        ];
    }
    
    /**
     * マッチするプロファイルを検索
     */
    private function findMatchingProfile($params) {
        foreach ($this->profiles as $name => $profile) {
            if ($this->paramsMatch($params, $profile)) {
                return $name;
            }
        }
        
        return null;
    }
    
    /**
     * パラメータが一致するかチェック
     */
    private function paramsMatch($params1, $params2) {
        $keys = ['lifetime', 'path', 'domain', 'secure', 'httponly'];
        
        foreach ($keys as $key) {
            if ($params1[$key] !== $params2[$key]) {
                return false;
            }
        }
        
        // SameSite は optional
        if (isset($params1['samesite']) && isset($params2['samesite'])) {
            if ($params1['samesite'] !== $params2['samesite']) {
                return false;
            }
        }
        
        return true;
    }
    
    /**
     * プロファイル比較
     */
    public function compareProfiles($profile1, $profile2) {
        if (!isset($this->profiles[$profile1]) || !isset($this->profiles[$profile2])) {
            throw new Exception("One or both profiles not found");
        }
        
        $p1 = $this->profiles[$profile1];
        $p2 = $this->profiles[$profile2];
        
        $differences = [];
        $allKeys = array_unique(array_merge(array_keys($p1), array_keys($p2)));
        
        foreach ($allKeys as $key) {
            $val1 = $p1[$key] ?? null;
            $val2 = $p2[$key] ?? null;
            
            if ($val1 !== $val2) {
                $differences[$key] = [
                    $profile1 => $val1,
                    $profile2 => $val2
                ];
            }
        }
        
        return [
            'profile1' => $profile1,
            'profile2' => $profile2,
            'differences' => $differences,
            'identical' => empty($differences)
        ];
    }
    
    /**
     * 環境に応じた推奨プロファイルを取得
     */
    public function getRecommendedProfile($environment = 'production', $isHttps = true) {
        if ($environment === 'development') {
            return 'development';
        }
        
        if (!$isHttps) {
            // HTTPSでない場合は警告
            return [
                'recommended' => 'development',
                'warning' => 'HTTPS is not available. Secure cookies cannot be used.'
            ];
        }
        
        return 'production';
    }
}

// 使用例
echo "=== クッキー設定管理システム ===\n";

$manager = new CookieParamsManager();

// プロファイル一覧
echo "利用可能なプロファイル:\n";
foreach ($manager->listProfiles() as $name => $info) {
    echo "  {$name}: セキュリティレベル={$info['security_level']}\n";
}

// 現在の設定
echo "\n現在の設定:\n";
$current = $manager->getCurrentSettings();
echo "  セキュリティレベル: {$current['security_level']}\n";
echo "  マッチするプロファイル: " . ($current['profile_match'] ?? 'なし') . "\n";

// プロファイル適用
echo "\n本番環境プロファイルを適用:\n";
$result = $manager->applyProfile('production');
echo "  適用完了\n";
echo "  Secure: " . ($result['after']['secure'] ? 'Yes' : 'No') . "\n";
echo "  HttpOnly: " . ($result['after']['httponly'] ? 'Yes' : 'No') . "\n";
echo "  SameSite: {$result['after']['samesite']}\n";

// プロファイル比較
echo "\n開発環境 vs 本番環境:\n";
$comparison = $manager->compareProfiles('development', 'production');
foreach ($comparison['differences'] as $key => $values) {
    echo "  {$key}:\n";
    foreach ($values as $profile => $value) {
        $displayValue = is_bool($value) ? ($value ? 'true' : 'false') : $value;
        echo "    {$profile}: {$displayValue}\n";
    }
}

// カスタムプロファイル作成
echo "\nカスタムプロファイル作成:\n";
$custom = $manager->createProfile('custom_strict', [
    'lifetime' => 900,  // 15分
    'path' => '/secure',
    'domain' => '',
    'secure' => true,
    'httponly' => true,
    'samesite' => 'Strict'
]);
echo "  {$custom['name']} を作成しました\n";

例4: クッキーパラメータ変更履歴トラッカー

class CookieParamsHistoryTracker {
    private $historyFile;
    private $history = [];
    
    /**
     * トラッカーを初期化
     */
    public function __construct($historyFile = '/tmp/cookie_params_history.json') {
        $this->historyFile = $historyFile;
        $this->loadHistory();
    }
    
    /**
     * 履歴を読み込み
     */
    private function loadHistory() {
        if (file_exists($this->historyFile)) {
            $this->history = json_decode(file_get_contents($this->historyFile), true) ?? [];
        }
    }
    
    /**
     * 履歴を保存
     */
    private function saveHistory() {
        file_put_contents($this->historyFile, json_encode($this->history, JSON_PRETTY_PRINT));
    }
    
    /**
     * 現在のパラメータを記録
     */
    public function snapshot($label = null) {
        $params = session_get_cookie_params();
        
        $snapshot = [
            'timestamp' => time(),
            'label' => $label,
            'params' => $params,
            'php_version' => PHP_VERSION,
            'session_started' => session_status() === PHP_SESSION_ACTIVE
        ];
        
        $this->history[] = $snapshot;
        $this->saveHistory();
        
        return $snapshot;
    }
    
    /**
     * 履歴を取得
     */
    public function getHistory($limit = null) {
        $history = $this->history;
        
        if ($limit !== null) {
            $history = array_slice($history, -$limit);
        }
        
        return $history;
    }
    
    /**
     * 変更を検出
     */
    public function detectChanges() {
        if (count($this->history) < 2) {
            return [
                'has_changes' => false,
                'message' => 'Insufficient history for comparison'
            ];
        }
        
        $latest = end($this->history);
        $previous = $this->history[count($this->history) - 2];
        
        $changes = [];
        
        foreach ($latest['params'] as $key => $value) {
            if (!isset($previous['params'][$key]) || $previous['params'][$key] !== $value) {
                $changes[$key] = [
                    'old' => $previous['params'][$key] ?? null,
                    'new' => $value,
                    'timestamp' => $latest['timestamp']
                ];
            }
        }
        
        return [
            'has_changes' => !empty($changes),
            'changes' => $changes,
            'previous_snapshot' => $previous,
            'latest_snapshot' => $latest
        ];
    }
    
    /**
     * 特定期間の変更履歴を分析
     */
    public function analyzeChanges($startTime = null, $endTime = null) {
        $startTime = $startTime ?? 0;
        $endTime = $endTime ?? time();
        
        $periodHistory = array_filter($this->history, function($snapshot) use ($startTime, $endTime) {
            return $snapshot['timestamp'] >= $startTime && $snapshot['timestamp'] <= $endTime;
        });
        
        if (count($periodHistory) < 2) {
            return ['error' => 'Insufficient data in specified period'];
        }
        
        $allChanges = [];
        $previousSnapshot = null;
        
        foreach ($periodHistory as $snapshot) {
            if ($previousSnapshot !== null) {
                foreach ($snapshot['params'] as $key => $value) {
                    if (!isset($previousSnapshot['params'][$key]) || 
                        $previousSnapshot['params'][$key] !== $value) {
                        
                        if (!isset($allChanges[$key])) {
                            $allChanges[$key] = [];
                        }
                        
                        $allChanges[$key][] = [
                            'timestamp' => $snapshot['timestamp'],
                            'old' => $previousSnapshot['params'][$key] ?? null,
                            'new' => $value,
                            'label' => $snapshot['label']
                        ];
                    }
                }
            }
            $previousSnapshot = $snapshot;
        }
        
        return [
            'period_start' => date('Y-m-d H:i:s', $startTime),
            'period_end' => date('Y-m-d H:i:s', $endTime),
            'total_snapshots' => count($periodHistory),
            'parameters_changed' => array_keys($allChanges),
            'change_count' => array_sum(array_map('count', $allChanges)),
            'changes' => $allChanges
        ];
    }
    
    /**
     * パラメータごとの変更頻度を取得
     */
    public function getChangeFrequency() {
        $frequency = [];
        
        for ($i = 1; $i < count($this->history); $i++) {
            $previous = $this->history[$i - 1];
            $current = $this->history[$i];
            
            foreach ($current['params'] as $key => $value) {
                if (!isset($previous['params'][$key]) || $previous['params'][$key] !== $value) {
                    if (!isset($frequency[$key])) {
                        $frequency[$key] = 0;
                    }
                    $frequency[$key]++;
                }
            }
        }
        
        arsort($frequency);
        
        return $frequency;
    }
    
    /**
     * ロールバック(特定のスナップショットに戻す)
     */
    public function rollback($index) {
        if (!isset($this->history[$index])) {
            throw new Exception("Snapshot not found at index: {$index}");
        }
        
        $snapshot = $this->history[$index];
        $params = $snapshot['params'];
        
        session_set_cookie_params($params);
        
        // 新しいスナップショットを作成
        $this->snapshot("Rolled back to index {$index}");
        
        return [
            'rolled_back_to' => $index,
            'timestamp' => $snapshot['timestamp'],
            'label' => $snapshot['label'],
            'params' => $params
        ];
    }
    
    /**
     * レポートを生成
     */
    public function generateReport() {
        $report = "=== Cookie Parameters History Report ===\n\n";
        
        $report .= "Total Snapshots: " . count($this->history) . "\n";
        
        if (!empty($this->history)) {
            $first = reset($this->history);
            $last = end($this->history);
            
            $report .= "First Snapshot: " . date('Y-m-d H:i:s', $first['timestamp']) . "\n";
            $report .= "Latest Snapshot: " . date('Y-m-d H:i:s', $last['timestamp']) . "\n\n";
            
            // 変更頻度
            $frequency = $this->getChangeFrequency();
            if (!empty($frequency)) {
                $report .= "Change Frequency:\n";
                foreach ($frequency as $param => $count) {
                    $report .= "  {$param}: {$count} changes\n";
                }
                $report .= "\n";
            }
            
            // 最新の変更
            $changes = $this->detectChanges();
            if ($changes['has_changes']) {
                $report .= "Latest Changes:\n";
                foreach ($changes['changes'] as $param => $change) {
                    $oldVal = is_bool($change['old']) ? ($change['old'] ? 'true' : 'false') : $change['old'];
                    $newVal = is_bool($change['new']) ? ($change['new'] ? 'true' : 'false') : $change['new'];
                    $report .= "  {$param}: {$oldVal} → {$newVal}\n";
                }
            } else {
                $report .= "No recent changes detected\n";
            }
        }
        
        return $report;
    }
    
    /**
     * タイムラインを表示
     */
    public function displayTimeline($limit = 10) {
        $history = array_slice($this->history, -$limit);
        
        echo "=== Cookie Parameters Timeline ===\n\n";
        
        foreach ($history as $index => $snapshot) {
            echo "[" . date('Y-m-d H:i:s', $snapshot['timestamp']) . "]";
            if ($snapshot['label']) {
                echo " {$snapshot['label']}";
            }
            echo "\n";
            
            foreach ($snapshot['params'] as $key => $value) {
                $displayValue = is_bool($value) ? ($value ? 'true' : 'false') : $value;
                echo "  {$key}: {$displayValue}\n";
            }
            echo "\n";
        }
    }
}

// 使用例
echo "=== クッキーパラメータ変更履歴 ===\n";

$tracker = new CookieParamsHistoryTracker('/tmp/cookie_history_test.json');

// 初期スナップショット
$tracker->snapshot('Initial state');
echo "初期状態を記録\n";

// パラメータ変更
session_set_cookie_params([
    'lifetime' => 3600,
    'path' => '/',
    'domain' => '',
    'secure' => true,
    'httponly' => true,
    'samesite' => 'Lax'
]);
$tracker->snapshot('Enabled security features');
echo "セキュリティ機能を有効化\n";

// さらに変更
session_set_cookie_params([
    'lifetime' => 7200,
    'path' => '/',
    'domain' => '',
    'secure' => true,
    'httponly' => true,
    'samesite' => 'Strict'
]);
$tracker->snapshot('Increased lifetime and stricter SameSite');
echo "有効期限を延長、SameSiteを厳格化\n";

// タイムライン表示
echo "\n";
$tracker->displayTimeline(5);

// 変更検出
echo "=== 最新の変更 ===\n";
$changes = $tracker->detectChanges();
if ($changes['has_changes']) {
    foreach ($changes['changes'] as $param => $change) {
        $oldVal = is_bool($change['old']) ? ($change['old'] ? 'true' : 'false') : ($change['old'] ?? 'null');
        $newVal = is_bool($change['new']) ? ($change['new'] ? 'true' : 'false') : $change['new'];
        echo "{$param}: {$oldVal} → {$newVal}\n";
    }
}

// レポート生成
echo "\n";
echo $tracker->generateReport();

// 変更頻度
echo "\n=== 変更頻度 ===\n";
$frequency = $tracker->getChangeFrequency();
foreach ($frequency as $param => $count) {
    echo "{$param}: {$count}回\n";
}

例5: 環境別設定検証システム

class EnvironmentConfigValidator {
    private $environments = [];
    
    /**
     * 環境を登録
     */
    public function registerEnvironment($name, $expectedParams) {
        $this->environments[$name] = $expectedParams;
    }
    
    /**
     * デフォルト環境を設定
     */
    public function setupDefaultEnvironments() {
        // ローカル開発環境
        $this->registerEnvironment('local', [
            'secure' => false,
            'httponly' => true,
            'samesite' => 'Lax',
            'lifetime' => 0
        ]);
        
        // ステージング環境
        $this->registerEnvironment('staging', [
            'secure' => true,
            'httponly' => true,
            'samesite' => 'Lax',
            'lifetime' => 3600
        ]);
        
        // 本番環境
        $this->registerEnvironment('production', [
            'secure' => true,
            'httponly' => true,
            'samesite' => 'Strict',
            'lifetime' => 7200
        ]);
    }
    
    /**
     * 現在の環境を検証
     */
    public function validate($environmentName) {
        if (!isset($this->environments[$environmentName])) {
            throw new Exception("Environment not registered: {$environmentName}");
        }
        
        $expected = $this->environments[$environmentName];
        $actual = session_get_cookie_params();
        
        $mismatches = [];
        $matches = [];
        
        foreach ($expected as $key => $expectedValue) {
            if (!isset($actual[$key])) {
                $mismatches[$key] = [
                    'expected' => $expectedValue,
                    'actual' => null,
                    'severity' => 'error'
                ];
            } elseif ($actual[$key] !== $expectedValue) {
                $mismatches[$key] = [
                    'expected' => $expectedValue,
                    'actual' => $actual[$key],
                    'severity' => $this->getSeverity($key, $expectedValue, $actual[$key])
                ];
            } else {
                $matches[$key] = $expectedValue;
            }
        }
        
        return [
            'environment' => $environmentName,
            'valid' => empty($mismatches),
            'matches' => $matches,
            'mismatches' => $mismatches,
            'match_count' => count($matches),
            'mismatch_count' => count($mismatches)
        ];
    }
    
    /**
     * 不一致の深刻度を判定
     */
    private function getSeverity($key, $expected, $actual) {
        // セキュリティ関連は重大
        if (in_array($key, ['secure', 'httponly', 'samesite'])) {
            // 期待値がセキュアだが実際がそうでない場合
            if (($key === 'secure' || $key === 'httponly') && $expected && !$actual) {
                return 'critical';
            }
            if ($key === 'samesite' && $expected === 'Strict' && $actual !== 'Strict') {
                return 'high';
            }
            return 'medium';
        }
        
        // その他は低
        return 'low';
    }
    
    /**
     * すべての環境を検証
     */
    public function validateAll() {
        $results = [];
        
        foreach (array_keys($this->environments) as $envName) {
            $results[$envName] = $this->validate($envName);
        }
        
        return $results;
    }
    
    /**
     * 環境を推測
     */
    public function detectEnvironment() {
        $actual = session_get_cookie_params();
        $scores = [];
        
        foreach ($this->environments as $envName => $expected) {
            $score = 0;
            $total = count($expected);
            
            foreach ($expected as $key => $value) {
                if (isset($actual[$key]) && $actual[$key] === $value) {
                    $score++;
                }
            }
            
            $scores[$envName] = [
                'score' => $score,
                'total' => $total,
                'percentage' => round(($score / $total) * 100, 2)
            ];
        }
        
        // 最高スコアの環境を取得
        uasort($scores, function($a, $b) {
            return $b['score'] - $a['score'];
        });
        
        $bestMatch = key($scores);
        
        return [
            'detected_environment' => $bestMatch,
            'confidence' => $scores[$bestMatch]['percentage'],
            'all_scores' => $scores
        ];
    }
    
    /**
     * 修正提案を生成
     */
    public function suggestFix($environmentName) {
        $validation = $this->validate($environmentName);
        
        if ($validation['valid']) {
            return [
                'needs_fix' => false,
                'message' => 'Configuration is correct for this environment'
            ];
        }
        
        $fixes = [];
        
        foreach ($validation['mismatches'] as $key => $mismatch) {
            $fixes[] = [
                'parameter' => $key,
                'current_value' => $mismatch['actual'],
                'expected_value' => $mismatch['expected'],
                'severity' => $mismatch['severity'],
                'fix_code' => $this->generateFixCode($key, $mismatch['expected'])
            ];
        }
        
        // 全体の修正コードを生成
        $allParams = session_get_cookie_params();
        foreach ($validation['mismatches'] as $key => $mismatch) {
            $allParams[$key] = $mismatch['expected'];
        }
        
        return [
            'needs_fix' => true,
            'environment' => $environmentName,
            'fixes' => $fixes,
            'complete_fix_code' => $this->generateCompleteFixCode($allParams)
        ];
    }
    
    /**
     * 修正コードを生成
     */
    private function generateFixCode($key, $value) {
        $valueStr = is_bool($value) ? ($value ? 'true' : 'false') : 
                   (is_string($value) ? "'{$value}'" : $value);
        
        return "session_set_cookie_params(['{$key}' => {$valueStr}, ...]);";
    }
    
    /**
     * 完全な修正コードを生成
     */
    private function generateCompleteFixCode($params) {
        $lines = ["session_set_cookie_params(["];
        
        foreach ($params as $key => $value) {
            $valueStr = is_bool($value) ? ($value ? 'true' : 'false') : 
                       (is_string($value) ? "'{$value}'" : $value);
            $lines[] = "    '{$key}' => {$valueStr},";
        }
        
        $lines[] = "]);";
        
        return implode("\n", $lines);
    }
    
    /**
     * 検証レポートを表示
     */
    public function displayValidationReport($environmentName) {
        $validation = $this->validate($environmentName);
        
        echo "=== Environment Validation Report ===\n";
        echo "Environment: {$environmentName}\n";
        echo "Status: " . ($validation['valid'] ? 'VALID' : 'INVALID') . "\n";
        echo "Matches: {$validation['match_count']}\n";
        echo "Mismatches: {$validation['mismatch_count']}\n\n";
        
        if (!empty($validation['matches'])) {
            echo "Correct Parameters:\n";
            foreach ($validation['matches'] as $key => $value) {
                $displayValue = is_bool($value) ? ($value ? 'true' : 'false') : $value;
                echo "  ✓ {$key}: {$displayValue}\n";
            }
            echo "\n";
        }
        
        if (!empty($validation['mismatches'])) {
            echo "Incorrect Parameters:\n";
            foreach ($validation['mismatches'] as $key => $mismatch) {
                $expectedVal = is_bool($mismatch['expected']) ? 
                    ($mismatch['expected'] ? 'true' : 'false') : $mismatch['expected'];
                $actualVal = is_bool($mismatch['actual']) ? 
                    ($mismatch['actual'] ? 'true' : 'false') : ($mismatch['actual'] ?? 'null');
                
                echo "  ✗ {$key}: expected {$expectedVal}, got {$actualVal} ";
                echo "[{$mismatch['severity']}]\n";
            }
            echo "\n";
            
            // 修正提案
            $fix = $this->suggestFix($environmentName);
            if ($fix['needs_fix']) {
                echo "Suggested Fix:\n";
                echo $fix['complete_fix_code'] . "\n";
            }
        }
    }
}

// 使用例
echo "=== 環境別設定検証 ===\n";

$validator = new EnvironmentConfigValidator();
$validator->setupDefaultEnvironments();

// 本番環境用の設定を適用
session_set_cookie_params([
    'lifetime' => 7200,
    'path' => '/',
    'domain' => '',
    'secure' => true,
    'httponly' => true,
    'samesite' => 'Strict'
]);

// 本番環境として検証
echo "\n";
$validator->displayValidationReport('production');

// 環境を推測
echo "\n=== 環境検出 ===\n";
$detected = $validator->detectEnvironment();
echo "検出された環境: {$detected['detected_environment']}\n";
echo "確信度: {$detected['confidence']}%\n\n";

// すべての環境のスコア
echo "全環境のマッチング:\n";
foreach ($detected['all_scores'] as $env => $score) {
    echo "  {$env}: {$score['score']}/{$score['total']} ({$score['percentage']}%)\n";
}

// 間違った設定でテスト
session_set_cookie_params([
    'lifetime' => 0,
    'path' => '/',
    'domain' => '',
    'secure' => false,  // 本番環境には不適切
    'httponly' => true,
    'samesite' => 'Lax'  // 本番環境には不適切
]);

echo "\n";
$validator->displayValidationReport('production');

例6: リアルタイムモニタリングシステム

class CookieParamsMonitor {
    private $alertThresholds = [];
    private $listeners = [];
    
    /**
     * モニターを初期化
     */
    public function __construct() {
        $this->setDefaultThresholds();
    }
    
    /**
     * デフォルトのしきい値を設定
     */
    private function setDefaultThresholds() {
        $this->alertThresholds = [
            'secure' => ['expected' => true, 'alert_if_different' => true],
            'httponly' => ['expected' => true, 'alert_if_different' => true],
            'samesite' => ['expected' => 'Strict', 'alert_if_different' => false],
            'lifetime' => ['max' => 86400, 'alert_if_exceeded' => true]
        ];
    }
    
    /**
     * しきい値を設定
     */
    public function setThreshold($param, $threshold) {
        $this->alertThresholds[$param] = $threshold;
    }
    
    /**
     * リスナーを追加
     */
    public function addListener($callback) {
        $this->listeners[] = $callback;
    }
    
    /**
     * モニタリングを実行
     */
    public function monitor() {
        $params = session_get_cookie_params();
        $alerts = [];
        
        foreach ($this->alertThresholds as $param => $threshold) {
            if (!isset($params[$param])) {
                continue;
            }
            
            $alert = $this->checkThreshold($param, $params[$param], $threshold);
            
            if ($alert !== null) {
                $alerts[] = $alert;
                $this->notifyListeners($alert);
            }
        }
        
        return [
            'timestamp' => time(),
            'params' => $params,
            'alerts' => $alerts,
            'alert_count' => count($alerts)
        ];
    }
    
    /**
     * しきい値をチェック
     */
    private function checkThreshold($param, $value, $threshold) {
        // 期待値チェック
        if (isset($threshold['expected']) && isset($threshold['alert_if_different'])) {
            if ($value !== $threshold['expected'] && $threshold['alert_if_different']) {
                return [
                    'type' => 'value_mismatch',
                    'param' => $param,
                    'expected' => $threshold['expected'],
                    'actual' => $value,
                    'severity' => 'warning',
                    'message' => "{$param} is {$value}, expected {$threshold['expected']}"
                ];
            }
        }
        
        // 最大値チェック
        if (isset($threshold['max']) && isset($threshold['alert_if_exceeded'])) {
            if ($value > $threshold['max'] && $threshold['alert_if_exceeded']) {
                return [
                    'type' => 'threshold_exceeded',
                    'param' => $param,
                    'max' => $threshold['max'],
                    'actual' => $value,
                    'severity' => 'info',
                    'message' => "{$param} ({$value}) exceeds maximum ({$threshold['max']})"
                ];
            }
        }
        
        return null;
    }
    
    /**
     * リスナーに通知
     */
    private function notifyListeners($alert) {
        foreach ($this->listeners as $listener) {
            call_user_func($listener, $alert);
        }
    }
    
    /**
     * 継続的モニタリング
     */
    public function continuousMonitor($duration = 60, $interval = 5) {
        $endTime = time() + $duration;
        $results = [];
        
        echo "Starting continuous monitoring for {$duration} seconds...\n\n";
        
        while (time() < $endTime) {
            $result = $this->monitor();
            $results[] = $result;
            
            echo "[" . date('H:i:s') . "] ";
            
            if ($result['alert_count'] > 0) {
                echo "ALERTS: {$result['alert_count']}\n";
                foreach ($result['alerts'] as $alert) {
                    echo "  [{$alert['severity']}] {$alert['message']}\n";
                }
            } else {
                echo "OK - No alerts\n";
            }
            
            sleep($interval);
        }
        
        return [
            'total_checks' => count($results),
            'total_alerts' => array_sum(array_column($results, 'alert_count')),
            'results' => $results
        ];
    }
    
    /**
     * ヘルスチェック
     */
    public function healthCheck() {
        $params = session_get_cookie_params();
        $score = 0;
        $maxScore = 100;
        $issues = [];
        
        // Secure(30点)
        if ($params['secure']) {
            $score += 30;
        } else {
            $issues[] = 'Secure attribute is disabled';
        }
        
        // HttpOnly(30点)
        if ($params['httponly']) {
            $score += 30;
        } else {
            $issues[] = 'HttpOnly attribute is disabled';
        }
        
        // SameSite(25点)
        if (isset($params['samesite']) && in_array($params['samesite'], ['Strict', 'Lax'])) {
            $score += 25;
        } else {
            $issues[] = 'SameSite attribute is not properly set';
        }
        
        // Lifetime(15点)
        if ($params['lifetime'] > 0 && $params['lifetime'] <= 86400) {
            $score += 15;
        } else {
            $issues[] = 'Lifetime is not optimal';
        }
        
        $health = 'critical';
        if ($score >= 85) {
            $health = 'excellent';
        } elseif ($score >= 70) {
            $health = 'good';
        } elseif ($score >= 50) {
            $health = 'fair';
        }
        
        return [
            'health' => $health,
            'score' => $score,
            'max_score' => $maxScore,
            'percentage' => round(($score / $maxScore) * 100, 2),
            'issues' => $issues,
            'params' => $params
        ];
    }
    
    /**
     * ダッシュボードを表示
     */
    public function displayDashboard() {
        $health = $this->healthCheck();
        $monitoring = $this->monitor();
        
        echo "=== Cookie Parameters Dashboard ===\n\n";
        
        echo "Health Status: " . strtoupper($health['health']) . "\n";
        echo "Score: {$health['score']}/{$health['max_score']} ({$health['percentage']}%)\n\n";
        
        echo "Current Parameters:\n";
        foreach ($health['params'] as $key => $value) {
            $displayValue = is_bool($value) ? ($value ? 'true' : 'false') : $value;
            echo "  {$key}: {$displayValue}\n";
        }
        echo "\n";
        
        if (!empty($health['issues'])) {
            echo "Issues:\n";
            foreach ($health['issues'] as $issue) {
                echo "  ⚠ {$issue}\n";
            }
            echo "\n";
        }
        
        if ($monitoring['alert_count'] > 0) {
            echo "Active Alerts:\n";
            foreach ($monitoring['alerts'] as $alert) {
                echo "  [{$alert['severity']}] {$alert['message']}\n";
            }
        } else {
            echo "✓ No active alerts\n";
        }
    }
}

// 使用例
echo "=== リアルタイムモニタリング ===\n";

$monitor = new CookieParamsMonitor();

// リスナーを追加
$monitor->addListener(function($alert) {
    error_log("Cookie Alert: [{$alert['severity']}] {$alert['message']}");
});

// カスタムしきい値を設定
$monitor->setThreshold('lifetime', [
    'max' => 3600,
    'alert_if_exceeded' => true
]);

// ダッシュボード表示
$monitor->displayDashboard();

// モニタリング実行
echo "\n=== モニタリング結果 ===\n";
$result = $monitor->monitor();
echo "アラート数: {$result['alert_count']}\n";
foreach ($result['alerts'] as $alert) {
    echo "  {$alert['message']}\n";
}

// ヘルスチェック
echo "\n=== ヘルスチェック ===\n";
$health = $monitor->healthCheck();
echo "健全性: {$health['health']}\n";
echo "スコア: {$health['percentage']}%\n";

// 継続的モニタリング(コメントアウト)
// echo "\n=== 継続的モニタリング ===\n";
// $continuous = $monitor->continuousMonitor(30, 5);
// echo "総チェック数: {$continuous['total_checks']}\n";
// echo "総アラート数: {$continuous['total_alerts']}\n";

セキュリティベストプラクティス

// 推奨されるセキュアな設定

// 本番環境(HTTPS必須)
session_set_cookie_params([
    'lifetime' => 7200,        // 2時間
    'path' => '/',
    'domain' => '',
    'secure' => true,          // HTTPS必須
    'httponly' => true,        // JavaScript無効
    'samesite' => 'Strict'     // CSRF防止
]);

// 設定を確認
$params = session_get_cookie_params();

// セキュリティチェック
$isSecure = $params['secure'] && 
            $params['httponly'] && 
            isset($params['samesite']) && 
            $params['samesite'] === 'Strict';

if (!$isSecure) {
    trigger_error('Insecure cookie configuration', E_USER_WARNING);
}

パラメータの意味と推奨値

// 各パラメータの詳細

// lifetime(有効期限)
// 0 = ブラウザ終了まで(推奨:セキュリティ重視)
// 3600 = 1時間(推奨:一般的なアプリ)
// 7200 = 2時間(推奨:通常の業務アプリ)
// 86400 = 24時間(長期セッション)

// path(有効パス)
// '/' = サイト全体(デフォルト、推奨)
// '/admin' = 管理画面のみ
// '/api' = API専用

// domain(有効ドメイン)
// '' = 現在のホストのみ(推奨)
// '.example.com' = サブドメイン含む(注意が必要)

// secure(HTTPS限定)
// true = HTTPS接続でのみ送信(本番環境推奨)
// false = HTTPでも送信(開発環境のみ)

// httponly(JavaScript無効)
// true = JavaScriptからアクセス不可(強く推奨)
// false = JavaScriptからアクセス可能(XSSリスク)

// samesite(CSRF対策)
// 'Strict' = 最も厳格(推奨:高セキュリティ)
// 'Lax' = バランス型(推奨:一般的)
// 'None' = 制限なし(Secure必須、特殊用途のみ)
// '' = 未設定(非推奨)

まとめ

session_get_cookie_params()関数の特徴をまとめると:

できること:

  • セッションクッキーパラメータの取得
  • セキュリティ設定の確認
  • 現在の設定の監査
  • 設定の検証とデバッグ

返される配列の構造:

[
    'lifetime' => int,      // 有効期限(秒)
    'path' => string,       // 有効パス
    'domain' => string,     // 有効ドメイン
    'secure' => bool,       // HTTPS限定
    'httponly' => bool,     // JavaScript無効
    'samesite' => string    // CSRF対策(PHP 7.3.0以降)
]

重要なセキュリティパラメータ:

  • secure: HTTPS接続でのみ送信(本番環境必須)
  • httponly: JavaScriptからアクセス不可(XSS対策)
  • samesite: CSRF攻撃防止(Strict/Lax推奨)

推奨される使用場面:

  • セキュリティ監査
  • 環境別設定の検証
  • 設定の確認とデバッグ
  • コンプライアンスチェック
  • 設定変更の履歴管理

session_set_cookie_params()との組み合わせ:

// 設定を変更
session_set_cookie_params([
    'secure' => true,
    'httponly' => true,
    'samesite' => 'Strict'
]);

// 変更を確認
$params = session_get_cookie_params();
var_dump($params);

ベストプラクティス:

// 1. 設定前の確認
$before = session_get_cookie_params();

// 2. セキュアな設定を適用
session_set_cookie_params([
    'lifetime' => 7200,
    'path' => '/',
    'domain' => '',
    'secure' => true,
    'httponly' => true,
    'samesite' => 'Strict'
]);

// 3. 設定後の検証
$after = session_get_cookie_params();
assert($after['secure'] === true);
assert($after['httponly'] === true);
assert($after['samesite'] === 'Strict');

// 4. セキュリティチェック
function validateCookieParams() {
    $params = session_get_cookie_params();
    
    $issues = [];
    
    if (!$params['secure']) {
        $issues[] = 'Secure attribute is disabled';
    }
    
    if (!$params['httponly']) {
        $issues[] = 'HttpOnly attribute is disabled';
    }
    
    if (!isset($params['samesite']) || empty($params['samesite'])) {
        $issues[] = 'SameSite attribute is not set';
    }
    
    return empty($issues);
}

if (!validateCookieParams()) {
    trigger_error('Insecure cookie configuration detected', E_USER_WARNING);
}

環境別推奨設定:

// 開発環境
$dev = [
    'lifetime' => 0,
    'secure' => false,      // HTTPでも動作
    'httponly' => true,
    'samesite' => 'Lax'
];

// ステージング環境
$staging = [
    'lifetime' => 3600,
    'secure' => true,
    'httponly' => true,
    'samesite' => 'Lax'
];

// 本番環境
$production = [
    'lifetime' => 7200,
    'secure' => true,       // 必須
    'httponly' => true,     // 必須
    'samesite' => 'Strict'  // 推奨
];

関連関数:

  • session_set_cookie_params(): クッキーパラメータを設定
  • session_name(): セッション名を取得/設定
  • session_id(): セッションIDを取得/設定
  • ini_get(): PHP設定を取得

PHP設定との関係:

// session_get_cookie_params()の値は以下のPHP設定に対応
ini_get('session.cookie_lifetime');  // lifetime
ini_get('session.cookie_path');      // path
ini_get('session.cookie_domain');    // domain
ini_get('session.cookie_secure');    // secure
ini_get('session.cookie_httponly');  // httponly
ini_get('session.cookie_samesite');  // samesite (PHP 7.3.0以降)

session_get_cookie_params()は、セッションクッキーの設定を確認するための重要な関数です。セキュリティ監査、設定検証、デバッグに活用して、安全なセッション管理を実現しましょう!

タイトルとURLをコピーしました