当前位置:网站首页>关于CSRF及其防御

关于CSRF及其防御

2022-06-09 10:47:00 Johnny丶me

关于CSRF

  • CSRF(Cross Site Request Forgy) 跨站请求伪造
  • 在用户不知情的情况下,其他网站对目标网站发出请求
  • 举个例子,比如你通过一个链接的点入,在其他网站上发表了一条动态
  • 也就是身份的盗用,这是一件非常可怕的事情
  • CSRF有个别称叫做"一点就爆炸",甚至,不需要点击,比如刷新一下
  • 只要能够产生请求就会存在CSRF攻击的可能
  • CSRF的攻击可以是多种多样的
    • 可以是表单,链接和图片等多种形式
    • 一次刷新,一个点击即可触发
    • 可以通过GET和POST的请求,而GET请求更致命,传播性更广
  • 一般CSRF可以称得上是一种网络蠕虫,传染性很强
  • 举个具体的代码示例
    // 创建一个表单
    document.write(` <form name="commentForm" target="csrf" method="post" action="目标网站的某一个接口地址"> <input name="postId" type="hidden" value="1"> <textarea name="content">来自CSRF!</textarea> </form>`
    );
    // 创建一个iframe
    var iframe = document.createElement('iframe');
    iframe.name = 'csrf';
    iframe.style.display = 'none'; // 隐藏状态
    document.body.appendChild(iframe);
    
    // 页面加载完成后提交表单
    setTimeout(function(){
          
        // 表单的target是iframe,会在iframe内进行提交和跳转, 当前页面没有任何变化
        document.querySelector('[name=commentForm]').submit();
    }, 1000);
    

CSRF攻击的原理

  • 主要涉及两个网站,一个是目标网站,一个是攻击者网站
  • 用户在目标网站进行登录,会有登录态session和一些凭证信息如cookies
  • 攻击者会通过一些手段, 比如链接,图片等方式利用用户在目标网站的身份, 向目标网站发送数据
  • 注意此时绕过了目标网站的前端,直接请求了目标网站的接口,举个具体的例子:
    • 用户只要在一个浏览器下登录过目标网站A,就会在A网站下存入身份标识,如cookie
    • 如果攻击者通过一封邮件,让用户点击邮件中的某个链接,此时这个链接打开了同样的浏览器
    • 该链接是A网站的某个接口,同时附带一些攻击者想要提交的数据
    • 因为用户登录过A网站,此时只要一点击这个链接就会自动发送攻击者想要提交的数据

CSRF攻击的危害

  • 用户完全不知情的情况下,利用用户登录态
  • 完成业务请求,如盗取用户资金(转账,消费)
  • 再比如,冒充用户发帖,用户背锅,这是直接危害
  • 同样间接地损害目标网站的声誉

CSRF攻击案例

  • QQ购买道具接口存在漏洞,导致道具连续购买,用户钱财不断被消耗
  • QQ音乐分享到腾讯微博歌单,发送一条带有蠕虫的微博消息,不断被传播
  • 当然,这些漏洞已经被修复了,我们应该警醒类似案例

CSRF如何防御

  • 通过CSRF攻击的原理知道访问目标网站时的referer为攻击者网站
  • 我们可以通过这个特征来做防御,一般而言,我们可以这样
    • 1 ) 在目标网站设置cookie时添加same-site属性
      • SameSite属性有两个值:Strict, Lax
      • Strict表示最为严格,完全禁止第三方Cookie,跨站点时,任何情况下都不会发送Cookie
      • Lax表示大多数情况也是不发送第三方Cookie,但是导航到目标网址的Get请求除外
      • 设置了Strict或Lax以后,基本就杜绝了CSRF攻击,当然,前提是用户浏览器支持SameSite属性
      • 网站可以选择显式关闭SameSite属性,将其设为None
      • 不过,前提是必须同时设置Secure属性(Cookie只能通过HTTPS协议发送), 否则无效
      • 但是SameSite属性兼容性并不是很乐观,所以并非普适地解决CSRF攻击
      • 在koajs中设置SameSite示例
        ctx.cookies.set('userId', user.id, {
                  sameSite: 'strict'})
        
    • 2 ) 在目标网站进行referer的过滤,只允许合法请求
      • 在哪里发起请求,referer就是谁,比如从A跳转到B,那么B的referer就是A
      • 如果referer是合法的地址,我们就通过,不合法我们就拒绝
      • 可以自定义合法referer列表,代码上也很好实现,做个严谨的判断即可
      • 注意, 如果是file协议访问是获取不到referer的,要通过http协议的形式才会有referer
    • 3 ) 在目标网站前端加入验证信息:验证码
      • 每次提交必须要有验证码,并且验证码相等
      • 在nodejs中关于生成图形验证码的没有原生的方法可以参考ccap的库
      • 这时候就可以有效阻碍CSRF攻击了,但是不可能所有的表单请求都要去做验证码校验
      • 通过验证码可以防御,但生产环境下使用并不理想,体验很差,代码就不作举例,比较麻烦
    • 4 ) 在目标网站前端加入验证信息:token
      • 让攻击者发起请求后无法直接获取,必须要经过网站前端
      • 后端生成一个csrftoken, 也就是一个随机字符串
      • 一个放在页面的表单中,一个放在cookies中
      • 校验cookies中的token和表单中的token是否一致
      • 一致表示正确直接放行,不一致说明存在错误或攻击
      • Django这个框架就是这么处理CSRF攻击的
      • 同样在普通的ajax请求中也可以这么校验,我们可以在页面的meta上存储token
      • 在正常发送ajax请求的时候获取token并且发送cookies中的值交给后端校验
      • 存在一个问题是多页面下只有最新打开的页面的表单能够提交,因为多个页面上的token不一致,而cookie动态变更
      • 解决方法有很多,比如页面token失效,校验不一致,再次请求新的页面token,可以有效的解决问题
      • 注意这里我们的两个token:一个是页面上的token,一个是cookies中的token

PHP防御CSRF

1 ) PHP中使用samesite这个cookies属性

  • PHP中打cookies的方式有两种:
    • 可以直接通过setcookie函数, 如 setcookie('test', '123') 这里并不支持SameSite属性的设置
    • 可以通过header函数来设置cookies, 如 header('Set-Cookie: test=123; SameSite=Lax');

2 ) 根据HTTP referer头

  • 获取referer头:var_dump($_SERVER['HTTP_REFERER'])
  • 具体用法可以参考
    if($_SERVER['HTTP_REFERER']) {
          
        // 判断referer是否符合合法域名, 可以有多种实现,可支持定义域名列表,下面只提供一种
        $isLegal = strpos($_SERVER['HTTP_REFERER'], 'http://localhost') === 0;
        var_dump(isLegal); // 将是否输出到页面
    }
    

3 ) 根据token处理

  • 可参考代码
    $csrfToken = rand(1000,9999); // 定义一个token, 随机数, 当然最好弄成更复杂的, 此处只作举例
    setcookie('csrfToken', $csrfToken); // 给站点打上相关cookie
    
    if($_POST['csrfToken'] === $_COOKIE['csrfToken']) {
          
        // ... 匹配了,处理自己的逻辑
    }
    
    <form method='post'>
        <input type='text' name='csrfToken' value="<?php echo $csrfToken;?>">
        <textarea name='content'>hello</textarea>
        <button type='submit'>提交</button>
    </form>
    

4 ) 根据验证码处理

  • 因为验证码这个方案用户体验并不是很好,在任何语言中处理都应作为最后的方案
  • 此处不再推荐
原网站

版权声明
本文为[Johnny丶me]所创,转载请带上原文链接,感谢
https://blog.csdn.net/Tyro_java/article/details/125154406