1.CSRF形貌
CSRF全称为“Cross-Site Request Forgery”,是在用户正当的SESSION内提议的进击。黑客经由过程在网页中嵌入Web歹意要求代码,并诱使受害者接见该页面,当页面被接见后,要求在受害者不知情的情况下以受害者的正当身份提议,并实行黑客所期待的行动。以下HTML代码供应了一个“删除产物”的功用:
<a href="http://www.shop.com/delProducts.php?id=100" "javascript:return confirm('Are you sure?')">Delete</a>
假定程序员在背景没有对该“删除产物”要求做响应的正当性考证,只需用户接见了该链接,响应的产物即被删除,那末黑客可经由过程诳骗受害者接见带有以下歹意代码的网页,即可在受害者不知情的情况下删除响应的产物。
2.yii的csrf考证道理 /vendor/yiisoft/yii2/web/Request.php简写为Request.php
/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对应的效果 对应的字符连接起来
相干教程:PHP视频教程
以上就是Yii2框架的csrf考证道理剖析及token缓存处理计划的细致内容,更多请关注ki4网别的相干文章!