CI4中防重复提交需自行实现一次性防重token:表单渲染前用bin2hex(random_bytes(16))生成并存入session,提交时用hash_equals()校验后立即remove(),不可复用csrf_hash()。

CI4 中必须用 session + csrf_hash() 配合隐藏字段
CodeIgniter 4 默认开启 CSRF 保护,但仅靠它不能防重复提交——它只校验请求来源合法性,不保证 token 一次性。要真正拦截重复提交,得在表单里加一个服务端生成、一次性的防重 token,并在控制器中手动验证和消耗它。
关键点:CI4 没有内置的 antiReplayToken 机制,需自行实现。推荐把 token 存进 session(非 $_SESSION 原生数组,而是 CI4 的 $this->session 实例),并用 session()->remove() 显式删除。
- 不要复用
csrf_token()或csrf_hash()值做防重——它们默认不过期,且可能被多请求并发读取 - token 字符串建议用
bin2hex(random_bytes(16))生成,避免依赖uuid扩展 - 必须在表单渲染时写入 session,不能等到提交时才生成
$this->request->getPost('antiReplayToken') 提交后立即校验并清除
控制器中收到 POST 后,第一件事不是验证业务规则,而是取出并比对防重 token。匹配成功即刻从 session 删除,后续同值请求必然失败。
示例逻辑片段:
if ($this->request->getMethod() === 'post') {
$submittedToken = $this->request->getPost('antiReplayToken');
$storedToken = $this->session->get('form_token');
if (empty($storedToken) || ! hash_equals($storedToken, $submittedToken)) {
return redirect()->back()->with('error', '非法或重复提交');
}
// ✅ 消耗 token
$this->session->remove('form_token');
// 后续走正常验证和保存流程...
}
- 务必用
hash_equals()比较字符串,防止时序攻击 - 如果验证失败,不要再调用
$this->session->remove(),否则会清空合法用户的 token - 错误响应必须用
redirect()->back(),不能直接return view(),否则表单仍带旧数据
表单模板里用 <input type="hidden" name="antiReplayToken" value="<?= session()->get('form_token') ?? '' ?>">
渲染表单前,控制器需先确保 session 中存在 token:
public function create()
{
// 若 session 无 token,则生成一个并存入
if (! session()->has('form_token')) {
session()->set('form_token', bin2hex(random_bytes(16)));
}
return view('user/create');
}
模板中直接输出即可,无需额外 JS 注入或动态生成。
- 不要用
old()辅助函数填充该字段——它只缓存用户输入,不处理自定义 token - 如果用户开多个标签页访问同一表单,每个页面会拿到不同 token,互不影响;但同一页面刷新会复用 token,所以刷新后首次提交有效,二次提交失败
- 若需支持“刷新后仍可提交一次”,可在生成新 token 前先清理旧的:
session()->remove('form_token')
为什么不能只靠 redirect('success') 解决所有问题
PRG 模式(Post-Redirect-Get)确实能防止 F5 刷新重复提交,但它解决的是「浏览器行为层面」的重复,不是「业务逻辑层面」的幂等性。比如用户用 Postman 连发 5 次请求、或脚本绕过前端按钮禁用,PRG 完全无效。
- PRG 是防御链的末端,防重 token 是前端入口的第一道业务闸门
- 数据库唯一索引(如
UNIQUE(email))是最后一道兜底,但报错回滚用户体验差,且无法区分“重复提交”和“真实冲突” - 三者必须共存:按钮禁用(UX 层)+ 防重 token(应用层)+ PRG + 唯一约束(存储层)
最容易被忽略的是 token 的生命周期管理:生成时机、存储位置、清除时机,三者缺一不可。漏掉任意一环,token 就退化成摆设。


















