我是靠谱客的博主 欢呼电话,这篇文章主要介绍Yii中的csrf,现在分享给大家,希望可以做个参考。

什么是CSRF攻击

百度百科

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

防御措施

检查Referer字段

HTTP头中有一个Referer字段,这个字段用以标明请求来源于哪个地址。在处理敏感数据请求时,通常来说,Referer字段应和请求的地址位于同一域名下。以上文银行操作为例,Referer字段地址通常应该是转账按钮所在的网页地址,应该也位于www.examplebank.com之下。而如果是CSRF攻击传来的请求,Referer字段会是包含恶意网址的地址,不会位于www.examplebank.com之下,这时候服务器就能识别出恶意的访问。
这种办法简单易行,工作量低,仅需要在关键访问处增加一步校验。但这种办法也有其局限性,因其完全依赖浏览器发送正确的Referer字段。虽然http协议对此字段的内容有明确的规定,但并无法保证来访的浏览器的具体实现,亦无法保证浏览器没有安全漏洞影响到此字段。并且也存在攻击者攻击某些浏览器,篡改其Referer字段的可能。

添加校验token

由于CSRF的本质在于攻击者欺骗用户去访问自己设置的地址,所以如果要求在访问敏感数据请求时,要求用户浏览器提供不保存在cookie中,并且攻击者无法伪造的数据作为校验,那么攻击者就无法再运行CSRF攻击。这种数据通常是窗体中的一个数据项。服务器将其生成并附加在窗体中,其内容是一个伪随机数。当客户端通过窗体提交请求时,这个伪随机数也一并提交上去以供校验。正常的访问时,客户端浏览器能够正确得到并传回这个伪随机数,而通过CSRF传来的欺骗性攻击中,攻击者无从事先得知这个伪随机数的值,服务端就会因为校验token的值为空或者错误,拒绝这个可疑请求。

Yii2中csrf的实现原理

Yii2是使用token来预防csrf攻击的。

在配置文件中开启

复制代码
1
2
3
'request' => [ 'enableCsrfValidation' => true, ],

校验token

在所有控制器继承的controller基类中,有个请求回调函数在请求处理前验证token。
在下面代码中,先判断是否开启了csrf验证,在判断目前异常处理器是否捕获了异常,最后才校验token

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

接下来看看validateCsrfToken函数

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public function validateCsrfToken($clientSuppliedToken = null) { // 先判断http请求方法,如果是get、head、options方法则直接跳过,不认证。 $method = $this->getMethod(); // only validate CSRF token on non-"safe" methods https://tools.ietf.org/html/rfc2616#section-9.1.1 if (!$this->enableCsrfValidation || in_array($method, ['GET', 'HEAD', 'OPTIONS'], true)) { return true; } // 获取服务器原始的token $trueToken = $this->getCsrfToken(); // 如果已经拿到了浏览器带过来的token,则直接与上面的服务器端原始的token进行比较 if ($clientSuppliedToken !== null) { return $this->validateCsrfTokenInternal($clientSuppliedToken, $trueToken); } // 否则从请求body或者请求head中获取浏览器端的token,再进行比较 return $this->validateCsrfTokenInternal($this->getBodyParam($this->csrfParam), $trueToken) || $this->validateCsrfTokenInternal($this->getCsrfTokenFromHeader(), $trueToken); }

先看看服务器端原始的token是怎么来的.

从注释中可以看到,服务端生成的原始token一般是保存在session或者cookie中。然后浏览器会通过html表单的隐藏字段
或者http的header带给服务器,然后两者比较进行验证。

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/** * Returns the token used to perform CSRF validation. * * This token is generated in a way to prevent [BREACH attacks](http://breachattack.com/). It may be passed * along via a hidden field of an HTML form or an HTTP header value to support CSRF validation. * @param bool $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) { // 因为这个函数在一次请求中会多次调用,所以会保存token在_csrfToken字段中 if ($this->_csrfToken === null || $regenerate) { $token = $this->loadCsrfToken(); if ($regenerate || empty($token)) { // 重新生成token $token = $this->generateCsrfToken(); } // 对token进行简单加密 $this->_csrfToken = Yii::$app->security->maskToken($token); } return $this->_csrfToken; }

看看loadCsrfToken函数,即从cookie或者session中获取服务端的token

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
/** * 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() { // 默认是保存在cookie中的,比较不安全 if ($this->enableCsrfCookie) { return $this->getCookies()->getValue($this->csrfParam); } return Yii::$app->getSession()->get($this->csrfParam); }

那么token是怎么生成的呢?看下面代码可知token就是一个随机字符串[A-Za-z0-9_-],生成后保存在cookie中或者session中

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

校验函数则比较简单了,就是解密后进行比较

复制代码
1
2
3
4
5
6
7
8
private function validateCsrfTokenInternal($clientSuppliedToken, $trueToken) { if (!is_string($clientSuppliedToken)) { return false; } $security = Yii::$app->security; return $security->compareString($security->unmaskToken($clientSuppliedToken), $security->unmaskToken($trueToken)); }

最后看看加解密函数

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
/** * Masks a token to make it uncompressible. * Applies a random mask to the token and prepends the mask used to the result making the string always unique. * Used to mitigate BREACH attack by randomizing how token is outputted on each request. * @param string $token An unmasked token. * @return string A masked token. * @since 2.0.12 */ public function maskToken($token) { // The number of bytes in a mask is always equal to the number of bytes in a token. $mask = $this->generateRandomKey(StringHelper::byteLength($token)); return StringHelper::base64UrlEncode($mask . ($mask ^ $token)); } /** * Unmasks a token previously masked by `maskToken`. * @param string $maskedToken A masked token. * @return string An unmasked token, or an empty string in case of token format is invalid. * @since 2.0.12 */ public function unmaskToken($maskedToken) { $decoded = StringHelper::base64UrlDecode($maskedToken); $length = StringHelper::byteLength($decoded) / 2; // Check if the masked token has an even length. if (!is_int($length)) { return ''; } return StringHelper::byteSubstr($decoded, $length, $length) ^ StringHelper::byteSubstr($decoded, 0, $length); }

值得一提的是,存在cookie中也是比较不安全的,但是要比存在session中高效??貌似这两种安全性差不多

最后,查看getCsrfToken函数的调用,发现有多次调用,比如登录的时候调用并且刷新token,生成表单的时候回调用。

总结

防止csrf攻击的主要核心在于如何确认该请求是否是自己方的代码产生的,而不是别人恶意代码伪造的。
token相当于一个身份的象征,一个请求对应一个token,从而确保请求的身份的认定。

最后

以上就是欢呼电话最近收集整理的关于Yii中的csrf的全部内容,更多相关Yii中内容请搜索靠谱客的其他文章。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(81)

评论列表共有 0 条评论

立即
投稿
返回
顶部