阅读 20

CSRF学习总结

一、CSRF原理

用户在A网站登陆后,浏览器保存其cookie。网站B使用某种方式请求A网站的接口时,带上了A网站的cookie,成功完成请求。

二、一个疑问

同源策略不能阻止CSRF的发生吗?
看同源策略的一些约束。(来自MDN:developer.mozilla.org/zh-CN/docs/…):

  1. 通常允许跨域写操作(Cross-origin writes)
    • 链接
    • 重定向
    • 表单提交。特定少数的HTTP请求需要添加 preflight。
  2. 通常允许跨域资源嵌入(Cross-origin embedding)。
    • script
    • link
    • img
    • iframe
    • <video>、<object>、<embed>、<applet>
  3. 通常不允许跨域读操作(Cross-origin reads)。但常可以通过内嵌资源来巧妙的进行读取访问
    • ajax跨域请求得到的结果将被拦截,不允许读操作
    • canvas 操作图片(e.g. 读取嵌入图片的高度和宽度)的时候会有跨域问题

同源策略通常不阻止请求发送,只拦截请求结果;并且,同源策略允许跨域资源的写操作和嵌入,举几个例子:
1、img标签发起

 <img src="http://bank.example/withdraw?amount=10000&for=hacker" /> 
复制代码

2、form提交

 <form action="http://bank.example/withdraw" method=POST>
     <input type="hidden" name="account" value="xiaoming" />            
     <input type="hidden" name="amount" value="10000" />
     <input type="hidden" name="for" value="hacker" />
 </form>
 <script> document.forms[0].submit(); </script> 
复制代码

3、link

 <a href="http://test.com/csrf/withdraw.php?amount=1000&for=hacker" taget="_blank">
     重磅消息!!
 <a/>
复制代码

上面这几个例子不报跨域是因为请求由 浏览器 控制,无法用 js 直接操作获得的结果,因此浏览器认为是安全的,不会报跨域的错。

来自知乎的总结(www.zhihu.com/question/31…

所以浏览器这个策略的本质是,一个域名的 JS ,在未经允许的情况下,不得读取另一个域名的内容。但浏览器并不阻止你向另一个域名发送请求。
### 三、CSRF特点概览 1、只可冒用cookie,无法获取到cookie里的具体内容

四、防护策略

1、浏览器

  • Samesite cookie

    • 用法:

      • strict值

        在任何跨域请求情况下,Cookie都不会被携带

      • lax值

        从外部引用的链接跳转到本站,可以保持用户的登录状态;但是对于有CSRF倾向的post请求,将不会发送cookie

      导航到目标网址的 GET 请求,只包括三种情况:链接,预加载请求,GET 表单。详见下表(表自 www.ruanyifeng.com/blog/2019/0…)。

      Set-Cookie: xxx=1; Samesite=Strict
      复制代码
    • 劣势:

      兼容性问题。

  • 同源判断

    可靠性:这些头部不能被程序代码修改(比如使用XSS中的javascript),只有浏览器有权设置他们。

    • Origin

      从HTTPS来的HTTP请求(即:https的网站协议降级访问http的链接),将会保留Origin头部。

      • 缺点:
        1. 少部分情况下,Origin头或Referrer头可能不存在(由于某些合法的原因,如保护用户隐私,或者浏览器的问题)
        2. 向可信域发送CORS跨域请求,IE11不会添加Origin头部。
        3. 重定向302跨域,Origin将不会被包含在重定向的请求头部中,因为它们被认为是敏感信息不能被发送到其他域。
        4. 某些涉及隐私的场景下,Origin被设置成“null”。
        5. 对于同源请求,Origin头部一般都会带上。但是在大多数情况下,只会被POST/DELETE/PUT这些请求方法带上。在GET请求中,做了一些修改状态的操作,那么这个方法将起不到原作用。 (详见我翻译的https://juejin.im/post/6844904053994979335)
    • Referer

      Origin头部不存在的时候,考虑使用Referer 在以下两种情况下,Referer 不会被发送:

      来源页面采用的协议为表示本地文件的 "file" 或者 "data" URI
      当前请求页面采用的是非安全协议,而来源页面采用的是安全协议(HTTPS)。

      参考以下表格控制Referer头部的发送策略

2、token防御:提交请求时,要求带上本域才能获取的信息

  • CSRF token

    • 用法:

      STEP1:
      服务端生成token,给到客户端(同步token需要存放于Session之中,然后在每次请求时把token从Session中拿出,下文详解)

      STEP2:
      提交请求的时候,客户端将token放在隐藏的form input中,或者放在请求头、请求parameter中。

      STEP3:
      服务端负责校验token。从session中取出token,与请求中的Token进行比对;或者使用加解密算法的原理作比较

    • 同步token

      • token的构造: 生成超大随机数,并存在session里
      • token的校验: 服务器比对token中的随机数
      • 缺点: 增加了服务端的存储压力;分布式场景下需将token存在公共数据库redis之类的
    • 基于加密算法的

      • token构造: 使用密钥key,将sessionId和时间戳通过加密算法加密,得到csrf token
      • token的校验: 将请求提交上来的token,使用密钥key解密,得到sessionId和时间戳。判断sessionId是否和当前请求用户的sessionId一致;比较时间戳和当前时间,用于判断token是否过期。
      • 优点: 不使用随机数,则不需要存储到数据库,服务器只需要使用密钥,解密token,进行sessionId的比对即可。
      • 缺点: 增加了服务器的计算量
  • 双重提交cookie (double submit cookie)

    cookie里的某些信息作为token

    • 原理:CSRF无法读取cookie的信息,只能冒用
    • 步骤:
      STEP1:把一个token写入cookie
      STEP2:发起请求的时候,从cookie种获取该token,在query、body或者header里带上这个token
      STEP3:服务器验证Cookie中的字段与URL参数中的字段是否一致,不一致则拒绝。能获取到cookie中的token,并且token值正确的请求,则可以判断是正常的本域请求。CSRF的请求无法做到这一点。
    • 优点:
      1)不使用session
      2)token存在客户端,减小服务器端的存储压力
      3)不需要逐个接口和页面加校验逻辑,统一拦截
    • 缺点:
      1)难以做到子域名隔离
      如果在子域名种下cookie,则在主域名中无法读取子域名的cookie;
      如果在主域名种下cookie,在子域名中可以读取,但是又可以修改主域名的cookie;
      子域名发生XSS攻击,攻击者修改主域的cookie之后,可在主域发起CSRF攻击;
      2)为了确保Cookie传输安全,需要确保整站使用HTTPS

【参考文章】