Yii2的csrf验证机制 [ 2.0 版本 ]
最近接触到yii2的csrf,这里简单介绍一下它的验证机制。
- 取用于csrf验证的token值;判断用于csrf的token是否存在,如果不存在则使用
generateCsrfToken()
生成。 - 验证
web\Controller
中的beforeAction()
方法中有Yii::$app->getRequest()->validateCsrfToken()
判断,用于验证csrf。
一般我的认识yii2的csrf都是从Yii::$app->request->getCsrfToken()
开始;好的,我们就从getCsrfToken()
说起。
此方法在yii\web\Request.php
中:
/**
* Returns the token used to perform CSRF validation.
* 返回用于执行CSRF验证的token
* 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) { //loadCsrfToken()就是在cookie或者session中获取token值
$token = $this->generateCsrfToken(); //如果token为空则调用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;
}
/**
* 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); //cookie中获取csrf的token
} else {
return Yii::$app->getSession()->get($this->csrfParam); //session中获取csrf的token
}
}
/**
* Creates a cookie with a randomly generated CSRF token.
* Initial values specified in [[csrfCookie]] will be applied to the generated cookie.
* @param string $token the CSRF token
* @return Cookie the generated cookie
* @see enableCsrfValidation
*/
protected function createCsrfCookie($token)
{
$options = $this->csrfCookie;
$options['name'] = $this->csrfParam;
$options['value'] = $token;
return new Cookie($options);
}
/**
* 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); //createCsrfCookie()用于生成csrf的key=>value形式的token
Yii::$app->getResponse()->getCookies()->add($cookie); //将生成key=>value保存到cookies
} else {
Yii::$app->getSession()->set($this->csrfParam, $token); //将csrf的token存在session中
}
return $token;
}
/**
* 每次调用控制器中的方法的时候都会调用下面的Yii::$app->getRequest()->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;
} else {
return false;
}
}
/**
* 校验方法
* Performs the CSRF validation.
*
* This method will validate the user-provided CSRF token by comparing it with the one stored in cookie or session.
* This method is mainly called in [[Controller::beforeAction()]].
*
* Note that the method will NOT perform CSRF validation if [[enableCsrfValidation]] is false or the HTTP method
* is among GET, HEAD or OPTIONS.
*
* @param string $token the user-provided CSRF token to be validated. If null, the token will be retrieved from
* the [[csrfParam]] POST field or HTTP header.
* This parameter is available since version 2.0.4.
* @return boolean whether CSRF token is valid. If [[enableCsrfValidation]] is false, this method will return true.
*/
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);
//getCsrfTokenFromHeader()这个我也不太理解,还请指点一下
}
}
/**
* @return string the CSRF token sent via [[CSRF_HEADER]] by browser. Null is returned if no such header is sent.
*/
public function getCsrfTokenFromHeader()
{
$key = 'HTTP_' . str_replace('-', '_', strtoupper(static::CSRF_HEADER));
return isset($_SERVER[$key]) ? $_SERVER[$key] : null;
}
/**
* Validates CSRF token
*
* @param string $token
* @param string $trueToken
* @return boolean
*/
private function validateCsrfTokenInternal($token, $trueToken)
{
$token = base64_decode(str_replace('.', '+', $token)); //解码从客户端获取的csrf的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和真实的token是否相等
}
冰恋冬
注册时间:2015-07-30
最后登录:2020-09-01
在线时长:21小时14分
最后登录:2020-09-01
在线时长:21小时14分
- 粉丝3
- 金钱4385
- 威望10
- 积分4695
热门源码
- 基于 Yii 2 + Bootstrap 3 搭建一套后台管理系统 CMF
- 整合完 yii2-rbac+yii2-admin+adminlte 等库的基础开发后台源码
- 适合初学者学习的一款通用的管理后台
- yii-goaop - 将 goaop 集成到 Yii,在 Yii 中优雅的面向切面编程
- yii-log-target - 监控系统异常且多渠道发送异常信息通知
- 店滴云1.3.0
- 面向对象的一小步:添加 ActiveRecord 的 Scope 功能
- Yii2 开源商城 FecShop
- 基于 Yii2 开发的多店铺商城系统,免费开源 + 适合二开
- leadshop - 基于 Yii2 开发的一款免费开源且支持商业使用的商城管理系统
共 1 条评论
完全照抄源码嘛,连不同方法所属的类都没有区分
那你能不能写一个更详细的分享给大家!