使用者重複點擊提交、瀏覽器重新整理、雙擊按鈕等行為會導致表單重複提交,造成資料重複、扣款重複等嚴重問題。本文實作基於 Session Token 的一次性驗證機制,輕量、有效、易整合。
$_POST['token'] 與 $_SESSION['token'];<?php
session_start();
// 處理提交
if (isset($_GET['action']) && $_GET['action'] === 'save') {
// 驗證 Token
if (isset($_SESSION['__form_token']) &&
isset($_POST['token']) &&
$_SESSION['__form_token'] === $_POST['token']) {
// Token 有效,處理業務
print_r($_POST); // 實際處理:存資料庫、發信等
// 關鍵:銷毀 Token,防止重複提交
$_SESSION['__form_token'] = null;
} else {
// Token 無效/重複提交,導回表單
header("Location: form.php");
}
exit();
}
// 生成新 Token 給表單頁
$token = $_SESSION['__form_token'] = microtime(true) . '_' . mt_rand();
?>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>防重複提交示範</title>
</head>
<body>
<form action="form.php?action=save" method="post">
<input type="hidden" name="token" value="<?php echo $token; ?>" />
<label>用戶名: <input type="text" name="username" /></label><br>
<label>密碼: <input type="password" name="password" /></label><br>
<input type="submit" value="提交" />
</form>
</body>
</html>
microtime(true) . '_' . mt_rand() 產生高熵值,極難碰撞;/get_token.php 取得 Token 填入隱藏欄位,或改用 JS 生成並同步至 Session(需後端 API 支援)。用陣列儲存多組 Token:$_SESSION['__form_tokens'][$form_name] = $token;
// 概念代碼
class CsrfMiddleware {
public function handle($request, $next) {
if ($request->isPost()) {
$token = $request->input('_token');
if (!$token || $token !== $request->session()->pull('_token')) {
return response('Invalid Token', 403);
}
}
return $next($request);
}
}
| 策略 | 優點 | 缺點 |
|---|---|---|
| Session Token (本文) | 可靠、支援多標籤、無 JS 依賴 | 需 Session、靜態頁需 AJAX |
| JS 禁用按鈕 | 簡單、即時反饋 | 可繞過、無法防重整 |
| Post-Redirect-Get (PRG) | 防重整重複、SEO 友善 | 不防雙擊、需配合 Token |
| 資料庫唯一約束 | 根治資料層重複 | 業務邏輯複雜時難定義唯一鍵 |
建議採用 Token + PRG + 資料庫約束 多層防護,徹底杜絕重複提交風險。