跨站請求偽造 (CSRF) 是常見的 Web 安全漏洞。攻擊者誘導已登入用戶訪問惡意頁面,在用戶不知情下向目標站點發送偽造請求。防禦核心在於「驗證請求來源」,最經典做法是使用 同步器 Token 模式:伺服器產生唯一 Token 存入 Session,表單攜帶 Token 提交,伺服器比對驗證。本文示範 PHP 實作該機制的完整流程。
$_SESSION['token'] 並嵌入表單隱藏欄位。$_POST['token'] 與 $_SESSION['token'] 是否一致。<?php
session_start();
// 1. 處理表單提交
if (isset($_POST['check']) && !empty($_POST['name'])) {
// 驗證 Token
if (hash_equals($_SESSION['check'] ?? '', $_POST['check'])) {
echo "正常訪問:Token 驗證通過";
// 實際業務邏輯:儲存資料、發送郵件等
} else {
echo "外部訪問:Token 驗證失敗,疑似 CSRF 攻擊";
// 記錄日誌、封鎖 IP 等防禦措施
}
}
// 2. 產生新 Token(每次請求更新,或僅在表單頁產生)
$token = bin2hex(random_bytes(32)); // 密碼學安全隨機字串
$_SESSION['check'] = $token;
?>
<!DOCTYPE html>
<html>
<head><meta charset="UTF-8"><title>CSRF Token 示例</title></head>
<body>
<h2>提交表單</h2>
<form method="post" action="">
<label>姓名:<input type="text" name="name" required></label><br><br>
<input type="hidden" name="check" value="<?php echo $token; ?>">
<button type="submit">送出</button>
</form>
</body>
</html>
hash_equals() 防止時序攻擊直接用 == 或 === 比較字串會因長度不同提早返回,導致時序側通道洩露資訊。hash_equals() 以恆定時間比較,避免此風險。
random_bytes() / openssl_random_pseudo_bytes() 產生高熵值隨機字串。md5(uniqid(rand(), true)) 等舊式做法,熵值不足且可被預測。在 session_set_cookie_params() 或 php.ini 設定 session.cookie_samesite = Strict (或 Lax),瀏覽器將不在跨站請求中攜帶 Session Cookie,進一步降低 CSRF 風險。
在現代框架 (Laravel, Symfony, ThinkPHP) 中,CSRF 防護通常內建於中介層,自動為所有非 GET 請求驗證 Token。若使用原生 PHP,可建立 csrf_verify() 函數並在所有需保護的入口檔案 require:
<?php
function csrf_verify() {
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
session_start();
if (!hash_equals($_SESSION['csrf_token'] ?? '', $_POST['csrf_token'] ?? '')) {
http_response_code(403);
exit('CSRF token validation failed');
}
}
}
// 在每個處理 POST 的腳本最開頭呼叫
csrf_verify();
?>
Token 驗證是防禦 CSRF 最有效、成本最低的手段。關鍵在於:安全隨機產生、Session 端儲存、恆定時間比對、SameSite Cookie 輔助。將此機制封裝為可複用的函數或中介層,即可為應用程式建立堅實的第一道防線。