yii2 ajax csrf meta,Yii2 中CSRF的疑问(完结)

描述你的问题

在开启Csrf防御的时候,默认csrf的值只能使用一次,第二次提交就是验证不通过,因为已经使用过了,那么如何做下面这种效果呢?

bVsP07

如图,该页面是Ajax提交请求,那么第一个按钮点击后csrf值失效了,第二次提交失败返回400错误,求解!

怎么让csrf的值可以刷新后用于第二次请求。

不要让我关闭csrf,csrf本来就是为了防御这种请求的。

经过楼下兄弟的指点,现分析如下,

在使用他自带的表单的时候,\yii\helpers\BaseHtml::beginForm 方法中有判断,如果在一次页面展示中多次调用表单创建,则每次获取的csrf都是一样的,也就是说,同一页面多个表单,都可用的。

我们知道csrf验证是在\yii\web\Request类中,但是该类在初始化的时候我并没有看到他有检查csrf,所以我们需要知道他是在哪里进行检查的,通过全文搜索,发现在\yii\web\Controller类的beforeAction方法中有检查,原型如下:

/**

* @inheritdoc

*/

public function beforeAction($action)

{

if (parent::beforeAction($action)) {

if ($this->enableCsrfValidation && Yii::$app->getErrorHandler()->exception === null && !Yii::$app->getRequest()->validateCsrfToken()) {

throw new BadRequestHttpException(Yii::t('yii', 'Unable to verify your data submission.'));

}

return true;

}

return false;

}

通过该代码我们知道,在检查csrf是直接使用的\yii\web\Request::validateCsrfToken方法,在该方法中先通过loadCsrfToken方法获取csrf,

/**

* Loads the CSRF token from cookie or session.

* @return string the CSRF token loaded from cookie or session. Null is returned if the cookie or session

* does not have CSRF token.

*/

protected function loadCsrfToken()

{

if ($this->enableCsrfCookie) {

return $this->getCookies()->getValue($this->csrfParam);

} else {

return Yii::$app->getSession()->get($this->csrfParam);

}

}

这个代码很好理解,就是cookie取,没开的话就在session取,然后返回。

然后通过validateCsrfTokenInternal方法验证csrf token,通过跟踪代码发现,在验证完csrf后并没有删除该csrf,换句话说就是该csrf还是可以用的,只要你页面不刷新,因为页面刷新时在 你布局文件的头部会有个 = Html::csrfMetaTags() ?>用来输出csrf,这时候上一个csrf就会失效的。所以说一个页面不停地AJAX请求,只要不刷新是没问题的。

结论:

在yii中一共有如下几个地方会刷新csrf

\yii\helpers\BaseHtml::beginForm、Html::csrfMetaTags()、\yii\web\Request::getCsrfToken,关键点在\yii\web\Request::getCsrfToken方法里如楼下兄弟所言,$regenerate 参数也可以控制强制重新生成csrf token.

private $_csrfToken;

/**

* Returns the token used to perform CSRF validation.

*

* This token is a masked version of [[rawCsrfToken]] to prevent [BREACH attacks](http://breachattack.com/).

* This token may be passed along via a hidden field of an HTML form or an HTTP header value

* to support CSRF validation.

* @param boolean $regenerate whether to regenerate CSRF token. When this parameter is true, each time

* this method is called, a new CSRF token will be generated and persisted (in session or cookie).

* @return string the token used to perform CSRF validation.

*/

public function getCsrfToken($regenerate = false)

{

if ($this->_csrfToken === null || $regenerate) {

if ($regenerate || ($token = $this->loadCsrfToken()) === null) {

$token = $this->generateCsrfToken();

}

// the mask doesn't need to be very random

$chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-.';

$mask = substr(str_shuffle(str_repeat($chars, 5)), 0, static::CSRF_MASK_LENGTH);

// The + sign may be decoded as blank space later, which will fail the validation

$this->_csrfToken = str_replace('+', '.', base64_encode($mask . $this->xorTokens($token, $mask)));

}

return $this->_csrfToken;

}

该方法在获取csrftoken的时候判断 $this->_csrfToken是否是空的,按照页面正常加载来说程序一启动肯定是空的,这个值不是存客户端的,这个也不是从服务端获取的,是页面启动后就是空的,只有执行该方法后生成的csrf token才会放入cookie和session,也就是说在同一个请求中多次执行该方法,获取的csrf token都是一样的,如果一个新页面请求到该方法就会重新生成,这也是不同页面刷新为何csrf token一直变的原因,所以,csrf token值可多次使用,但是只限当前页面的ajax请求。

我的系统问题是由于我使用的是IIS 10作为Web,又开了Url重写,默认不存在的文件或文件夹都会重写给Yii处理,然而我没有在web目录下放置传说中的favicon.ico文件,导致有个404,这个是浏览器在后台自动请求的,导致yii输出了一个404,而我不知道,404页面又调用了布局,导致csrf token 被刷新了。很二是吧。。。。。。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值