yii2的csrf验证原理分析及token缓存解决方案
正文主要分多个部分,首先简单介绍csrf,接着对照源码重点分析一下yii框架的证实原理,最终针对页面缓存导致的token被缓存提出一种有效的方案。涉及的知识点会作为附录附于文末。
一、CSRF
即Cross-site request
forgery跨站请求伪造,是指有人冒充你的身份展开局地恶心操作。
例如你登录了网站A,网站A在您的微处理器安装了cookie用以标识身份和状态,然后你又走访了网站B,那时候网站B就足以伪造你的身份在A网站举办操作,因为网站B在呼吁网站A时,浏览器会自行发送从前设置的cookie音信,让网站A误以为如故是您在拓展操作。
对于csrf的防护,一般都会放在服务器端进行,那么我们来看下Yii2中是怎么样举办戒备的。
一、CSRF
yii2的csrf验证原理分析及token缓存解决方案。即Cross-site request
forgery跨站请求伪造,是指有人伪造你的地位进行局地恶心操作。
诸如您登录了网站A,网站A在你的处理器安装了cookie用以标识身份和景色,然后您又走访了网站B,那时候网站B就可以以假乱真你的地位在A网站开展操作,因为网站B在呼吁网站A时,浏览器会自行发送在此以前安装的cookie消息,让网站A误以为仍然是您在拓展操作。
对此csrf的防护,一般都会放在服务器端进行,那么我们来看下Yii2中是何等举行预防的。
一、CSRF
即Cross-site request
forgery跨站请求伪造,是指有人冒充你的地位展开局地恶心操作。
譬如说您登录了网站A,网站A在你的处理器安装了cookie用以标识身份和意况,然后您又走访了网站B,那时候网站B就可以伪造你的地点在A网站开展操作,因为网站B在呼吁网站A时,浏览器会活动发送之前设置的cookie新闻,让网站A误以为依旧是你在举办操作。
对此csrf的防护,一般都会放在服务器端举办,那么我们来看下Yii2中是什么开展防备的。
1.CSRF描述
二、Yii2 CSRF
首先表明一(Karicare)下,我设置的是Yii2高级模版。
二、Yii2 CSRF
先是说Bellamy(Bellamy)下,我设置的是Yii2尖端模版。
二、Yii2 CSRF
率先说圣元(Synutra)下,我设置的是Yii2高档模版。
CSRF全称为“Cross-Site Request
Forgery”,是在用户合法的SESSION内发起的口诛笔伐。黑客通过在网页中置放Web恶意请求代码,并诱使受害人访问该页面,当页面被访问后,请求在受害人不知情的情状下以事主的官方身份发起,并举行黑客所企望的动作。以下HTML代码提供了一个“删除产品”的作用:
csrf token生成
vendor\yiisoft\yii2\web\Request.php
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;
}
getCsrfToken方法首先会用loadCsrfToken方法尝试加载已存在的token,即使没有则用generateCsrfToken方法再生成一个,并因此持续处理,得到最终的前台请求时带领的csrf
token。
protected function loadCsrfToken()
{
if ($this->enableCsrfCookie) {
return $this->getCookies()->getValue($this->csrfParam);
} else {
return Yii::$app->getSession()->get($this->csrfParam);
}
}
loadCsrfToken方法会尝试从cookie或session中加载已经存在的token,enableCsrfCookie默许为true,所以一般会从cookie中取得
public function getCookies()
{
if ($this->_cookies === null) {
$this->_cookies = new CookieCollection($this->loadCookies(), [
'readOnly' => true,
]);
}
return $this->_cookies;
}
此处又调用了loadCookies方法
protected function loadCookies()
{
$cookies = [];
if ($this->enableCookieValidation) {
if ($this->cookieValidationKey == '') {
throw new InvalidConfigException(get_class($this) . '::cookieValidationKey must be configured with a secret key.');
}
foreach ($_COOKIE as $name => $value) {
if (!is_string($value)) {
continue;
}
$data = Yii::$app->getSecurity()->validateData($value, $this->cookieValidationKey);
if ($data === false) {
continue;
}
$data = @unserialize($data);
if (is_array($data) && isset($data[0], $data[1]) && $data[0] === $name) {
$cookies[$name] = new Cookie([
'name' => $name,
'value' => $data[1],
'expire' => null,
]);
}
}
} else {
foreach ($_COOKIE as $name => $value) {
$cookies[$name] = new Cookie([
'name' => $name,
'value' => $value,
'expire' => null,
]);
}
}
return $cookies;
}
这边就是分析验证$_首席营业官KIE中的数据。
csrf token生成
vendor\yiisoft\yii2\web\Request.php
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;
}
getCsrfToken方法首先会用loadCsrfToken方法尝试加载已存在的token,如果没有则用generateCsrfToken方法再生成一个,并由此持续处理,得到终极的前台请求时辅导的csrf
token。
protected function loadCsrfToken()
{
if ($this->enableCsrfCookie) {
return $this->getCookies()->getValue($this->csrfParam);
} else {
return Yii::$app->getSession()->get($this->csrfParam);
}
}
loadCsrfToken方法会尝试从cookie或session中加载已经存在的token,enableCsrfCookie默许为true,所以一般会从cookie中得到
public function getCookies()
{
if ($this->_cookies === null) {
$this->_cookies = new CookieCollection($this->loadCookies(), [
'readOnly' => true,
]);
}
return $this->_cookies;
}
此处又调用了loadCookies方法
protected function loadCookies()
{
$cookies = [];
if ($this->enableCookieValidation) {
if ($this->cookieValidationKey == '') {
throw new InvalidConfigException(get_class($this) . '::cookieValidationKey must be configured with a secret key.');
}
foreach ($_COOKIE as $name => $value) {
if (!is_string($value)) {
continue;
}
$data = Yii::$app->getSecurity()->validateData($value, $this->cookieValidationKey);
if ($data === false) {
continue;
}
$data = @unserialize($data);
if (is_array($data) && isset($data[0], $data[1]) && $data[0] === $name) {
$cookies[$name] = new Cookie([
'name' => $name,
'value' => $data[1],
'expire' => null,
]);
}
}
} else {
foreach ($_COOKIE as $name => $value) {
$cookies[$name] = new Cookie([
'name' => $name,
'value' => $value,
'expire' => null,
]);
}
}
return $cookies;
}
那边就是分析验证$_主任KIE中的数据。
csrf token生成
yii2的csrf验证原理分析及token缓存解决方案。vendor\yiisoft\yii2\web\Request.php
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;
}
getCsrfToken方法首先会用loadCsrfToken方法尝试加载已存在的token,若是没有则用generateCsrfToken方法再生成一个,并透过延续处理,得到终极的前台请求时带领的csrf
token。
protected function loadCsrfToken()
{
if ($this->enableCsrfCookie) {
return $this->getCookies()->getValue($this->csrfParam);
} else {
return Yii::$app->getSession()->get($this->csrfParam);
}
}
loadCsrfToken方法会尝试从cookie或session中加载已经存在的token,enableCsrfCookie默许为true,所以一般会从cookie中赢得
public function getCookies()
{
if ($this->_cookies === null) {
$this->_cookies = new CookieCollection($this->loadCookies(), [
'readOnly' => true,
]);
}
return $this->_cookies;
}
那里又调用了loadCookies方法
protected function loadCookies()
{
$cookies = [];
if ($this->enableCookieValidation) {
if ($this->cookieValidationKey == '') {
throw new InvalidConfigException(get_class($this) . '::cookieValidationKey must be configured with a secret key.');
}
foreach ($_COOKIE as $name => $value) {
if (!is_string($value)) {
continue;
}
$data = Yii::$app->getSecurity()->validateData($value, $this->cookieValidationKey);
if ($data === false) {
continue;
}
$data = @unserialize($data);
if (is_array($data) && isset($data[0], $data[1]) && $data[0] === $name) {
$cookies[$name] = new Cookie([
'name' => $name,
'value' => $data[1],
'expire' => null,
]);
}
}
} else {
foreach ($_COOKIE as $name => $value) {
$cookies[$name] = new Cookie([
'name' => $name,
'value' => $value,
'expire' => null,
]);
}
}
return $cookies;
}
那边就是分析验证$_老总KIE中的数据。
<a href="http://www.shop.com/delProducts.php?id=100" "javascript:return confirm('Are you sure?')">Delete</a>
cookies设置
vendor\yiisoft\yii2\web\Response.php
protected function sendCookies()
{
if ($this->_cookies === null) {
return;
}
$request = Yii::$app->getRequest();
if ($request->enableCookieValidation) {
if ($request->cookieValidationKey == '') {
throw new InvalidConfigException(get_class($request) . '::cookieValidationKey must be configured with a secret key.');
}
$validationKey = $request->cookieValidationKey;
}
foreach ($this->getCookies() as $cookie) {
$value = $cookie->value;
if ($cookie->expire != 1 && isset($validationKey)) {
$value = Yii::$app->getSecurity()->hashData(serialize([$cookie->name, $value]), $validationKey);
}
setcookie($cookie->name, $value, $cookie->expire, $cookie->path, $cookie->domain, $cookie->secure, $cookie->httpOnly);
}
}
sendCookies方法运用cookieValidationKey对cookie进行一多级处理,主假如为着博取的时候进行表达,防止cookie被曲解。
public function getCookies()
{
if ($this->_cookies === null) {
$this->_cookies = new CookieCollection;
}
return $this->_cookies;
}
这里的getCookies方法跟request中的分歧,并不会从$_COOKIE中获取,_cookies属性在request中的generateCsrfToken方法中有进展设置
protected function generateCsrfToken()
{
$token = Yii::$app->getSecurity()->generateRandomString();
if ($this->enableCsrfCookie) {
$cookie = $this->createCsrfCookie($token);
Yii::$app->getResponse()->getCookies()->add($cookie);
} else {
Yii::$app->getSession()->set($this->csrfParam, $token);
}
return $token;
}
cookies设置
vendor\yiisoft\yii2\web\Response.php
protected function sendCookies()
{
if ($this->_cookies === null) {
return;
}
$request = Yii::$app->getRequest();
if ($request->enableCookieValidation) {
if ($request->cookieValidationKey == '') {
throw new InvalidConfigException(get_class($request) . '::cookieValidationKey must be configured with a secret key.');
}
$validationKey = $request->cookieValidationKey;
}
foreach ($this->getCookies() as $cookie) {
$value = $cookie->value;
if ($cookie->expire != 1 && isset($validationKey)) {
$value = Yii::$app->getSecurity()->hashData(serialize([$cookie->name, $value]), $validationKey);
}
setcookie($cookie->name, $value, $cookie->expire, $cookie->path, $cookie->domain, $cookie->secure, $cookie->httpOnly);
}
}
send库克ies方法运用cookieValidationKey对cookie举行一多级处理,紧假若为着取得的时候举行认证,幸免cookie被篡改。
public function getCookies()
{
if ($this->_cookies === null) {
$this->_cookies = new CookieCollection;
}
return $this->_cookies;
}
此处的getCookies方法跟request中的不一样,并不会从$_COOKIE中获取,_cookies属性在request中的generateCsrfToken方法中有拓展安装
protected function generateCsrfToken()
{
$token = Yii::$app->getSecurity()->generateRandomString();
if ($this->enableCsrfCookie) {
$cookie = $this->createCsrfCookie($token);
Yii::$app->getResponse()->getCookies()->add($cookie);
} else {
Yii::$app->getSession()->set($this->csrfParam, $token);
}
return $token;
}
cookies设置
vendor\yiisoft\bf88必发唯一官网,yii2\web\Response.php
protected function sendCookies()
{
if ($this->_cookies === null) {
return;
}
$request = Yii::$app->getRequest();
if ($request->enableCookieValidation) {
if ($request->cookieValidationKey == '') {
throw new InvalidConfigException(get_class($request) . '::cookieValidationKey must be configured with a secret key.');
}
$validationKey = $request->cookieValidationKey;
}
foreach ($this->getCookies() as $cookie) {
$value = $cookie->value;
if ($cookie->expire != 1 && isset($validationKey)) {
$value = Yii::$app->getSecurity()->hashData(serialize([$cookie->name, $value]), $validationKey);
}
setcookie($cookie->name, $value, $cookie->expire, $cookie->path, $cookie->domain, $cookie->secure, $cookie->httpOnly);
}
}
sendCookies方法运用cookieValidationKey对cookie举办一多重处理,重假若为着赢得的时候进行验证,幸免cookie被歪曲。
public function getCookies()
{
if ($this->_cookies === null) {
$this->_cookies = new CookieCollection;
}
return $this->_cookies;
}
此处的getCookies方法跟request中的差别,并不会从$_COOKIE中获取,_cookies属性在request中的generateCsrfToken方法中有进行安装
protected function generateCsrfToken()
{
$token = Yii::$app->getSecurity()->generateRandomString();
if ($this->enableCsrfCookie) {
$cookie = $this->createCsrfCookie($token);
Yii::$app->getResponse()->getCookies()->add($cookie);
} else {
Yii::$app->getSession()->set($this->csrfParam, $token);
}
return $token;
}
一旦程序员在后台从未对该“删除产品”请求做相应的合法性验证,只要用户访问了该链接,相应的产品即被删除,那么黑客可经过欺骗受害者访问带有以下恶意代码的网页,即可在事主不知情的动静下删除相应的制品。
csrf验证
vendor\yiisoft\yii2\web\Request.php
public function validateCsrfToken($token = null)
{
$method = $this->getMethod();
// only validate CSRF token on non-"safe" methods http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.1.1
if (!$this->enableCsrfValidation || in_array($method, ['GET', 'HEAD', 'OPTIONS'], true)) {
return true;
}
$trueToken = $this->loadCsrfToken();
if ($token !== null) {
return $this->validateCsrfTokenInternal($token, $trueToken);
} else {
return $this->validateCsrfTokenInternal($this->getBodyParam($this->csrfParam), $trueToken)
|| $this->validateCsrfTokenInternal($this->getCsrfTokenFromHeader(), $trueToken);
}
}
此间先验证一下请求方式,接着获取cookie中的token,然后用validateCsrfTokenInternal方法开展对照
private function validateCsrfTokenInternal($token, $trueToken)
{
if (!is_string($token)) {
return false;
}
$token = base64_decode(str_replace('.', '+', $token));
$n = StringHelper::byteLength($token);
if ($n <= static::CSRF_MASK_LENGTH) {
return false;
}
$mask = StringHelper::byteSubstr($token, 0, static::CSRF_MASK_LENGTH);
$token = StringHelper::byteSubstr($token, static::CSRF_MASK_LENGTH, $n - static::CSRF_MASK_LENGTH);
$token = $this->xorTokens($mask, $token);
return $token === $trueToken;
}
剖析呼吁指导的csrf token 进行相比较并赶回结果。
csrf验证
vendor\yiisoft\yii2\web\Request.php
public function validateCsrfToken($token = null)
{
$method = $this->getMethod();
// only validate CSRF token on non-"safe" methods http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.1.1
if (!$this->enableCsrfValidation || in_array($method, ['GET', 'HEAD', 'OPTIONS'], true)) {
return true;
}
$trueToken = $this->loadCsrfToken();
if ($token !== null) {
return $this->validateCsrfTokenInternal($token, $trueToken);
} else {
return $this->validateCsrfTokenInternal($this->getBodyParam($this->csrfParam), $trueToken)
|| $this->validateCsrfTokenInternal($this->getCsrfTokenFromHeader(), $trueToken);
}
}
那里先验证一下请求格局,接着获取cookie中的token,然后用validateCsrfTokenInternal方法开展对照
private function validateCsrfTokenInternal($token, $trueToken)
{
if (!is_string($token)) {
return false;
}
$token = base64_decode(str_replace('.', '+', $token));
$n = StringHelper::byteLength($token);
if ($n <= static::CSRF_MASK_LENGTH) {
return false;
}
$mask = StringHelper::byteSubstr($token, 0, static::CSRF_MASK_LENGTH);
$token = StringHelper::byteSubstr($token, static::CSRF_MASK_LENGTH, $n - static::CSRF_MASK_LENGTH);
$token = $this->xorTokens($mask, $token);
return $token === $trueToken;
}
解析呼吁率领的csrf token 进行对照并赶回结果。
csrf验证
vendor\yiisoft\yii2\web\Request.php
public function validateCsrfToken($token = null)
{
$method = $this->getMethod();
// only validate CSRF token on non-"safe" methods http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.1.1
if (!$this->enableCsrfValidation || in_array($method, ['GET', 'HEAD', 'OPTIONS'], true)) {
return true;
}
$trueToken = $this->loadCsrfToken();
if ($token !== null) {
return $this->validateCsrfTokenInternal($token, $trueToken);
} else {
return $this->validateCsrfTokenInternal($this->getBodyParam($this->csrfParam), $trueToken)
|| $this->validateCsrfTokenInternal($this->getCsrfTokenFromHeader(), $trueToken);
}
}
这边先验证一下呼吁形式,接着获取cookie中的token,然后用validateCsrfTokenInternal方法举办自查自纠
private function validateCsrfTokenInternal($token, $trueToken)
{
if (!is_string($token)) {
return false;
}
$token = base64_decode(str_replace('.', '+', $token));
$n = StringHelper::byteLength($token);
if ($n <= static::CSRF_MASK_LENGTH) {
return false;
}
$mask = StringHelper::byteSubstr($token, 0, static::CSRF_MASK_LENGTH);
$token = StringHelper::byteSubstr($token, static::CSRF_MASK_LENGTH, $n - static::CSRF_MASK_LENGTH);
$token = $this->xorTokens($mask, $token);
return $token === $trueToken;
}
浅析呼吁辅导的csrf token 举行对照并赶回结果。
2.yii的csrf验证原理
/vendor/yiisoft/yii2/web/Request.php简写为Request.php
三、总结
Yii2的做法就是文人成一个随意token,存入cookie中,同时在伸手中引导随机变化的csrf
token,也是按照以前的自由token而生成的,验证的时候对cookie和csrf
token举行分析,获得随机token举办自查自纠,从而判断请求是或不是合法。
最终,本文只是对几乎的流程展开了剖析,具体的底细还请查看源码。
三、总结
Yii2的做法就是一介书生成一个无限制token,存入cookie中,同时在呼吁中带走随机生成的csrf
token,也是依照以前的擅自token而变化的,验证的时候对cookie和csrf
token举办辨析,得到随机token举行对照,从而判断请求是或不是合法。
最后,本文只是对大约的流水线进行掌握析,具体的细节还请查看源码。
三、总结
Yii2的做法就是秀才成一个肆意token,存入cookie中,同时在伸手中指导随机变化的csrf
token,也是依据此前的人身自由token而转变的,验证的时候对cookie和csrf
token举办剖析,获得随机token进行自查自纠,从而判断请求是还是不是合法。
末尾,本文只是对大概的流程展开了分析,具体的细节还请查看源码。
/vendor/yiisoft/yii2/web/Controller.php简写为Controller.php
开启csrf验证
在控制器里将enableCsrfValidation为true,则控制器内有所操作都会打开验证,平常做法是将enableCsrfValidation为false,而将有些敏感操作设为true,开启局地验证。
public $enableCsrfValidation = false;
/**
* @param \yii\base\Action $action
* @return bool
* @desc: 局部开启csrf验证(重要的表单提交必须加入验证,加入$accessActions即可
*/
public function beforeAction($action){
$currentAction = $action->id;
$accessActions = ['vote','like','delete','download'];
if(in_array($currentAction,$accessActions)) {
$action->controller->enableCsrfValidation = true;
}
parent::beforeAction($action);
return true;
}
生成token字段
在Request.php
首先通过平安组件Security获取一个32位的随意字符串,并存入cookie或session,那是原生的token.
/**
* Generates an unmasked random token used to perform CSRF validation.
* @return string the random token for CSRF validation.
*/
protected function generateCsrfToken()
{
$token = Yii::$app->getSecurity()->generateRandomString();
if ($this->enableCsrfCookie) {
$cookie = $this->createCsrfCookie($token);
Yii::$app->getResponse()->getCookies()->add($cookie);
} else {
Yii::$app->getSession()->set($this->csrfParam, $token);
}
return $token;
}
继而通过一文山会海加密替换操作,生成加密后_csrfToken,这些是传给浏览器的token.
先随机暴发CSRF_MASK_LENGTH(Yii2里默许是8位)长度的字符串 mask
对mask和token举办如下运算 str_replace('+', '.', base64_encode($mask . $this->xorTokens($token, $mask))); $this->xorTokens($arg1,$arg2)
是一个先补位异或运算
/**
* Returns the XOR result of two strings.
* If the two strings are of different lengths, the shorter one will be padded to the length of the longer one.
* @param string $token1
* @param string $token2
* @return string the XOR result
*/
private function xorTokens($token1, $token2)
{
$n1 = StringHelper::byteLength($token1);
$n2 = StringHelper::byteLength($token2);
if ($n1 > $n2) {
$token2 = str_pad($token2, $n1, $token2);
} elseif ($n1 < $n2) {
$token1 = str_pad($token1, $n2, $n1 === 0 ? ' ' : $token1);
}
return $token1 ^ $token2;
}
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;
}
验证token
在controller.php里调用request.php里的validateCsrfToken方法
/**
* @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;
}
public function validateCsrfToken($token = null)
{
$method = $this->getMethod();
if (!$this->enableCsrfValidation || in_array($method, ['GET', 'HEAD', 'OPTIONS'], true)) {
return true;
}
$trueToken = $this->loadCsrfToken();//如果开启了enableCsrfCookie,CsrfToken就从cookie里取,否者从session里取(更安全)
if ($token !== null) {
return $this->validateCsrfTokenInternal($token, $trueToken);
} else {
return $this->validateCsrfTokenInternal($this->getBodyParam($this->csrfParam), $trueToken)
|| $this->validateCsrfTokenInternal($this->getCsrfTokenFromHeader(), $trueToken);
}
}
得到客户端传入
$this->getBodyParam($this->csrfParam)
然后是validateCsrfTokenInternal
private function validateCsrfTokenInternal($token, $trueToken)
{
if (!is_string($token)) {
return false;
}
$token = base64_decode(str_replace('.', '+', $token));
$n = StringHelper::byteLength($token);
if ($n <= static::CSRF_MASK_LENGTH) {
return false;
}
$mask = StringHelper::byteSubstr($token, 0, static::CSRF_MASK_LENGTH);
$token = StringHelper::byteSubstr($token, static::CSRF_MASK_LENGTH, $n - static::CSRF_MASK_LENGTH);
$token = $this->xorTokens($mask, $token);
return $token === $trueToken;
}
加密时用的是 str_replace('+', '.', base64_encode(mask.mask.this->xorTokens(token,token,mask)));
解密
1.率先要把.替换成+ 2.然后base64_decode 再
依照长度分别取出mask和mask和this->xorTokens(token,token,mask) ;
为了求证方便 this−>xorTokens(this−>xorTokens(token, $mask)
那里名为 token1 然后 举办mask和token1的异或运算,即得token
注意在加密时
token1=token^mask
所以 解密时
token=mask^token1=mask^(token^mask)
3.token缓存的解决方案
当页面全体被缓存后,token也被缓存导致验证败北,一种常见的缓解思路是每一回提交前再一次取得token,那样就足以经过认证了。
附录:
str_pad()
,该函数再次来到 input
被从左端、右端或者同时两端被填充到制定长度后的结果。假如可选的
pad_string 参数没有被指定,input 将被空格字符填充,否则它将被
pad_string 填充到指定长度;
str_shuffle()
函数打乱一个字符串,使用其余一种可能的排序方案。
因为yii2 csrf的认证的加解密
涉及到异或运算
于是要求先添补php里字符串异或运算的相干文化,不必要的可以跳过
^异或运算 不平等再次来到1 否者再次来到 0
在PHP语言中,平常用来做加密的运算,解密也直接用^就行 字符串运算时
利用字符的ascii码转换为2进制来运算 单个字符运算
1.对于单个字符和单个字符的
直接计算其结果即可 比如表里的a^b
2.对此长度一样的几个字符串 如表里的ab^cd
总括a^c对应的结果和和b^d对应的结果 对应的字符连接起来