yii中csrf的验证机制

发布时间:2021-01-13 13:51:01 作者:小新
来源:亿速云 阅读:254

小编给大家分享一下yii中csrf的验证机制,希望大家阅读完这篇文章之后都有所收获,下面让我们一起去探讨吧!

跨站请求伪造(英语:Cross-site request forgery),也被称为 one-click attack 或者 session riding,通常缩写为 CSRF 或者 XSRF, 是一种挟制用户在当前已登录的Web应用程序上执行非本意的操作的攻击方法。

跨站请求攻击,简单地说,是攻击者通过一些技术手段欺骗用户的浏览器去访问一个自己曾经认证过的网站并运行一些操作(如发邮件,发消息,甚至财产操作如转账和购买商品)。         

由于浏览器曾经认证过,所以被访问的网站会认为是真正的用户操作而去运行。

这利用了web中用户身份验证的一个漏洞:简单的身份验证只能保证请求发自某个用户的浏览器,却不能保证请求本身是用户自愿发出的。

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是否相等
}

看完了这篇文章,相信你对“yii中csrf的验证机制”有了一定的了解,如果想了解更多相关知识,欢迎关注亿速云行业资讯频道,感谢各位的阅读!

推荐阅读:
  1. 什么是Yii2框架的csrf验证原理分析及token缓存解决方案
  2. 怎么解决django前后端分离csrf验证的问题

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

yii csrf

上一篇:yii框架模型指的是什么

下一篇:yii2与laravel框架有什么区别

相关阅读

您好,登录后才能下订单哦!

密码登录
登录注册
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》