原文链接:https://www.yuque.com/1ncounter/courtyard/xqlqhr

什么是 CSRF 漏洞

CSRF(Cross Site Request Forgery,跨站请求伪造,也叫 XSRF)漏洞是由于未校验请求来源,导致攻击者可在第三方站点发起 HTTP 请求,并以受害者的目标网站登录态(cookie、session 等)请求,从而执行一些敏感的业务功能操作,比如更改密码、修改个人资料、关注好友。
用张时序图来解释会更清楚一些,我把常用的攻击方式也画了上去,如下图所示:
CSRF(转载) - 图1

CSRF 分类

从漏洞利用角度来分类的话,CSRF 可以分为 CSRF 读与 CSRF 写。

  • CSRF 读通过伪造请求来获取返回的敏感信息,比如用户资料;常见的就是 JSON 劫持,以及利用 Flash API 加载页面获取敏感信息。由于浏览器已经默认禁止 Flash,我就不介绍 Flash CSRF 的攻击手法了。
  • CSRF 写通过伪造请求去修改网站数据,比如修改密码、发表文章、发送消息等操作。

    JSON 劫持攻击

    JSON 劫持是一种特殊的 CSRF 攻击方式,本质上也是未对请求来源做有效校验导致的,它主要是用来窃取服务器返回的敏感信息。
    实现 JSON 劫持主要有两种攻击方式:覆写数据构造器和执行回调函数。
    覆写数据构造器
    若服务端返回的 JSON 数据中包含一个序列化数组,那攻击者就可以重定义数组构造器,以实现 JSON 数据的访问。比如 2006 年的 Gmail 就曾出现过 JSON 劫持联系人列表的漏洞,漏洞 CGI 位于: http://mail.google.com/mail/?url_scrubbed ,它会返回联系人列表的 JSON 数据,因此,可通过覆盖数组构造器来读取 JSON 数据。
    执行回调函数(JSONP)
    不同域名之间传递数据时,无法通过 JavaScript 直接跨域访问,因此需要在访问脚本的请求中指定一个回调函数,用于处理 JSON 数据。正因如此,攻击者也可以利用它来劫持其他域返回的数据。这种攻击方式是当前 JSON 劫持中最为常见的方式。

    CSRF 检测方法

    通过前面对 CSRF 原理的讲解,测试思路就很容易了:
  1. 抓包记录正常的 HTTP 请求;
  2. 分析 HTTP 请求参数是否可预测,以及相应的用途;
  3. 去掉或更改 referer 为第三方站点,然后重放请求;
  4. 判断是否达到与正常请求的同等效果,若是则可能存在 CSRF 漏洞,反之则不存在。

    防御 CSRF

    防御 CSRF 的关键思路就是令请求参数不可预测,所以常用的方法就是在敏感操作请求上使用 POST 代替 GET,然后添加验证码或 Token 进行验证。
    这里不推荐 referer(即请求头中的来源地址)限制方法,因为通过 javascript:// 伪协议就能以空 referer 的形式发起请求,很容易绕过限制。如果你直接禁止空 referer,一些移动 App 上的请求又可能无法完成,因为移动 App 上的 http/https 请求经常是空 referer。

    验证码

    在一些重要的敏感操作上设置验证码(短信、图片等等),比如更改密码(此场景下也可要求输入原密码,这也是不可预测值)、修改个人资料等操作时。

    Token 验证

    对于 CSRF 的防御,Token 验证无疑是最常用的方法,它对用户是无感知的,体验上比验证码好太多了。
    生成 Csrf Token 的算法,常常会取登录后 cookie 中的某值作为输入,然后采用一些加密/哈希算法生成,这也是为了方便后台校验和区分用户。
    除了 Cookie Token,还可以使用伪随机值的 Session Token,即服务端生成一个伪随机数,存储到 SESSION 中,然后返回给用户的页面中隐藏此 Token;等用户提交后,再拿它与存储在 SESSION 的 Token 值比较。这是当前比较常用的 Token 生成与校验方式。

    总结

    理解 CSRF 的难点就在于,它不是为了窃取用户的登录凭证(cookie 等),而是直接利用用户已经登录过网站而留存在浏览器上的凭证,诱使用户访问恶意链接,借助登录凭证去执行敏感操作,整个攻击过程是在用户的浏览器上完成的。