Web 安全 之 CSRF

Cross-site request forgery (CSRF)

在本节中,咱们将解释什么是跨站请求伪造,并描述一些常见的 CSRF 漏洞示例,同时说明如何防护 CSRF 攻击。html

什么是 CSRF

跨站请求伪造(CSRF)是一种 web 安全漏洞,它容许攻击者诱使用户执行他们不想执行的操做。攻击者进行 CSRF 可以部分规避同源策略。web

CSRF 攻击能形成什么影响

在成功的 CSRF 攻击中,攻击者会使受害用户无心中执行某个操做。例如,这多是更改他们账户上的电子邮件地址、更改密码或进行资金转帐。根据操做的性质,攻击者可能可以彻底控制用户的账户。若是受害用户在应用程序中具备特权角色,则攻击者可能可以彻底控制应用程序的全部数据和功能。跨域

CSRF 是如何工做的

要使 CSRF 攻击成为可能,必须具有三个关键条件:浏览器

  • 相关的动做。攻击者有理由诱使应用程序中发生某种动做。这多是特权操做(例如修改其余用户的权限),也多是针对用户特定数据的任何操做(例如更改用户本身的密码)。
  • 基于 Cookie 的会话处理。执行该操做涉及发出一个或多个 HTTP 请求,应用程序仅依赖会话cookie 来标识发出请求的用户。没有其余机制用于跟踪会话或验证用户请求。
  • 没有不可预测的请求参数。执行该操做的请求不包含攻击者没法肯定或猜想其值的任何参数。例如,当致使用户更改密码时,若是攻击者须要知道现有密码的值,则该功能不会受到攻击。

假设应用程序包含一个容许用户更改其邮箱地址的功能。当用户执行此操做时,会发出以下 HTTP 请求:安全

POST /email/change HTTP/1.1
Host: vulnerable-website.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 30
Cookie: session=yvthwsztyeQkAPzeQ5gHgTvlyxHfsAfE

email=wiener@normal-user.com

这个例子符合 CSRF 要求的条件:服务器

  • 更改用户账户上的邮箱地址的操做会引发攻击者的兴趣。执行此操做后,攻击者一般可以触发密码重置并彻底控制用户的账户。
  • 应用程序使用会话 cookie 来标识发出请求的用户。没有其余标记或机制来跟踪用户会话。
  • 攻击者能够轻松肯定执行操做所需的请求参数的值。

具有这些条件后,攻击者能够构建包含如下 HTML 的网页:cookie

<html>
  <body>
    <form action="https://vulnerable-website.com/email/change" method="POST">
      <input type="hidden" name="email" value="pwned@evil-user.net" />
    </form>
    <script>
      document.forms[0].submit();
    </script>
  </body>
</html>

若是受害用户访问了攻击者的网页,将发生如下状况:session

  • 攻击者的页面将触发对易受攻击的网站的 HTTP 请求。
  • 若是用户登陆到易受攻击的网站,其浏览器将自动在请求中包含其会话 cookie(假设 SameSite cookies 未被使用)。
  • 易受攻击的网站将以正常方式处理请求,将其视为受害者用户发出的请求,并更改其电子邮件地址。

注意:虽然 CSRF 一般是根据基于 cookie 的会话处理来描述的,但它也出如今应用程序自动向请求添加一些用户凭据的上下文中,例如 HTTP Basic authentication 基本验证和 certificate-based authentication 基于证书的身份验证。并发

如何构造 CSRF 攻击

手动建立 CSRF 攻击所需的 HTML 可能很麻烦,尤为是在所需请求包含大量参数的状况下,或者在请求中存在其余异常状况时。构造 CSRF 攻击的最简单方法是使用 Burp Suite Professional(付费软件) 中的 CSRF PoC generatorapp

如何传递 CSRF

跨站请求伪造攻击的传递机制与反射型 XSS 的传递机制基本相同。一般,攻击者会将恶意 HTML 放到他们控制的网站上,而后诱使受害者访问该网站。这能够经过电子邮件或社交媒体消息向用户提供指向网站的连接来实现。或者,若是攻击被放置在一个流行的网站(例如,在用户评论中),则只需等待用户上钩便可。

请注意,一些简单的 CSRF 攻击使用 GET 方法,而且能够经过易受攻击网站上的单个 URL 彻底自包含。在这种状况下,攻击者可能不须要使用外部站点,而且能够直接向受害者提供易受攻击域上的恶意 URL 。在前面的示例中,若是可使用 GET 方法执行更改电子邮件地址的请求,则自包含的攻击以下所示:

![](https://vulnerable-website.com/email/change?email=pwned@evil-user.net)

防护 CSRF 攻击

防护 CSRF 攻击最有效的方法就是在相关请求中使用 CSRF token ,此 token 应该是:

  • 不可预测的,具备高熵的
  • 绑定到用户的会话中
  • 在相关操做执行前,严格验证每种状况

可与 CSRF token 一块儿使用的附加防护措施是 SameSite cookies

常见的 CSRF 漏洞

最有趣的 CSRF 漏洞产生是由于对 CSRF token 的验证有问题。

在前面的示例中,假设应用程序在更改用户密码的请求中须要包含一个 CSRF token

POST /email/change HTTP/1.1
Host: vulnerable-website.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 68
Cookie: session=2yQIDcpia41WrATfjPqvm9tOkDvkMvLm

csrf=WfF1szMUHhiokx9AHFply5L2xAOfjRkE&email=wiener@normal-user.com

这看上去好像能够防护 CSRF 攻击,由于它打破了 CSRF 须要的必要条件:应用程序再也不仅仅依赖 cookie 进行会话处理,而且请求也包含攻击者没法肯定其值的参数。然而,仍然有多种方法能够破坏防护,这意味着应用程序仍然容易受到 CSRF 的攻击。

CSRF token 的验证依赖于请求方法

某些应用程序在请求使用 POST 方法时正确验证 token ,但在使用 GET 方法时跳过了验证。

在这种状况下,攻击者能够切换到 GET 方法来绕过验证并发起 CSRF 攻击:

GET /email/change?email=pwned@evil-user.net HTTP/1.1
Host: vulnerable-website.com
Cookie: session=2yQIDcpia41WrATfjPqvm9tOkDvkMvLm

CSRF token 的验证依赖于 token 是否存在

某些应用程序在 token 存在时正确地验证它,可是若是 token 不存在,则跳过验证。

在这种状况下,攻击者能够删除包含 token 的整个参数,从而绕过验证并发起 CSRF 攻击:

POST /email/change HTTP/1.1
Host: vulnerable-website.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 25
Cookie: session=2yQIDcpia41WrATfjPqvm9tOkDvkMvLm

email=pwned@evil-user.net

CSRF token 未绑定到用户会话

有些应用程序不验证 token 是否与发出请求的用户属于同一会话。相反,应用程序维护一个已发出的 token 的全局池,并接受该池中出现的任何 token 。

在这种状况下,攻击者可使用本身的账户登陆到应用程序,获取有效 token ,而后在 CSRF 攻击中使用本身的 token 。

CSRF token 被绑定到非会话 cookie

在上述漏洞的变体中,有些应用程序确实将 CSRF token 绑定到了 cookie,但与用于跟踪会话的同一个 cookie 不绑定。当应用程序使用两个不一样的框架时,很容易发生这种状况,一个用于会话处理,另外一个用于 CSRF 保护,这两个框架没有集成在一块儿:

POST /email/change HTTP/1.1
Host: vulnerable-website.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 68
Cookie: session=pSJYSScWKpmC60LpFOAHKixuFuM4uXWF; csrfKey=rZHCnSzEp8dbI6atzagGoSYyqJqTz5dv

csrf=RhV7yQDO0xcq9gLEah2WVbmuFqyOq7tY&email=wiener@normal-user.com

这种状况很难利用,但仍然存在漏洞。若是网站包含任何容许攻击者在受害者浏览器中设置 cookie 的行为,则可能发生攻击。攻击者可使用本身的账户登陆到应用程序,获取有效的 token 和关联的 cookie ,利用 cookie 设置行为将其 cookie 放入受害者的浏览器中,并在 CSRF 攻击中向受害者提供 token 。

注意:cookie 设置行为甚至没必要与 CSRF 漏洞存在于同一 Web 应用程序中。若是所控制的 cookie 具备适当的范围,则能够利用同一整体 DNS 域中的任何其余应用程序在目标应用程序中设置 cookie 。例如,staging.demo.normal-website.com 域上的 cookie 设置函数能够放置提交到 secure.normal-website.com 上的 cookie 。

CSRF token 仅要求与 cookie 中的相同

在上述漏洞的进一步变体中,一些应用程序不维护已发出 token 的任何服务端记录,而是在 cookie 和请求参数中复制每一个 token 。在验证后续请求时,应用程序只需验证在请求参数中提交的 token 是否与在 cookie 中提交的值匹配。这有时被称为针对 CSRF 的“双重提交”防护,之因此被提倡,是由于它易于实现,而且避免了对任何服务端状态的须要:

POST /email/change HTTP/1.1
Host: vulnerable-website.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 68
Cookie: session=1DQGdzYbOJQzLP7460tfyiv3do7MjyPw; csrf=R8ov2YBfTYmzFyjit8o2hKBuoIjXXVpa

csrf=R8ov2YBfTYmzFyjit8o2hKBuoIjXXVpa&email=wiener@normal-user.com

在这种状况下,若是网站包含任何 cookie 设置功能,攻击者能够再次执行 CSRF 攻击。在这里,攻击者不须要得到本身的有效 token 。他们只需发明一个 token ,利用 cookie 设置行为将 cookie 放入受害者的浏览器中,并在 CSRF 攻击中向受害者提供此 token 。

基于 Referer 的 CSRF 防护

除了使用 CSRF token 进行防护以外,有些应用程序使用 HTTP Referer 头去防护 CSRF 攻击,一般是验证请求来自应用程序本身的域名。这种方法一般不太有效,并且常常会被绕过。

注意:HTTP Referer 头是一个可选的请求头,它包含连接到所请求资源的网页的 URL 。一般,当用户触发 HTTP 请求时,好比单击连接或提交表单,浏览器会自动添加它。然而存在各类方法,容许连接页面保留或修改 Referer 头的值。这一般是出于隐私考虑。

Referer 的验证依赖于其是否存在

某些应用程序当请求中有 Referer 头时会验证它,可是若是没有的话,则跳过验证。

在这种状况下,攻击者能够精心设计其 CSRF 攻击,使受害用户的浏览器在请求中丢弃 Referer 头。实现这一点有多种方法,但最简单的是在托管 CSRF 攻击的 HTML 页面中使用 META 标记:

<meta name="referrer" content="never">

Referer 的验证能够被规避

某些应用程序以一种能够被绕过的方式验证 Referer 头。例如,若是应用程序只是验证 Referer 是否包含本身的域名,那么攻击者能够将所需的值放在 URL 的其余位置:

http://attacker-website.com/csrf-attack?vulnerable-website.com

若是应用程序验证 Referer 中的域以预期值开头,那么攻击者能够将其做为本身域的子域:

http://vulnerable-website.com.attacker-website.com/csrf-attack

CSRF tokens

在本节中,咱们将解释什么是 CSRF token,它们是如何防护的 CSRF 攻击,以及如何生成和验证CSRF token 。

什么是 CSRF token

CSRF token 是一个惟一的、秘密的、不可预测的值,它由服务端应用程序生成,并以这种方式传输到客户端,使得它包含在客户端发出的后续 HTTP 请求中。当发出后续请求时,服务端应用程序将验证请求是否包含预期的 token ,并在 token 丢失或无效时拒绝该请求。

因为攻击者没法肯定或预测用户的 CSRF token 的值,所以他们没法构造出一个应用程序验证所需所有参数的请求。因此 CSRF token 能够防止 CSRF 攻击。

CSRF token 应该如何生成

CSRF token 应该包含显著的熵,而且具备很强的不可预测性,其一般与会话令牌具备相同的特性。

您应该使用加密强度伪随机数生成器(PRNG),该生成器附带建立时的时间戳以及静态密码。

若是您须要 PRNG 强度以外的进一步保证,能够经过将其输出与某些特定于用户的熵链接来生成单独的令牌,并对整个结构进行强哈希。这给试图分析令牌的攻击者带来了额外的障碍。

如何传输 CSRF token

CSRF token 应被视为机密,并在其整个生命周期中以安全的方式进行处理。一种一般有效的方法是将令牌传输到使用 POST 方法提交的 HTML 表单的隐藏字段中的客户端。提交表单时,令牌将做为请求参数包含:

<input type="hidden" name="csrf-token" value="CIwNZNlR4XbisJF39I8yWnWX9wX4WFoz" />

为了安全起见,包含 CSRF token 的字段应该尽早放置在 HTML 文档中,最好是在任何非隐藏的输入字段以前,以及在 HTML 中嵌入用户可控制数据的任何位置以前。这能够对抗攻击者使用精心编制的数据操纵 HTML 文档并捕获其部份内容的各类技术。

另外一种方法是将令牌放入 URL query 字符串中,这种方法的安全性稍差,由于 query 字符串:

  • 记录在客户端和服务器端的各个位置;
  • 容易在 HTTP Referer 头中传输给第三方;
  • 能够在用户的浏览器中显示在屏幕上。

某些应用程序在自定义请求头中传输 CSRF token 。这进一步防止了攻击者预测或捕获另外一个用户的令牌,由于浏览器一般不容许跨域发送自定义头。然而,这种方法将应用程序限制为使用 XHR 发出受 CSRF 保护的请求(与 HTML 表单相反),而且在许多状况下可能被认为过于复杂。

CSRF token 不该在 cookie 中传输。

如何验证 CSRF token

当生成 CSRF token 时,它应该存储在服务器端的用户会话数据中。当接收到须要验证的后续请求时,服务器端应用程序应验证该请求是否包含与存储在用户会话中的值相匹配的令牌。不管请求的HTTP 方法或内容类型如何,都必须执行此验证。若是请求根本不包含任何令牌,则应以与存在无效令牌时相同的方式拒绝请求。


XSS vs CSRF

在本节中,咱们将解释 XSSCSRF 之间的区别,并讨论 CSRF token 是否有助于防护 XSS 攻击。

XSS 和 CSRF 之间有啥区别

跨站脚本攻击 XSS 容许攻击者在受害者用户的浏览器中执行任意 JavaScript 。

跨站请求伪造 CSRF 容许攻击者伪造受害用户执行他们不打算执行的操做。

XSS 漏洞的后果一般比 CSRF 漏洞更严重:

  • CSRF 一般只适用于用户可以执行的操做的子集。一般,许多应用程序都实现 CSRF 防护,可是忽略了暴露的一两个操做。相反,成功的 XSS 攻击一般能够执行用户可以执行的任何操做,而无论该漏洞是在什么功能中产生的。
  • CSRF 能够被描述为一个“单向”漏洞,由于尽管攻击者能够诱导受害者发出 HTTP 请求,但他们没法从该请求中检索响应。相反,XSS 是“双向”的,由于攻击者注入的脚本能够发出任意请求、读取响应并将数据传输到攻击者选择的外部域。

CSRF token 可否防护 XSS 攻击

一些 XSS 攻击确实能够经过有效使用 CSRF token 来进行防护。假设有一个简单的反射型 XSS 漏洞,其能够被利用以下:

https://insecure-website.com/status?message=<script>/*+Bad+stuff+here...+*/</script>

如今,假设漏洞函数包含一个 CSRF token :

https://insecure-website.com/status?csrf-token=CIwNZNlR4XbisJF39I8yWnWX9wX4WFoz&message=<script>/*+Bad+stuff+here...+*/</script>

若是服务器正确地验证了 CSRF token ,并拒绝了没有有效令牌的请求,那么该令牌确实能够防止此 XSS 漏洞的利用。这里的关键点是“跨站脚本”的攻击中涉及到了跨站请求,所以经过防止攻击者伪造跨站请求,该应用程序可防止对 XSS 漏洞的轻度攻击。

这里有一些重要的注意事项:

  • 若是反射型 XSS 漏洞存在于站点上任何其余不受 CSRF token 保护的函数内,则能够以常规方式利用该 XSS 漏洞。
  • 若是站点上的任何地方都存在可利用的 XSS 漏洞,则能够利用该漏洞使受害用户执行操做,即便这些操做自己受到 CSRF token 的保护。在这种状况下,攻击者的脚本能够请求相关页面获取有效的 CSRF token,而后使用该令牌执行受保护的操做。
  • CSRF token 不保护存储型 XSS 漏洞。若是受 CSRF token 保护的页面也是存储型 XSS 漏洞的输出点,则能够以一般的方式利用该 XSS 漏洞,而且当用户访问该页面时,将执行 XSS 有效负载。

SameSite cookies

某些网站使用 SameSite cookies 防护 CSRF 攻击。

这个 SameSite 属性可用于控制是否以及如何在跨站请求中提交 cookie 。经过设置会话 cookie 的属性,应用程序能够防止浏览器默认自动向请求添加 cookie 的行为,而无论cookie 来自何处。

这个 SameSite 属性在服务器的 Set-Cookie 响应头中设置,该属性能够设为 Strict 严格或者 Lax 松懈。例如:

SetCookie: SessionId=sYMnfCUrAlmqVVZn9dqevxyFpKZt30NN; SameSite=Strict;

SetCookie: SessionId=sYMnfCUrAlmqVVZn9dqevxyFpKZt30NN; SameSite=Lax;

若是 SameSite 属性设置为 Strict ,则浏览器将不会在来自其余站点的任何请求中包含cookie。这是最具防护性的选择,但它可能会损害用户体验,由于若是登陆的用户经过第三方连接访问某个站点,那么他们将不会登陆,而且须要从新登陆,而后才能以正常方式与站点交互。

若是 SameSite 属性设置为 Lax ,则浏览器将在来自另外一个站点的请求中包含cookie,但前提是知足如下两个条件:

  • 请求使用 GET 方法。使用其余方法(如 POST )的请求将不会包括 cookie 。
  • 请求是由用户的顶级导航(如单击连接)产生的。其余请求(如由脚本启动的请求)将不会包括 cookie 。

使用 SameSiteLax 模式确实对 CSRF 攻击提供了部分防护,由于 CSRF 攻击的目标用户操做一般使用 POST 方法实现。这里有两个重要的注意事项:

  • 有些应用程序确实使用 GET 请求实现敏感操做。
  • 许多应用程序和框架可以容忍不一样的 HTTP 方法。在这种状况下,即便应用程序自己设计使用的是 POST 方法,但它实际上也会接受被切换为使用 GET 方法的请求。

出于上述缘由,不建议仅依赖 SameSite Cookie 来抵御 CSRF 攻击。当其与 CSRF token 结合使用时,SameSite cookies 能够提供额外的防护层,并减轻基于令牌的防护中的任何缺陷。

image

相关文章
相关标签/搜索