yii2的防御csrf攻击机制
csrf,中文名称:跨站请求伪造,可以在百度上搜索资料,详细了解这一方面的概念。对于我们是非常有帮助的。
yii2的csrf的实现功能是在yii\web\request类实现功能的。
request类中的属性,默认是true的。
public 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; } |
每一次访问regenerate 默认等于false。所以接着执行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中获取this->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']的值就不会变,也就代表本机客户端的唯一凭证。
接着再看一下getCsrfToken()函数里的这几行代码:
mask = substr(str_shuffle(str_repeat(this->_csrfToken = str_replace('+', '.', base64_encode(this->xorTokens(mask)));
这里是利用token和字符串,通过64位进行编码加密生成_csrfToken并且返回,也就是获取csrfToken这个值了。
(2)第二次访问时,Yii::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,那就的认证。
this->loadCsrfToken();这个获取完整COOKIE['_csrf']的真实存在的token。
看看this->getBodyParam(trueToken);
里面的这一句this->csrfParam)。就是获取post过来的csrfToken的值或者表单的值,然后validateCsrfTokenInternal(trueToken),这个函数将csrfToken进行解密(因为之前通过Yii::trueToken相同的话就返回true。
再看看this->getCsrfTokenFromHeader(), this->getCsrfTokenFromHeader()获取head中的csrfToken的值,再进行解密,解密之后的值如果和this->validateCsrfTokenInternal(this->csrfParam), this->validateCsrfTokenInternal(trueToken));
这一句是判断(this->getBodyParam(trueToken) || this->getCsrfTokenFromHeader(), $trueToken))通过解密认证后就等效于return (true || false)的模式,也就是通过||判定括号里的真假,只要有一个true,则返回true
所以说一旦生成token并保存在COOKIE['_csrf']中,那么每一次在访问时,就会以这个token作为一个基准进行数据加密随意生成一个csrfToken,然后返回给表单中。当post数据过来的时候,就得将这个csrfToken传递过来,然后进行解密,再和COOKIE['_csrf']的token进行比较,那么如果相等就说明访问是无攻击性的,是本站的访问。如果访问不通过,说明可能删改了一些信息,是不安全的。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 基于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最大的设计失误
· 单元测试从入门到精通