PHP での CSRF 保護

Patricia Arquette
Patricia Arquetteオリジナル
2024-12-09 04:41:11359ブラウズ

CSRF Protection in PHP

CSRFとは何ですか?

クロスサイト リクエスト フォージェリ (CSRF) は、攻撃者が認証されたユーザーをだまして、現在ログインしている Web サイト上で望ましくないアクションを実行させる Web セキュリティの脆弱性です。この攻撃は、Web サイトが持つ信頼を悪用することによって機能します。ユーザーのブラウザで。

CSRF 攻撃の仕組み

  1. ユーザーは正規の Web サイト A にログインし、セッション Cookie を受け取ります
  2. ユーザーが A にログインしたまま悪意のある Web サイト B にアクセスします
  3. ウェブサイト B には、ウェブサイト A にリクエストを行うコードが含まれています
  4. ブラウザにはセッション Cookie が自動的に組み込まれます
  5. ウェブサイト A はリクエストが正当であると考えて処理します

PHP での CSRF 保護メソッド

1. 隠し入力を使用したトークンベースの保護

これが最も一般的な方法です。実装方法は次のとおりです:

// In your session initialization (e.g., at login)
session_start();
if (empty($_SESSION['csrf_token'])) {
    $_SESSION['csrf_token'] = bin2hex(random_bytes(32));
}

// In your form
function generateFormWithCSRFToken() {
    return '<form method="POST" action="/submit">
        <input type="hidden" name="csrf_token" value="' . $_SESSION['csrf_token'] . '">
        <!-- rest of your form fields -->
        <input type="submit" value="Submit">
    </form>';
}

// In your form processing
function validateCSRFToken() {
    if (!isset($_POST['csrf_token']) || !isset($_SESSION['csrf_token']) ||
        !hash_equals($_SESSION['csrf_token'], $_POST['csrf_token'])) {
        die('CSRF token validation failed');
    }
    return true;
}

2. カスタムヘッダーを使用した CSRF 保護

このメソッドは、カスタム ヘッダーを持つ AJAX リクエストを使用します:

// PHP Backend
session_start();
if (empty($_SESSION['csrf_token'])) {
    $_SESSION['csrf_token'] = bin2hex(random_bytes(32));
}

// Validate the token
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    $headers = getallheaders();
    if (!isset($headers['X-CSRF-Token']) || 
        !hash_equals($_SESSION['csrf_token'], $headers['X-CSRF-Token'])) {
        http_response_code(403);
        die('CSRF token validation failed');
    }
}

// JavaScript Frontend
const csrfToken = '<?php echo $_SESSION["csrf_token"]; ?>';

fetch('/api/endpoint', {
    method: 'POST',
    headers: {
        'X-CSRF-Token': csrfToken,
        'Content-Type': 'application/json'
    },
    body: JSON.stringify(data)
});

3. 二重送信 Cookie パターン

このメソッドには、トークンを Cookie とリクエスト パラメーターの両方として送信することが含まれます。

// Set both cookie and session token
session_start();
$token = bin2hex(random_bytes(32));
$_SESSION['csrf_token'] = $token;
setcookie('csrf_token', $token, [
    'httponly' => true,
    'secure' => true,
    'samesite' => 'Strict'
]);

// Validation function
function validateDoubleSubmitToken() {
    if (!isset($_COOKIE['csrf_token']) || 
        !isset($_POST['csrf_token']) || 
        !isset($_SESSION['csrf_token'])) {
        return false;
    }

    return hash_equals($_COOKIE['csrf_token'], $_POST['csrf_token']) && 
           hash_equals($_SESSION['csrf_token'], $_POST['csrf_token']);
}

4. SameSite Cookie 属性

最新のアプリケーションでは、追加の保護層として SameSite Cookie 属性を使用することもできます。

// Set cookie with SameSite attribute
session_start();
session_set_cookie_params([
    'lifetime' => 0,
    'path' => '/',
    'domain' => $_SERVER['HTTP_HOST'],
    'secure' => true,
    'httponly' => true,
    'samesite' => 'Strict'
]);

CSRF 保護のベスト プラクティス

  1. トークンの生成
    • 暗号的に安全な乱数生成器を使用する
    • トークンを十分な長さ (少なくとも 32 バイト) にします
    • セッションごとに新しいトークンを生成します
function generateSecureToken($length = 32) {
    return bin2hex(random_bytes($length));
}
  1. トークンの検証
    • タイミングセーフな比較関数を使用する
    • トークンの存在と価値を検証します
    • 適切なエラー処理を実装する
function validateToken($userToken, $storedToken) {
    if (empty($userToken) || empty($storedToken)) {
        return false;
    }
    return hash_equals($storedToken, $userToken);
}
  1. フォームの実装
    • すべての形式のトークンを含めます
    • 自動トークンインジェクションを実装する
    • トークンのローテーションを処理します
class CSRFProtection {
    public static function getTokenField() {
        return sprintf(
            '<input type="hidden" name="csrf_token" value="%s">',
            htmlspecialchars($_SESSION['csrf_token'])
        );
    }
}

フレームワーク固有の保護

多くの PHP フレームワークは、組み込みの CSRF 保護を提供します。

Laravel の例

// In your session initialization (e.g., at login)
session_start();
if (empty($_SESSION['csrf_token'])) {
    $_SESSION['csrf_token'] = bin2hex(random_bytes(32));
}

// In your form
function generateFormWithCSRFToken() {
    return '<form method="POST" action="/submit">
        <input type="hidden" name="csrf_token" value="' . $_SESSION['csrf_token'] . '">
        <!-- rest of your form fields -->
        <input type="submit" value="Submit">
    </form>';
}

// In your form processing
function validateCSRFToken() {
    if (!isset($_POST['csrf_token']) || !isset($_SESSION['csrf_token']) ||
        !hash_equals($_SESSION['csrf_token'], $_POST['csrf_token'])) {
        die('CSRF token validation failed');
    }
    return true;
}

Symfony の例

// PHP Backend
session_start();
if (empty($_SESSION['csrf_token'])) {
    $_SESSION['csrf_token'] = bin2hex(random_bytes(32));
}

// Validate the token
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    $headers = getallheaders();
    if (!isset($headers['X-CSRF-Token']) || 
        !hash_equals($_SESSION['csrf_token'], $headers['X-CSRF-Token'])) {
        http_response_code(403);
        die('CSRF token validation failed');
    }
}

// JavaScript Frontend
const csrfToken = '<?php echo $_SESSION["csrf_token"]; ?>';

fetch('/api/endpoint', {
    method: 'POST',
    headers: {
        'X-CSRF-Token': csrfToken,
        'Content-Type': 'application/json'
    },
    body: JSON.stringify(data)
});

避けるべきよくある落とし穴

  1. 予測可能なトークンを使用しないでください
  2. グローバルにアクセス可能な JavaScript 変数にトークンを保存しないでください
  3. AJAX リクエストの CSRF 保護をスキップしないでください
  4. Referer ヘッダーのチェックだけに依存しないでください
  5. 複数のフォームに同じトークンを使用しないでください

CSRF 保護は、Web アプリケーションのセキュリティにとって非常に重要です。 CSRF 保護を実装するには複数のアプローチがありますが、非表示のフォーム フィールドを使用するトークンベースのアプローチが、依然として最も広く使用されており、信頼性の高い方法です。 PHP アプリケーションに CSRF 保護を実装する場合は、セキュリティを強化するためにさまざまな保護方法を組み合わせ、常にセキュリティのベスト プラクティスに従ってください。

CSRF 保護は、適切なセッション管理、安全な Cookie 処理、入力検証を含む、より広範なセキュリティ戦略の一部である必要があることに注意してください。

以上がPHP での CSRF 保護の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。