yii2的防御csrf攻击机制

csrf,中文名称:跨站请求伪造,可以在百度上搜索资料,详细了解这一方面的概念。对于我们是非常有帮助的。
yii2的csrf的实现功能是在yii\web\request类实现功能的。
request类中的属性,默认是true的。
public enableCsrfValidation=true;requestrequest=>[enableCookieValidation=>true,]postcsrf,访1Yii::app->request->csrfToken 第一次访问获取csrfToken时,直接到getCsrfToken()访问

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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;
}

 每一次访问this>csrfTokennullregenerate 默认等于false。所以接着执行token=this->loadCsrfToken()这个函数。

 

复制代码
1
2
3
4
5
6
7
8
protected function loadCsrfToken()
    {
        if ($this->enableCsrfCookie) {
            return $this->getCookies()->getValue($this->csrfParam);
        } else {
            return Yii::$app->getSession()->get($this->csrfParam);
        }
}

 

 去cookie中获取COOKIE[csrf]token访tokennullthis->generateCsrfToken()。

 

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
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;
}

 

 这个函数就是随意创建一个token字符串,然后将它保存在COOKIE[csrf]/COOKIEtokentoken_COOKIE['_csrf']的值就不会变,也就代表本机客户端的唯一凭证。
接着再看一下getCsrfToken()函数里的这几行代码:
chars=ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789.;mask = substr(str_shuffle(str_repeat(chars,5)),0,static::CSRFMASKLENGTH);//The+signmaybedecodedasblankspacelater,whichwillfailthevalidationthis->_csrfToken = str_replace('+', '.', base64_encode(mask.this->xorTokens(token,mask)));
这里是利用token和字符串,通过64位进行编码加密生成_csrfToken并且返回,也就是获取csrfToken这个值了。
(2)第二次访问时,Yii::app>request>csrfTokentoken = this>loadCsrfToken()访token_COOKIE['_csrf']的值,所以不会再次重新生成的,所以接着进行64位编码加密生成_csrfToken并且返回。
(3)那么我们需要将数据post过去的时候,我们会在yii\web\conreoller的类中的beforeAction($action)函数进行验证

复制代码
1
2
3
4
5
6
7
8
9
10
11
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;
}

 通过Yii::$app->getRequest()->validateCsrfToken()这个函数验证

 

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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();
  
        var_dump($trueToken);
  
        if ($token !== null) {
            return $this->validateCsrfTokenInternal($token, $trueToken);
        } else {
            // 只要有一个为真,则返回真
            return ($this->validateCsrfTokenInternal($this->getBodyParam($this->csrfParam), $trueToken) || $this->validateCsrfTokenInternal($this->getCsrfTokenFromHeader(), $trueToken));
        }
}

 

 返回true代表认证通过,false代表失败,对于GET,HEAD', 'OPTIONS',这种方式是不认证的,返回true,默认通过,可以继续访问。

如果是其他的访问方式,例如POST,那就的认证。
trueToken=this->loadCsrfToken();这个获取完整COOKIE['_csrf']的真实存在的token。

看看this>validateCsrfTokenInternal(this->getBodyParam(this>csrfParam),trueToken);
里面的这一句this>getBodyParam(this->csrfParam)。就是获取post过来的csrfToken的值或者表单的值,然后validateCsrfTokenInternal(token,trueToken),这个函数将csrfToken进行解密(因为之前通过Yii::app>request>csrfTokentrueToken相同的话就返回true。

再看看this>validateCsrfTokenInternal(this->getCsrfTokenFromHeader(), trueToken)this->getCsrfTokenFromHeader()获取head中的csrfToken的值,再进行解密,解密之后的值如果和trueTokentruereturn(this->validateCsrfTokenInternal(this>getBodyParam(this->csrfParam), trueToken)||this->validateCsrfTokenInternal(this>getCsrfTokenFromHeader(),trueToken));
 这一句是判断(this>validateCsrfTokenInternal(this->getBodyParam(this>csrfParam),trueToken) || this>validateCsrfTokenInternal(this->getCsrfTokenFromHeader(), $trueToken))通过解密认证后就等效于return (true || false)的模式,也就是通过||判定括号里的真假,只要有一个true,则返回true

所以说一旦生成token并保存在COOKIE['_csrf']中,那么每一次在访问时,就会以这个token作为一个基准进行数据加密随意生成一个csrfToken,然后返回给表单中。当post数据过来的时候,就得将这个csrfToken传递过来,然后进行解密,再和COOKIE['_csrf']的token进行比较,那么如果相等就说明访问是无攻击性的,是本站的访问。如果访问不通过,说明可能删改了一些信息,是不安全的。

 

posted @   牛奔  阅读(575)  评论(0编辑  收藏  举报
编辑推荐:
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
点击右上角即可分享
微信分享提示