$_SESSION['uniqid'] = $_uniqid = sha1(uniqid(rand(), true));
既然 `$_SESSION['uniqid']` 與 `$_uniqid` 賦值時相等,為何驗證時還要比較兩者是否一致?`$_uniqid` 是表單隱藏域的值。
這不是多餘,而是經典的 同步器令牌模式(Synchronizer Token Pattern) 核心邏輯:
| 攻擊場景 | 若不比較會發生什麼 | 比較後的防護效果 |
|---|---|---|
| 用戶重複點擊提交 | 後端處理多次業務邏輯(如重複扣款) | 第二次提交 Token 已失效/不匹配,攔截 |
| CSRF 跨站請求偽造 | 攻擊者構造惡意表單,受害者不知情提交 | 攻擊者無法獲取受害者 Session 中的 Token |
| 表單重放攻擊 | 截獲請求重複發送 | 一次性 Token 機制使重放失效 |
<?php
// 生成頁面時
session_start();
$token = bin2hex(random_bytes(32)); // 密碼學安全隨機數
$_SESSION['csrf_token'] = $token;
?>
<form method="POST">
<input type="hidden" name="csrf_token" value="<?php echo $token; ?>">
<!-- 其他表單欄位 -->
</form>
<?php
// 處理提交時
session_start();
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$submitted = $_POST['csrf_token'] ?? '';
$stored = $_SESSION['csrf_token'] ?? '';
// 使用 hash_equals 防止時序攻擊
if (!hash_equals($stored, $submitted)) {
die('Token 驗證失敗,請重新提交');
}
// 驗證通過,執行業務邏輯
// ...
// 可選:一次性 Token,驗證後銷毀
unset($_SESSION['csrf_token']);
}
?>
random_bytes() 而非 uniqid(rand()),後者熵值不足hash_equals() 恆定時間比較,防範側通道攻擊