当前位置: 首页 > 后端技术 > PHP

Csrf的那些事儿

时间:2023-03-29 17:18:09 PHP

什么是csrf?csrf的全称:crosssiterequestforgery(跨站请求伪造)。第三方站点利用正常站点的登录凭据绕过后端认证,达到攻击站点的目的。为什么会有csrf攻击?说这个之前,我们还得知道一个东西,cookie,浏览器一般用它来存储用户登录凭证。当有数据需要发送给相关站点时,它会在浏览器中带上该站点的相关cookie信息,以达到身份验证的目的。我们需要知道的几点:1.csrf一般发生在跨站请求上,因为第三方站点更容易被“操作”。2.cookie信息无法被窃取,只能“用”常用的csrf防御方式1.csrftoken方式是一种比较常见的方式:在提交的内容或请求头中隐藏一个token值,token值为1-time,保证其不可预测性,然后在服务端验证token值。缺点:对于前后端分离的站点,token值不能直接渲染到前端页面。2.图片验证码3.手机验证码这些防御措施是为了增加破解难度,提高系统的安全性。Yii2.0中csrftoken的实现1.页面渲染request->csrfToken?>">2.生成令牌保护函数generateCsrfToken(){$token=Yii::$app->getSecurity()->generateRandomString();如果($this->enableCsrfCookie){$config=$this->csrfCookie;$config['name']=$this->csrfParam;$config['value']=$token;Yii::$app->getResponse()->getCookies()->add(newCookie($config));}else{Yii::$app->getSession()->set($this->csrfParam,$token);}return$token;}publicfunctiongenerateRandomString($length=32){$bytes=$this->generateRandomKey($length);//base64_encode()返回的'='字符总是被丢弃,因为//它们保证在base64_encode()输出中的位置$length之后。返回strtr(substr(base64_encode($bytes),0,$length),'+/','_-');}1。服务器行生成一个长度为322的随机字符串,将1)中的字符串进行base64编码,然后截取前32个字符的字符串,将'+','/'替换3.将2)中的字符串保存到session中(这里假设服务器使用session来存储token值)。以上3个步骤生成服务器使用的令牌值。返回给客户端的时候,也需要做一些计算=$this->loadCsrfToken())===null){$token=$this->generateCsrfToken();}//掩码不需要非常随机$chars='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-.';$mask=substr(str_shuffle(str_shuffle(5)),0,self::CSRF_MASK_LENGTH);//+符号稍后可能会被解码为空格,这将导致验证失败$this->_csrfToken=str_replace('+','.',base64_encode($mask.$this->xorTokens($token,$mask)));}return$this->_csrfToken;}这一步主要是对serversession中使用的token进行加密,不直接暴露给client真正的token处理如下:1.生成一个8位的随机maskmask2。token和mask进行异或运算,将mask拼接在字符串的前面3.将2)中生成的字符串进行base64编码,将'+'替换为'.'这样,页面上的csrftoken就生成了。那么服务端如何验证csrftoken呢?3.验证csrftokenprivatefunctionvalidateCsrfTokenInternal($token,$trueToken){$token=base64_decode(str_replace('.','+',$token));$n=StringHelper::byteLength($token);如果($n<=self::CSRF_MASK_LENGTH){返回假;}$mask=StringHelper::byteSubstr($token,0,self::CSRF_MASK_LENGTH);$token=StringHelper::byteSubstr($token,self::CSRF_MASK_LENGTH,$n-self::CSRF_MASK_LENGTH);$token=$this->xorTokens($mask,$token);return$token===$trueToken;}1.获取客户端csrftoken值,替换'.'加上'+',然后做base64解码2.取出第一个8位的字符串,这个字符串就是对应的maskmask3。从第9位到最后就是4次异或运算后的结果,将3)中的字符串与掩码进行异或运算,此时得到解密后的token值。5.将令牌值与会话中的令牌进行比较。如果一致,则csrftoken验证通过。否则,验证将失败。4.总结Yii2.0的csrftoken生成规则是随机生成一个字符串token,然后和mask异或拼接,再做base64编码。解码规则是对生成进行逆运算。这里巧妙的地方是与掩码进行异或运算,异或运算可以反转。