跨站请求伪造已经死了!(译文)

在Web上使用Cross-Site Request Forgery进行劳作之后,我们终于有了一个合适的解决方案。 没有网站所有者的技术负担,没有困难的实施,部署简单,它是Same-Site Cookies。

与Web本身一样古老

跨站请求伪造,也称为CSRF或XSRF,基本上永远存在。 它源于网站必须向另一个站点发出请求的简单功能。 假设我在此页面中嵌入了以下表单。

1
2
3
4
<form action="https://your-bank.com/transfer" method="POST" id="stealMoney">
<input type="hidden" name="to" value="Scott Helme">
<input type="hidden" name="account" value="14278935">
<input type="hidden" name="amount" value="£1,000">

您的浏览器加载此页面,结果是上面的表单,然后我在页面上使用一个简单的JS提交。

1
document.getElementById("stealMoney").submit();

这就是CSRF的名称来源。 我正在伪造一个跨站点发送请求到您的银行。 这里真正的问题不是我发送了请求,而是您的浏览器会发送您的cookie。 该请求将把您当前持有的全部权限一并发送,这意味着如果您已登录到您的银行,您就需向我捐赠1,000英镑, 谢谢! 如果您没有登录,那么请求将是无害的,因为您无法在未登录的情况下转账。目前,您的银行可以通过几种方式来缓解这些CSRF攻击。

CSRF缓解措施

我不会详细说明这些,因为网上有大量有关此主题的信息,但我想快速介绍它们以显示实施它们的技术要求。

检查来源(Origin)

在服务端收到请求时,会向我们提供Origin头和Referer头来说明请求的来源。 您可以检查其中的一个或两个值,以查看请求是否源自不同的来源。 如果是跨域请求,则只需将其丢弃即可。 Origin和Referer头确实得到了一些防止浏览器的保护,以防止被篡改,但它们可能并不总是存在。

1
2
3
4
5
6
7
8
9
10
accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
accept-encoding: gzip, deflate, br
cache-control: max-age=0
content-length: 166
content-type: application/x-www-form-urlencoded
dnt: 1
origin: https://report-uri.io
referer: https://report-uri.io/login
upgrade-insecure-requests: 1
user-agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36

Anti-CSRF令牌

您可以使用两种不同的方式使用Anti-CSRF令牌,但原则保持不变。当访问者请求页面时,如上例中的转移金额页面,您将随机令牌嵌入到表单中。当真正的用户提交此表单时,将返回随机令牌,您可以检查它是否与您在表单中发出的令牌相匹配。在CSRF攻击情形中,攻击者永远无法获得此值,即使他们请求页面也无法获取此值,因为同源策略(SOP)会阻止攻击者读取包含令牌的响应。此方法运行良好,但要求站点跟踪Anti-CSRF令牌的发布和返回。类似的方法是将令牌嵌入到表单中,并向浏览器发出包含相同值的cookie。当真正的用户提交他们的表单时,cookie中的值和表单将在网站收到时匹配。当攻击者发送伪造请求时,浏览器将不会设置CSRF cookie,测试将失败。

1
2
3
4
5
6
<form action="https://report-uri.io/login/auth" method="POST">
<input type="hidden" name="csrf_token" value="d82c90fc4a14b01224gde6ddebc23bf0">
<input type="email" id="email" name="email">
<input type="password" id="password" name="password">
<button type="submit" class="btn btn-primary">Login</button>
</form>

问题

上述方法长期为我们提供了相当强大的CSRF保护。 检查Origin和Referer头不是100%可靠,并且大多数站点采用Anti-CSRF令牌方法的一些变体。 问题是,这些都对网站提出了某种要求来实施和维护解决方案。 它们可能不是世界上技术最复杂的东西,但我们仍然在构建一个解决方案,围绕浏览器做一些我们不希望它做的事情。 相反,为什么我们不告诉浏览器停止做我们不希望它做的事情?…现在我们可以!

Same-Site Cookies

您可能已经在我最近的一篇名为Tough Cookies的博客中看到过Same-Site Cookies,但我将在这里通过一些例子更深入地介绍它。 从本质上讲,Same-Site Cookies完全有效地抵消了CSRF攻击。Dead. Finito. Adios! 捕获我们在网络上真正需要的本质以赢得安全战,Same-Site Cookies易于部署,非常简单。 拿你现有的cookie:

1
Set-Cookie: sess=abc123; path=/

只需添加SameSite属性即可。

1
Set-Cookie: sess=abc123; path=/; SameSite=lax

你完成了。 说真的,就是这样! 在cookie上启用此属性将指示浏览器为此cookie提供某些保护。 有两种模式可以在Strict或Lax中启用此保护,具体取决于您想要获得的严重程度。

1
2
SameSite=Strict
SameSite=Lax

Strict 严格模式

将SameSite保护设置为严格模式显然是首选,但我们有两个选择的原因是并非所有站点都相同,也没有相同的要求。当在严格模式下操作时,浏览器根本不会在任何跨源请求上发送cookie,因此CSRF完全死在水中。您可能遇到的唯一问题是它也不会在顶级导航上发送cookie(更改地址栏中的URL)。如果我提供了https://facebook.com的链接,并且Facebook将SameSite cookie设置为严格模式,当您单击链接到打开Facebook时,您将无法登录。无论您是否已登录,在新标签页中打开它,无论您做什么,从该链接访问时都不会登录到Facebook。这对用户来说可能有点烦人和/或意外,但确实提供了令人难以置信的强大保护。 Facebook在这里需要做的是类似于亚马逊做的,他们有2个cookie。一种是一种“基本”cookie,可以将您识别为用户,并允许您拥有登录体验,但如果您想做一些敏感的事情,比如购买或更改帐户中的内容,则需要第二个cookie, “真正的”cookie,可以让你做重要的事情。在这种情况下,第一个cookie不会设置SameSite属性,因为它是一个“方便”的cookie,它实际上不允许你做任何敏感的事情,如果攻击者可以用它做出跨域请求,则没有任何反应。但是,第二个cookie(敏感cookie)将设置SameSite属性,攻击者就不能滥用权限到跨源请求中。这是用户和安全性的理想解决方案。但这并不总是可行的,因为我们希望SameSite cookie易于部署,这里还有第二种选择。

Lax 松散模式

将SameSite保护设置为Lax模式可以修复上述用户点击链接的严格模式中提到的问题,如果他们已经登录,则不会在目标站点上登录。在Lax模式下,只有一个例外允许cookie附加到使用安全HTTP方法的顶级导航。 “安全”HTTP方法在RFC 7321第4.2.1节中定义为GET,HEAD,OPTIONS和TRACE,我们对此处的GET方法感兴趣。 这意味着,当用户单击链接时,我们对https://facebook.com的顶级导航现在在浏览器发出请求时附加了SameSite标记的cookie,从而保持了预期的用户体验。 我们还完全受到基于POST的CSRF攻击的保护。 回到顶部的示例,此攻击仍然无法在Lax模式下工作。

1
2
3
4
<form action="https://your-bank.com/transfer" method="POST" id="stealMoney">
<input type="hidden" name="to" value="Scott Helme">
<input type="hidden" name="account" value="14278935">
<input type="hidden" name="amount" value="£1,000">

由于POST方法不被认为是安全的,因此浏览器不会在请求中附加cookie。 攻击者当然可以自由地将方法更改为“安全”方法并发出相同的请求。

1
2
3
4
<form action="https://your-bank.com/transfer" method="GET" id="stealMoney">
<input type="hidden" name="to" value="Scott Helme">
<input type="hidden" name="account" value="14278935">
<input type="hidden" name="amount" value="£1,000">

只要我们不接受GET请求代替POST请求,那么这种攻击是不可能的,但是在Lax模式下操作时需要注意。 此外,如果攻击者可以触发顶级导航或弹出新窗口,他们还可以使浏览器发出附加了cookie的GET请求。 这是在Lax模式下运营的权衡,我们保持用户体验不变,但也有较小的风险接受付款。

其他用途

此博客旨在使用SameSite Cookie缓解CSRF,但正如您可能已经猜到的那样,此机制也有其他用途。 规范中列出的第一个是跨站点脚本包含(XSSI),浏览器在此处请求资源,例如脚本,该脚本将根据用户是否经过身份验证而更改。 在跨站点请求方案中,攻击者不能滥用SameSite Cookie的环境权限来产生不同的响应。 此处详细介绍了一些有趣的计时攻击,那可以有效缓解滥用情况。

另一个不详细的有趣用途是防止在BEAST式压缩攻击(CRIME),BREACH),HEISTTIME)中泄露会话cookie的价值。 这是非常高级别的,但基本情况是MiTM可以强制浏览器通过他们喜欢的任何机制发出跨源请求并监视它们。 通过滥用请求有效负载大小的变化,攻击者可以通过改变浏览器发出的请求并在线路上观察它们的大小,一次一个字节地猜测会话ID值。 使用SameSite Cookies,浏览器不会在此类请求中包含cookie,因此攻击者无法猜测其值。

浏览器支持

随着浏览器中大多数新的安全功能,您可以期望Firefox或Chrome的领导这项功能,这里的情况也不例外。 自v51以来,Chrome已经支持Same-Site Cookies,这意味着Android上的Opera,Android浏览器和Chrome也有支持。 您可以在caniuse.com上查看列出当前支持的详细信息,Firefox也有一个bug,可以添加支持。 尽管支持尚未普及,但我们仍应将SameSite属性添加到cookie中。 理解它的浏览器将尊重设置并提供cookie额外的保护,而那些不理解它的人将完全忽略它并继续前行。 这里没有什么可失去的,它形成了一个非常好的防御深度方法。 我们可以考虑删除传统的反CSRF机制,但是在这些机制之上添加SameSite会给我们带来难以置信的强大防御。

原文:https://scotthelme.co.uk/csrf-is-dead/
原文发布日期:2017年2月20日
作者:Scott Helme
译者:帕奇式

翻译及转载获得作者许可:
licence