Yii框架自带了成熟的跨站请求伪造防护机制,默认会对POST、PUT、DELETE等非GET请求进行CSRF令牌校验,很多开发者在开发过程中会遇到验证失败的错误提示,影响正常功能实现。

CSRF验证失败的常见原因
1. 表单未生成CSRF令牌
Yii的表单如果没有使用框架自带的表单组件生成,或者手动编写表单时遗漏了CSRF令牌字段,提交请求时就会缺少令牌信息,导致验证失败。使用yiihelpersHtml::beginForm方法生成表单时,框架会自动插入CSRF令牌字段,而手动编写的表单需要主动添加。
2. 请求未携带正确的令牌
如果是通过Ajax发送请求,没有在请求参数或者请求头中携带CSRF令牌,也会触发验证失败。另外如果令牌已经过期,或者和当前会话的令牌不匹配,同样会出现该问题。
3. 配置参数不匹配
CSRF相关的配置参数如果设置错误,比如令牌验证的开关、令牌存储方式、允许的请求来源等配置不正确,也会导致验证流程异常。
对应的解决方法
解决表单提交验证失败
如果是普通表单提交,确保使用框架的表单辅助方法生成表单,或者手动添加CSRF令牌字段。以下是手动添加令牌的示例代码:
<?php
use yiihelpersHtml;
?>
<form action="/site/submit" method="post">
<?= Html::hiddenInput(Yii::$app->request->csrfParam, Yii::$app->request->csrfToken) ?>
<input type="text" name="username" placeholder="请输入用户名">
<button type="submit">提交</button>
</form>
解决Ajax请求验证失败
如果是Ajax请求,需要在请求中携带CSRF令牌,可以将令牌放在请求参数中,也可以放在请求头中。以下是携带令牌的Ajax请求示例:
// 获取CSRF令牌
var csrfToken = $('meta[name="csrf-token"]').attr('content');
// 发送Ajax请求
$.ajax({
url: '/site/ajax-submit',
type: 'post',
data: {
username: 'test',
_csrf: csrfToken // 携带CSRF令牌
},
success: function(res) {
console.log(res);
}
});
如果是将令牌放在请求头中,需要在请求头添加X-CSRF-Token字段,值为当前会话的CSRF令牌。
调整CSRF配置参数
CSRF相关配置位于config/web.php的components数组中的request配置项里,以下是常用配置示例:
'components' => [
'request' => [
'csrfCookie' => [
'httpOnly' => true, // 禁止JavaScript读取CSRF Cookie,提升安全性
],
'enableCsrfValidation' => true, // 是否开启CSRF验证,默认开启
'csrfVar' => '_csrf', // 表单中CSRF令牌的字段名,默认是_csrf
],
],
如果某些接口不需要进行CSRF验证,可以在对应的控制器中设置$enableCsrfValidation属性为false,示例如下:
namespace appcontrollers;
use yiiwebController;
class ApiController extends Controller
{
public $enableCsrfValidation = false; // 关闭当前控制器的CSRF验证
public function actionIndex()
{
// 接口逻辑
return json_encode(['code' => 0, 'msg' => 'success']);
}
}
CSRF防护配置的最佳实践
生产环境中建议保持CSRF验证开启,不要随意关闭全局的验证开关。如果确实需要部分接口跳过验证,尽量精确到单个动作或者单个控制器,避免整个应用暴露在CSRF攻击风险下。同时建议开启httpOnly配置,防止CSRF令牌被前端脚本窃取,进一步提升应用的安全性。