CSRF令牌验证失败主因是未正确传递token:表单缺失csrf_token_name隐藏字段或AJAX未在data/header中携带;需确保form辅助函数已加载、用form_open()自动生成字段或手动添加,且每次提交使用实时get_csrf_hash()值。

CSRF 令牌验证失败,基本就是 $config['csrf_protection'] = TRUE 开启后,表单没带或带错了 csrf_token_name 对应的隐藏字段,或者 AJAX 请求没传对 header / data。
表单提交时 missing csrf_token_name 字段
CodeIgniter 默认用 csrf_token_name 作为隐藏字段名(不是固定叫 csrf_test_name,除非你改过配置),且值必须是当前会话有效的 csrf_cookie_name 值。常见错误是手写表单但漏掉这个字段,或用了 form_open() 却没加载 form 辅助函数。
- 确认已加载
form辅助函数:$this->load->helper('form'); - 用
form_open()自动生成表单,它会自动注入隐藏字段:echo form_open('login'); - 若手动写表单,必须显式添加:
<input type="hidden" name="<?= $this->security->get_csrf_token_name(); ?>" value="<?= $this->security->get_csrf_hash(); ?>"> - 注意:每次刷新页面,
get_csrf_hash()返回值都会变(除非配置了csrf_regenerate = FALSE)
AJAX 请求被拒绝:403 Forbidden 或空白响应
默认情况下,CI 的 CSRF 验证只检查 POST 数据里的 token 字段,不检查请求头。所以 jQuery/Axios 发送 JSON 或 FormData 时,如果没把 token 放进 data 或 header,就会失败。
- 推荐方式:把 token 加到请求体(适用于
application/x-www-form-urlencoded或FormData):data: { [csrf_name]: csrf_hash, ... } - 若用 JSON,需在控制器里临时放宽验证(不推荐)或改用
$_SERVER['HTTP_X_CSRF_TOKEN']方式 —— 但这需要自己在前端设 header,并在Security.php中扩展逻辑 - 更稳妥的做法:在全局 JS 初始化时读取隐藏字段值,再统一 inject 到所有 AJAX 的
data或headers中 - 别依赖
$.ajaxSetup()自动加 data,因为 GET 请求不该带 CSRF,容易误触发
CSRF cookie 过期或未写入导致 hash 不匹配
CI 把 token hash 存在 session(或 cookie,取决于 csrf_cookie 配置),同时把同名字段写入 cookie(csrf_cookie_name)。两者不一致就验证失败,典型现象是“刚打开页面第一次提交成功,刷新后再提交就 403”。
- 检查
$config['csrf_cookie_name']和$config['csrf_token_name']是否不同名(必须不同,否则覆盖) - 确保
$config['cookie_prefix']没和其它系统冲突,导致 cookie 被覆盖或无法读取 - 如果用 file session,确认
session.save_path可写;如果用 database session,确认ci_sessions表结构含user_dataTEXT 字段且未被截断 - 开发时禁用浏览器广告拦截插件(如 uBlock Origin),某些规则会屏蔽含 “csrf” 字样的 cookie
最易被忽略的是:CSRF 验证发生在输入类初始化阶段,早于控制器构造函数执行。这意味着你在 __construct() 里调用 $this->security->get_csrf_hash() 拿到的值,可能和表单渲染时的值不一致 —— 尤其当页面内有重定向、或开启了 csrf_regenerate = TRUE(默认值)。务必在视图中实时调用 get_csrf_hash(),而不是从控制器传过去再复用。


















