CSRF(Cross-Site Request Forgery)攻擊者誘導受害者在已登入的目標網站執行非預期操作(刪除資料、修改密碼、發文、轉帳)。因瀏覽器會自動攜帶目標域的 Cookie,伺服器無法分辨請求是否為使用者自願發起。
<img src="https://bank.com/transfer?to=attacker&amount=1000">,受害者瀏覽含此圖的頁面即觸發 GET 請求。form.submit() 發送 POST 請求。<a href="https://forum.com/delete?id=123">看貓咪圖片</a>。每個表單/狀態改變請求攜帶不可預測、使用者專屬、一次性的 Token,伺服端驗證 Token 有效性。
<?php
class CsrfToken
{
const SALT = 'your-application-specific-random-salt-change-in-production';
const TTL = 7200; // 2 小時
/**
* 產生 Token
* @param string|int $userId 使用者唯一識別
* @param string $action 動作標識(可選,區分不同表單)
* @return string 10 字元 Token
*/
public static function generate($userId, $action = '')
{
$timeSlot = ceil(time() / self::TTL);
$data = $timeSlot . '|' . $action . '|' . $userId;
return substr(hash_hmac('sha256', $data, self::SALT), 0, 10);
}
/**
* 驗證 Token(允許前一個時段,避免邊界失效)
*/
public static function validate($userId, $token, $action = '')
{
$currentSlot = ceil(time() / self::TTL);
// 當前時段
if (self::generate($userId, $action) === $token) {
return true;
}
// 前一個時段(緩衝)
$prevData = ($currentSlot - 1) . '|' . $action . '|' . $userId;
if (substr(hash_hmac('sha256', $prevData, self::SALT), 0, 10) === $token) {
return true;
}
return false;
}
}
?>
<!-- 表單頁面 -->
<form method="post" action="/delete_post.php">
<input type="hidden" name="csrf_token" value="<?php echo CsrfToken::generate($currentUserId, 'delete_post'); ?>">
<input type="hidden" name="post_id" value="123">
<button type="submit">刪除文章</button>
</form>
<?php
// delete_post.php 處理端
session_start();
$userId = $_SESSION['user_id'] ?? 0;
$token = $_POST['csrf_token'] ?? '';
if (!CsrfToken::validate($userId, $token, 'delete_post')) {
http_response_code(403);
die('CSRF Token 驗證失敗,請重新整理頁面再試。');
}
// Token 通過,執行刪除邏輯
$postId = (int)($_POST['post_id'] ?? 0);
// ... 刪除資料庫記錄
echo '刪除成功';
?>
| 層級 | 措施 | 適用場景 |
|---|---|---|
| 基礎 | 同步 Token + SameSite Cookie | 所有狀態改變操作 |
| 強化 | 雙重提交 Cookie(Token 同時存在 Cookie 與表單,JS 讀取 Cookie 寫入 Header) | API、SPA、需防禦 Subdomain 攻擊 |
| 最高 | 自定義 HTTP Header(X-Requested-With: XMLHttpRequest)+ Token,伺服端檢查 Header 存在 | 純 AJAX 介面,瀏覽器同源策略天然阻擋跨域自定 Header |
@csrf Blade 指令、VerifyCsrfToken 中介層自動驗證。csrf_token() Twig 函式、CsrfTokenManager 服務。ActiveForm 自動渲染隱藏 _csrf、Request::validateCsrfToken()。{:token()} 模板標籤、Request::token() 驗證。$_SERVER['HTTP_REFERER']:Referer 可偽造、隱私模式下不送出。