在yii2.0中,默认csrf校验是开启的。
页面中,使用小部件ActiveForm,默认会生成一个带有_csrf值的隐藏域。
如果不用ActiveForm,也能调用Request中的方法自己生成。
例子1:
use yii\widgets\ActiveForm;
use yii\helpers\Html;
<?php $form = ActiveForm::begin();?>
<?=Html::submitButton('submit')?>
<?php ActiveForm::end();?>
这样生成的页面表单中,有隐藏域:
<input type="hidden" name="_csrf" value="S0VSOXZHYmgJJgdTEGoDAyJ9YFAVEAQiHRY2fE9yKiECIRVyMjcUEg==">
例子2:
<input type="hidden" id ="_csrf" name="_csrf" value="<?= Yii::$app->request->getCsrfToken();?>”>
这样生成的页面表单中,也有隐藏域:
<input type="hidden" id="_csrf" name="_csrf" value="TzNiQmRfMUoNUDcoAnJQISYLUCsHCFcAGWAGB11qeQMGVyUJIC9HMA==">
请求得到的页面中,隐藏域和cookie都有_csrf值。它们的本质是在渲染页面的时候使用了Request类中的方法 getCsrfToken
(), 执行后会生成一个$token,存于cookie(_csrf)或者session中,存哪里依赖于配置。然后根据$token,会进行加密处理,得到的值,写到页面隐藏域中。
用户提交页面的时候,隐藏域和cookie中的_csrf值,服务器脚本执行时都能取到它们。cookie中存的是$token, 隐藏域中存的是加密后的字符串。调用Yii::$app->request->validateCsrfToken();就能知道是否校验通过。
加密过程如下:
先通过特殊方式,生成一个32位的字符串$token。
$chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-.’;
把上面65个字符复制5遍变为325字符,打乱顺序,取前8位字符,即得到$mask,比如
$token是32位字符,$mask是8位字符,把$mask右补齐到32位,用自己补。
在用$token和$mask进行异或运算(位运算)。
用$mask连接异或运算得到的字符串,再进行base64_encde操作,在把其中的+用.替代。
解密过程如下:
对于表单提交过来的cookie中的_csrf的值,存到$token中。
对于表单提交过来的隐藏域_csrf的值,先把其中的.替换成+号。再进行base64_decode操作。
得到的字符串,前八位是$mask,后面32位作为$m。
因此,把$mask补齐32位,再$token^$mask = $m,判断是否相等。
隐藏域是加密后的字符串
<input type="hidden" id="_csrf" name="_csrf" value="cGFOQ3Q1R2goLwgcImcPGhlRChI3cxMhJCl.dA4HLjsvMQ8XEUM/BQ==">
cookie中存的是token
7bb0ee3a13cf5a86cbde634da1071817c1e8fcfff52c231522c435355c0cbe38a
cookie数据都是加密的,实际就是下面32位字符串
XNF_VRHri0DQCFTITH07z2iS_PATevxm
页面中,使用小部件ActiveForm,默认会生成一个带有_csrf值的隐藏域。
如果不用ActiveForm,也能调用Request中的方法自己生成。
例子1:
use yii\widgets\ActiveForm;
use yii\helpers\Html;
<?php $form = ActiveForm::begin();?>
<?=Html::submitButton('submit')?>
<?php ActiveForm::end();?>
这样生成的页面表单中,有隐藏域:
<input type="hidden" name="_csrf" value="S0VSOXZHYmgJJgdTEGoDAyJ9YFAVEAQiHRY2fE9yKiECIRVyMjcUEg==">
例子2:
<input type="hidden" id ="_csrf" name="_csrf" value="<?= Yii::$app->request->getCsrfToken();?>”>
这样生成的页面表单中,也有隐藏域:
<input type="hidden" id="_csrf" name="_csrf" value="TzNiQmRfMUoNUDcoAnJQISYLUCsHCFcAGWAGB11qeQMGVyUJIC9HMA==">
请求得到的页面中,隐藏域和cookie都有_csrf值。它们的本质是在渲染页面的时候使用了Request类中的方法 getCsrfToken
(), 执行后会生成一个$token,存于cookie(_csrf)或者session中,存哪里依赖于配置。然后根据$token,会进行加密处理,得到的值,写到页面隐藏域中。
用户提交页面的时候,隐藏域和cookie中的_csrf值,服务器脚本执行时都能取到它们。cookie中存的是$token, 隐藏域中存的是加密后的字符串。调用Yii::$app->request->validateCsrfToken();就能知道是否校验通过。
加密过程如下:
先通过特殊方式,生成一个32位的字符串$token。
$chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-.’;
把上面65个字符复制5遍变为325字符,打乱顺序,取前8位字符,即得到$mask,比如
$token是32位字符,$mask是8位字符,把$mask右补齐到32位,用自己补。
在用$token和$mask进行异或运算(位运算)。
用$mask连接异或运算得到的字符串,再进行base64_encde操作,在把其中的+用.替代。
解密过程如下:
对于表单提交过来的cookie中的_csrf的值,存到$token中。
对于表单提交过来的隐藏域_csrf的值,先把其中的.替换成+号。再进行base64_decode操作。
得到的字符串,前八位是$mask,后面32位作为$m。
因此,把$mask补齐32位,再$token^$mask = $m,判断是否相等。
隐藏域是加密后的字符串
<input type="hidden" id="_csrf" name="_csrf" value="cGFOQ3Q1R2goLwgcImcPGhlRChI3cxMhJCl.dA4HLjsvMQ8XEUM/BQ==">
cookie中存的是token
7bb0ee3a13cf5a86cbde634da1071817c1e8fcfff52c231522c435355c0cbe38a
cookie数据都是加密的,实际就是下面32位字符串
XNF_VRHri0DQCFTITH07z2iS_PATevxm