首頁 >後端開發 >php教程 >PHP 中的 CSRF 保護

PHP 中的 CSRF 保護

Patricia Arquette
Patricia Arquette原創
2024-12-09 04:41:11377瀏覽

CSRF Protection in PHP

什麼是 CSRF?

跨站請求偽造 (CSRF) 是一種網路安全漏洞,攻擊者可以利用該漏洞誘騙經過身份驗證的用戶在他們目前登入的網站上執行不需要的操作。此攻擊透過利用網站所擁有的信任來進行在使用者的瀏覽器中。

CSRF 攻擊如何運作

  1. 使用者登入合法網站 A 並收到會話 cookie
  2. 使用者在仍登入 A 的情況下造訪惡意網站 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;
}

交響樂範例

// 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中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn