首页 后端开发 php教程 Yii2框架的csrf验证原理分析及token缓存解决方案

Yii2框架的csrf验证原理分析及token缓存解决方案

Apr 25, 2019 pm 03:20 PM
yii框架

本文主要分三个部分,首先简单介绍csrf,接着对照源码重点分析一下yii框架的验证原理,最后针对页面缓存导致的token被缓存提出一种可行的方案。涉及的知识点会作为附录附于文末。感兴趣的朋友了解一下吧。

1.CSRF描述

CSRF全称为“Cross-Site Request Forgery”,是在用户合法的SESSION内发起的攻击。黑客通过在网页中嵌入Web恶意请求代码,并诱使受害者访问该页面,当页面被访问后,请求在受害者不知情的情况下以受害者的合法身份发起,并执行黑客所期待的动作。以下HTML代码提供了一个“删除产品”的功能:

<a href="http://www.shop.com/delProducts.php?id=100" "javascript:return confirm(&#39;Are you sure?&#39;)">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 = [&#39;vote&#39;,&#39;like&#39;,&#39;delete&#39;,&#39;download&#39;];
    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(&#39;+&#39;, &#39;.&#39;, 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 ? &#39; &#39; : $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&#39;t need to be very random
        $chars = &#39;ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-.&#39;;
        $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(&#39;+&#39;, &#39;.&#39;, 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(&#39;yii&#39;, &#39;Unable to verify your data submission.&#39;));
        }
        return true;
    }
    
    return false;
}
public function validateCsrfToken($token = null)
{
    $method = $this->getMethod();
    if (!$this->enableCsrfValidation || in_array($method, [&#39;GET&#39;, &#39;HEAD&#39;, &#39;OPTIONS&#39;], 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(&#39;.&#39;, &#39;+&#39;, $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(&#39;+&#39;, &#39;.&#39;, 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缓存解决方案的详细内容。更多信息请关注PHP中文网其他相关文章!

本站声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

热AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover

AI Clothes Remover

用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool

Undress AI Tool

免费脱衣服图片

Clothoff.io

Clothoff.io

AI脱衣机

AI Hentai Generator

AI Hentai Generator

免费生成ai无尽的。

热工具

记事本++7.3.1

记事本++7.3.1

好用且免费的代码编辑器

SublimeText3汉化版

SublimeText3汉化版

中文版,非常好用

禅工作室 13.0.1

禅工作室 13.0.1

功能强大的PHP集成开发环境

Dreamweaver CS6

Dreamweaver CS6

视觉化网页开发工具

SublimeText3 Mac版

SublimeText3 Mac版

神级代码编辑软件(SublimeText3)

Yii框架中的RESTful API开发 Yii框架中的RESTful API开发 Jun 21, 2023 pm 12:34 PM

Yii是一款基于PHP的高性能MVC框架,它提供了非常丰富的工具和功能,支持快速、高效地开发Web应用程序。其中,Yii框架的RESTfulAPI功能得到了越来越多开发者的关注和喜爱,因为使用Yii框架可以非常方便地构建出高性能、易扩展的RESTful接口,为Web应用的开发提供了强有力的支持。RESTfulAPI简介RESTfulAPI是一种基于

Yii框架中间件:为应用程序提供多重数据存储支持 Yii框架中间件:为应用程序提供多重数据存储支持 Jul 28, 2023 pm 12:43 PM

Yii框架中间件:为应用程序提供多重数据存储支持介绍中间件(middleware)是Yii框架中的一个重要概念,它为应用程序提供了多重数据存储支持。中间件的作用类似于一个过滤器,它能够在应用程序的请求和响应之间插入自定义代码。通过中间件,我们可以对请求进行处理、验证、过滤,然后将处理后的结果传递给下一个中间件或最终的处理程序。Yii框架中的中间件使用起来非常

PHP中如何使用Yii框架 PHP中如何使用Yii框架 Jun 27, 2023 pm 07:00 PM

随着Web应用程序的快速发展,现代Web开发已成为一项重要技能。许多框架和工具可用于开发高效的Web应用程序,其中Yii框架就是一个非常流行的框架。Yii是一个高性能、基于组件的PHP框架,它采用了最新的设计模式和技术,提供了强大的工具和组件,是构建复杂Web应用程序的理想选择。在本文中,我们将讨论如何使用Yii框架来构建Web应用程序。安装Yii框架首先,

使用Yii框架实现网页缓存和页面分块的步骤 使用Yii框架实现网页缓存和页面分块的步骤 Jul 30, 2023 am 09:22 AM

使用Yii框架实现网页缓存和页面分块的步骤引言:在Web开发过程中,为了提高网站的性能和用户体验,常常需要对页面进行缓存和分块处理。Yii框架提供了强大的缓存和布局功能,可以帮助开发者快速实现网页缓存和页面分块,本文将介绍如何使用Yii框架进行网页缓存和页面分块的实现。一、网页缓存开启网页缓存在Yii框架中,可以通过配置文件来开启网页缓存。打开主配置文件co

使用Yii框架创建游戏攻略网站 使用Yii框架创建游戏攻略网站 Jun 21, 2023 pm 01:45 PM

近年来,随着游戏行业的快速发展,越来越多的玩家开始寻找游戏攻略来帮助游戏过关。因此,创建一个游戏攻略网站可以让玩家们更加方便地获取游戏攻略,同时也能为玩家提供更好的游戏体验。在创建这样一个网站时,我们可以使用Yii框架来进行开发。Yii框架是一个基于PHP编程语言的Web应用开发框架。它具有高效、安全、扩展性强等特点,可以为我们更快速、高效地创建一个游戏攻略

Yii框架中间件:为应用程序添加日志记录和调试功能 Yii框架中间件:为应用程序添加日志记录和调试功能 Jul 28, 2023 pm 08:49 PM

Yii框架中间件:为应用程序添加日志记录和调试功能【引言】在开发Web应用程序时,我们通常需要添加一些附加功能以提高应用的性能和稳定性。Yii框架提供了中间件的概念,使我们能够在应用程序处理请求之前和之后执行一些额外的任务。本文将介绍如何使用Yii框架的中间件功能来实现日志记录和调试功能。【什么是中间件】中间件是指在应用程序处理请求之前和之后,对请求和响应做

在Yii框架中使用控制器(Controllers)处理Ajax请求的方法 在Yii框架中使用控制器(Controllers)处理Ajax请求的方法 Jul 28, 2023 pm 07:37 PM

在Yii框架中,控制器(Controllers)扮演着处理请求的重要角色。除了处理常规的页面请求之外,控制器还可以用于处理Ajax请求。本文将介绍在Yii框架中处理Ajax请求的方法,并提供代码示例。在Yii框架中,处理Ajax请求可以通过以下步骤进行:第一步,创建一个控制器(Controller)类。可以通过继承Yii框架提供的基础控制器类yiiwebCo

使用Yii框架中间件加密和解密敏感数据 使用Yii框架中间件加密和解密敏感数据 Jul 28, 2023 pm 07:12 PM

使用Yii框架中间件加密和解密敏感数据引言:在现代的互联网应用中,隐私和数据安全是非常重要的问题。为了确保用户的敏感数据不被未经授权的访问者获取,我们需要对这些数据进行加密。Yii框架为我们提供了一种简单且有效的方法来实现加密和解密敏感数据的功能。在本文中,我们将介绍如何使用Yii框架的中间件来实现这一目标。Yii框架简介Yii框架是一个高性能的PHP框架,

See all articles