csrf,中文名称:跨站请求伪造,可以在百度上搜索资料,详细了解这一方面的概念。对于我们是非常有帮助的。
yii2的csrf的实现功能是在yii\web\request类实现功能的。
request类中的属性,默认是true的。
public $enableCsrfValidation = true;
所以我们在配置文件中的request组件中可以配置该值
request => [
'enableCookieValidation' => true,
]
这是全局有效的,也就是说每一个post的请求,都会启用csrf的防御攻击的功能,即进行验证。
简单的说,整个访问策略如下:

(1)通过Yii::$app->request->csrfToken 第一次访问获取csrfToken时,直接到getCsrfToken()访问

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->_csrfToken都会等于null,$regenerate 默认等于false。所以接着执行$token = $this->loadCsrfToken()这个函数。

protected function loadCsrfToken()
{
if ($this->enableCsrfCookie) {
return $this->getCookies()->getValue($this->csrfParam);
} else {
return Yii::$app->getSession()->get($this->csrfParam);
}
}

去cookie中获取$_COOKIE['_csrf']这个token,由于第一次访问这个token肯定不会存在,故返回null。所以就会去执行$this->generateCsrfToken()。

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']中。这样子在网站的根目录/,COOKIE就存在了这个token了,并且返回这个token。只要我们没有关闭整个网页,那么这个$_COOKIE['_csrf']的值就不会变,也就代表本机客户端的唯一凭证。
接着再看一下getCsrfToken()函数里的这几行代码:
$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)));
这里是利用token和字符串,通过64位进行编码加密生成_csrfToken并且返回,也就是获取csrfToken这个值了。
(2)第二次访问时,Yii::$app->request->csrfToken,由于$token = $this->loadCsrfToken()这个函数访问已经可以获取到token,也就是获取网站的$_COOKIE['_csrf']的值,所以不会再次重新生成的,所以接着进行64位编码加密生成_csrfToken并且返回。
(3)那么我们需要将数据post过去的时候,我们会在yii\web\conreoller的类中的beforeAction($action)函数进行验证

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()这个函数验证

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->csrfToken这个值的时候是加密的了,所以现在要解密)。解密之后的值如果和$trueToken相同的话就返回true。

再看看$this->validateCsrfTokenInternal($this->getCsrfTokenFromHeader(), $trueToken)这一句是通过$this->getCsrfTokenFromHeader()获取head中的csrfToken的值,再进行解密,解密之后的值如果和$trueToken相同的话就返回true。

注意的是return ($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进行比较,那么如果相等就说明访问是无攻击性的,是本站的访问。如果访问不通过,说明可能删改了一些信息,是不安全的。

yii2的防御csrf攻击机制的更多相关文章

  1. Spring mvc拦截器防御CSRF攻击

    CSRF(具体参考百度百科) CSRF(Cross-site request forgery跨站请求伪造,也被称为“One Click Attack”或者Session Riding,通常缩写为CSR ...

  2. Web安全之CSRF攻击的防御措施

    Web安全之CSRF攻击的防御措施   CSRF是什么? Cross Site Request Forgery,中文是:跨站点请求伪造. CSRF攻击者在用户已经登录目标网站之后,诱使用户访问一个攻击 ...

  3. 防御CSRF的方法有哪些(一) HTTP 头中自定义属性并验证 CSRF跨站域请求伪造攻击

    CSRF (Cross Site Request Forgery, 跨站域请求伪造)是一种网络的攻击方式,该攻击可以在受害者毫不知情的情况下以受害者名义伪造请求发送给受攻击站点,从而在并未授权的情况下 ...

  4. CSRF攻击与防御

    CSRF是什么 CSRF在百度百科中是这么说的:“CSRF(Cross-site request forgery跨站请求伪造,也被称为“one click attack”或者session ridin ...

  5. CSRF 攻击原理和防御方法

    1. CSRF攻击原理 CSRF(Cross site request forgery),即跨站请求伪造.我们知道XSS是跨站脚本攻击,就是在用户的浏览器中执行攻击者的脚本,来获得其cookie等信息 ...

  6. csrf 攻击和防御

    CSRF概念:CSRF跨站点请求伪造(Cross—Site Request Forgery),跟XSS攻击一样,存在巨大的危害性,你可以这样来理解:       攻击者盗用了你的身份,以你的名义发送恶 ...

  7. CSRF攻击与防御(写得非常好)

    转自:http://blog.csdn.net/stpeace/article/details/53512283 CSRF概念:CSRF跨站点请求伪造(Cross—Site Request Forge ...

  8. 转!!CSRF攻击与防御(写得非常好)

    CSRF概念:CSRF跨站点请求伪造(Cross—Site Request Forgery),跟XSS攻击一样,存在巨大的危害性,你可以这样来理解:       攻击者盗用了你的身份,以你的名义发送恶 ...

  9. 19、Flask实战第19天:CSRF攻击与防御

    CSRF攻击原理 网站是通过cookie来实现登录功能的.而cookie只要存在浏览器中,那么浏览器在访问这个cookie的服务器的时候,就会自动的携带cookie信息到服务器上去.那么这时候就存在一 ...

随机推荐

  1. ArcGis Server manager 忘记用户名和密码

    ArcGIS 10.1及以后版本重置Server Manager账户密码:(1)找到arcgis server的安装目录,目录指向\ArcGIS\Server\tools\passwordreset文 ...

  2. ubuntu19.04 redis启动和停止及连接

    1.启动停止 如果以(sudo apt install redis-server)方式安装 启动: sudo srevice redis start 停止:     sudo srevice redi ...

  3. js中的闭包理解

    闭包是一个比较抽象的概念,尤其是对js新手来说.书上的解释实在是比较晦涩,对我来说也是一样. 但是他也是js能力提升中无法绕过的一环,几乎每次面试必问的问题,因为在回答的时候.你的答案的深度,对术语的 ...

  4. 删除表 (truncate 、delete 、drop)

    来源:http://www.cnblogs.com/dieyaxianju/p/7238936.html 删除表中内容分为三种情况. 1.删除表中所有数据,但保留表结构(可用以下两个语句): trun ...

  5. Java中new一个子类对象的同时并不会自动创建一个父类对象

    首先重申一个概念:子类会继承父类所有非私有成员变量和方法,包括父类的构造方法 当创建一个子类对象时,首先开辟内存,然后调用类的构造函数,这里的构造函数由两部分组成,一部分是从父类继承而来的父类的构造方 ...

  6. oracle 启动

    Oracle数据库启动过程及状态详解(nomount.mount和open) 先来简要了解一下Oracle数据库体系架构以便于后面深入理解,Oracle Server主要由实例(instance)和数 ...

  7. input设置为disabled,表单无法提交后台解决方法

    <input name="country" id="country" size=12 value="disabled提交时得不到该值 " ...

  8. [Luogu]中位数

    Description Luogu1168 Solution 一种神奇的做法:开一个大根堆和小根堆,保证大根堆比小根堆多1个元素,且大根堆堆顶元素比小根堆堆顶元素小,那么大根堆堆顶就是中位数.插入的时 ...

  9. C++ 跨dll传递string类型参数执行出错问题

    今天遇到一个问题,在一个dll工程中定义了一个返回值为string,参数为string的函数,然后在一个测试工程中调用,Release模式下一切正常Debug模式下整个函数的执行到return之前都毫 ...

  10. Docker+JMeter单机版+File Browser

    基于JMeter5.1.1+File Browser2.1.0  JMeter发起压测  File Browser作为文件服务器 一.目录结构: Dockerfile文件: FROM ubuntu:1 ...