为什么在CSR中放置CSRF预防令牌很常见?

met*_*att 248 security cookies csrf owasp web

我试图了解CSRF的整个问题以及预防它的适当方法.(资源我已阅读,理解并同意:OWASP CSRF预防报告表,关于CSRF的问题.)

据我了解,CSRF的漏洞是通过假设(从网络服务器的角度来看)传入HTTP请求中的有效会话cookie反映经过身份验证的用户的意愿而引入的.但是,原始域的所有cookie都被浏览器神奇地附加到请求上,因此实际上所有服务器都可以通过请求中存在的有效会话cookie来推断该请求来自具有经过身份验证的会话的浏览器; 它无法进一步假设该浏览器中运行的代码,或者它是否真正反映了用户的意愿.防止这种情况的方法是在请求中包含其他身份验证信息("CSRF令牌"),这些信息由浏览器的自动cookie处理以外的某些方式携带.然后,松散地说,会话cookie验证用户/浏览器,并且CSRF令牌验证在浏览器中运行的代码.

简而言之,如果您使用会话cookie来验证Web应用程序的用户,您还应该为每个响应添加一个CSRF令牌,并在每个(变异)请求中要求匹配的CSRF令牌.然后,CSRF令牌从服务器到浏览器进行往返回服务器,向服务器证明发出请求的页面是由该服务器批准(甚至由该服务器生成).

关于我的问题,这是关于在该往返中用于该CSRF令牌的特定传输方法.

看起来很常见(例如在AngularJS,Django,Rails中)将CSRF令牌从服务器发送到客户端作为cookie(即在Set-Cookie头中),然后让客户端中的Javascript将其从cookie中删除并附加它作为单独的XSRF-TOKEN标头发送回服务器.

(另一种方法是由例如Express推荐的方法,其中由服务器生成的CSRF令牌通过服务器端模板扩展包含在响应主体中,直接附加到将其提供回服务器的代码/标记,例如作为一个隐藏的表单输入.这个例子是一个更多的Web 1.0-doh的做事方式,但是可以很好地推广给更多JS的客户端.)

为什么使用Set-Cookie作为CSRF令牌的下游传输是如此常见/为什么这是一个好主意?我想所有这些框架的作者都仔细考虑了他们的选择,并没有弄错.但乍一看,使用cookie解决基本上对cookie的设计限制似乎很愚蠢.实际上,如果您使用cookie作为往返传输(Set-Cookie:服务器的下游标头告诉浏览器CSRF令牌,而Cookie:上游标题,浏览器将其返回给服务器)您将重新引入漏洞正试图解决.

我意识到上面的框架不会使用cookie来进行CSRF令牌的整个往返; 他们使用Set-Cookie下游,然后在上游使用其他东西(例如X-CSRF-Token标头),这确实可以关闭漏洞.但即使使用Set-Cookie作为下游传输也可能具有误导性和危险性; 浏览器现在将CSRF令牌附加到每个请求,包括真正的恶意XSRF请求; 充其量只会使请求变得比它需要的更大,而在最坏的情况下,一些善意但误导的服务器代码实际上可能会尝试使用它,这将是非常糟糕的.此外,由于CSRF令牌的实际预期接收者是客户端Javascript,这意味着此cookie不能仅使用http保护.因此,在Set-Cookie标头中向下游发送CSRF令牌对我来说似乎非常不理想.

Sil*_*Fox 236

您接触到的一个很好的理由是,一旦收到CSRF cookie,它就可以在客户端脚本中的整个应用程序中使用,以便在常规表单和AJAX POST中使用.这在JavaScript重型应用程序中是有意义的,例如AngularJS使用的应用程序(使用AngularJS不要求应用程序将是单页面应用程序,因此在状态需要在CSRF值的不同页面请求之间流动时会很有用通常不能在浏览器中持续存在).

在典型应用程序中考虑以下场景和过程,以了解您描述的每种方法的优缺点.这些基于同步器令牌模式.

请求身体方法

  1. 用户成功登录.
  2. 服务器发出auth cookie.
  3. 用户单击以导航到表单.
  4. 如果尚未为此会话生成,则服务器会生成CSRF令牌,将其存储在用户会话中并将其输出到隐藏字段.
  5. 用户提交表单.
  6. 服务器检查隐藏字段匹配会话存储令牌.

好处:

  • 易于实施.
  • 适用于AJAX.
  • 适用于表格.
  • Cookie实际上可以是HTTP Only.

缺点:

  • 所有表单都必须以HTML格式输出隐藏字段.
  • 任何AJAX POST都必须包含该值.
  • 页面必须事先知道它需要CSRF令牌,因此它可以将其包含在页面内容中,因此所有页面都必须在某处包含令牌值,这可能会使实现大型站点的时间过长.

自定义HTTP标头(下游)

  1. 用户成功登录.
  2. 服务器发出auth cookie.
  3. 用户单击以导航到表单.
  4. 在浏览器中加载页面,然后发出AJAX请求以检索CSRF令牌.
  5. 服务器生成CSRF令牌(如果尚未为会话生成),将其存储在用户会话中并将其输出到标头.
  6. 用户提交表单(令牌通过隐藏字段发送).
  7. 服务器检查隐藏字段匹配会话存储令牌.

好处:

缺点:

  • 没有AJAX请求获取标头值不起作用.
  • 所有表单必须动态地将值添加到其HTML中.
  • 任何AJAX POST都必须包含该值.
  • 该页面必须首先发出一个AJAX请求以获取CSRF令牌,因此这意味着每次额外的往返行程.
  • 也可以简单地将令牌输出到页面,这将节省额外的请求.

自定义HTTP标头(上游)

  1. 用户成功登录.
  2. 服务器发出auth cookie.
  3. 用户单击以导航到表单.
  4. 如果尚未为此会话生成,则服务器会生成CSRF令牌,将其存储在用户会话中,并将其输出到某处的页面内容中.
  5. 用户通过AJAX提交表单(令牌通过标头发送).
  6. 服务器检查自定义标头匹配会话存储标记

好处:

缺点:

  • 不适用于表单.
  • 所有AJAX POST都必须包含标题.

自定义HTTP标头(上游和下游)

  1. 用户成功登录.
  2. 服务器发出auth cookie.
  3. 用户单击以导航到表单.
  4. 在浏览器中加载页面,然后发出AJAX请求以检索CSRF令牌.
  5. 服务器生成CSRF令牌(如果尚未为会话生成),将其存储在用户会话中并将其输出到标头.
  6. 用户通过AJAX提交表单(令牌通过标头发送).
  7. 服务器检查自定义标头匹配会话存储标记

好处:

缺点:

  • 不适用于表单.
  • 所有AJAX POST都必须包含该值.
  • 该页面必须首先发出一个AJAX请求以获取CRSF令牌,因此这意味着每次额外的往返行程.

设置Cookie

  1. 用户成功登录.
  2. 服务器发出auth cookie.
  3. 用户单击以导航到表单.
  4. 服务器生成CSRF令牌,将其存储在用户会话中并将其输出到cookie.
  5. 用户通过AJAX或HTML表单提交表单.
  6. 服务器检查自定义标头(或隐藏的表单字段)匹配会话存储的标记.
  7. Cookie在浏览器中可用于其他AJAX和表单请求,而无需向服务器请求检索CSRF令牌.

好处:

  • 易于实施.
  • 适用于AJAX.
  • 适用于表格.
  • 不一定需要AJAX请求来获取cookie值.任何HTTP请求都可以检索它,并且可以通过JavaScript将其附加到所有表单/ AJAX请求.
  • 一旦检索到CSRF令牌,因为它存储在cookie中,可以在没有其他请求的情况下重用该值.

缺点:

  • 所有表单必须动态地将值添加到其HTML中.
  • 任何AJAX POST都必须包含该值.
  • 将为每个请求(即所有未参与CSRF过程的图像,CSS,JS等的GET)提交cookie ,从而增加请求大小.
  • Cookie不能仅限HTTP.

所以cookie方法是相当动态的,提供了一种简单的方法来检索cookie值(任何HTTP请求)并使用它(JS可以自动将值添加到任何表单中,并且它可以作为标题或作为标题在AJAX请求中使用形式价值).一旦收到会话的CSRF令牌,就不需要重新生成它,因为使用CSRF漏洞的攻击者没有检索此令牌的方法.如果恶意用户尝试使用上述任何方法读取用户的CSRF令牌,则同源策略将阻止此操作.如果恶意用户试图检索CSRF令牌服务器端(例如,通过curl),则此令牌将不会与同一用户帐户关联,因为请求中将缺少受害者的auth会话cookie(这将是攻击者的 - 因此它赢了不要将服务器端与受害者的会话相关联.

除了Synchronizer Token Pattern之外,还有Double Submit Cookie CSRF预防方法,当然使用cookie来存储一种CSRF令牌.这更容易实现,因为它不需要CSRF令牌的任何服务器端状态.实际上,CSRF令牌可以是使用此方法时的标准身份验证cookie,并且此值通常会像请求一样通过cookie提交,但该值也会在隐藏字段或标头中重复,攻击者无法将其复制为他们首先无法读取价值.除了身份验证cookie之外,建议选择另一个cookie,以便通过标记为HttpOnly来保护身份验证cookie.因此,这是您使用基于cookie的方法找到CSRF预防的另一个常见原因.

  • 在我对CSRF攻击的理解中,伪造者确实有*my*session cookie.好吧,他们实际上并没有*看到*cookie,但他们有能力在他们的伪造请求中提供它,因为请求来自我的浏览器,我的浏览器提供我的会话cookie.因此,从服务器的角度来看,仅会话cookie无法区分合法请求和伪造请求.这实际上是我们试图阻止的攻击.顺便说一句,感谢您耐心地谈论这一点,特别是如果我对此感到困惑的话. (27认同)
  • 他们可以提供auth cookie,但是他们无法读取包含CSRF令牌的响应. (8认同)
  • @metamatt对于死灵来说很抱歉,但是我会为那些徘徊的人做这件事.据我所知,攻击者通常无法访问响应.CSRF主要用于_cause副作用_,而不是直接收集数据.例如,CSRF攻击脚本可能会强制特权用户升级攻击者的权限,禁用安全设置,或强制登录的PayPal用户将转移发送到特定的电子邮件地址.在这些情况下,攻击者都不关心响应,这仍然发送到受害者的浏览器; 只有攻击的结果. (8认同)
  • 我不确定我是否理解"AJAX请求如何检索CSRF令牌"("自定义标题:下游"部分中的步骤4)可以安全地完成; 因为这是一个单独的请求,服务器不知道它来自谁; 它如何知道泄露CSRF令牌是安全的?在我看来,如果您无法从初始页面加载中获取令牌,则会丢失(这使得自定义下游响应头不可能). (7认同)
  • 因为伪造者不会有会话cookie.他们可能有自己的会话cookie,但由于CSRF令牌与会话相关联,因此他们的CSRF令牌与受害者不匹配. (5认同)
  • 我的意思是,什么阻止其他人(一个想成为CSRF的伪造者)将请求发回服务器(包括会话cookie)?这个伪造者不必从客户端窃取它(这是同源政策会阻止的); 伪造者可以很好地询问服务器,服务器会提供它.所以我仍然没有看到服务器如何在主页加载*的单独请求中安全地提供CSRF令牌*. (4认同)
  • 你有'任何AJAX POSTs也必须包含值.' 在所有情况下都被列为劣势.我想说这不是任何一种方法的缺点,而是一般要求CSRF保护. (3认同)
  • @mak 如果攻击者具有 XSS 能力,那么他们就不需要 CSRF。XSS 几乎总是具有更大的影响。 (2认同)
  • @t3_ 因为 JavaScript 需要访问该值。 (2认同)
  • @TRIG 这是一篇关于这个主题的精彩文章:https://portswigger.net/research/web-storage-the-lesser-evil-for-session-tokens (2认同)
  • @ApoorvaJain 如果实现了 XSS,那么防御 CSRF 的所有赌注都将失败。攻击者可以使用脚本来获取他们需要包含在任何请求中的值,当然 cookie 会自动发送。 (2认同)

Ton*_*gfa 52

使用cookie向客户端提供CSRF令牌不允许成功攻击,因为攻击者无法读取cookie的值,因此无法将其置于服务器端CSRF验证所需的位置.

攻击者将能够使用请求标头中的身份验证令牌cookie和CSRF cookie向服务器发出请求.但是服务器没有在请求头中查找CSRF令牌作为cookie,它正在查看请求的有效负载.即使攻击者知道将CSRF令牌放在有效负载中的哪个位置,他们也必须读取它的值才能将其放在那里.但浏览器的跨域策略阻止从目标网站读取任何cookie值.

相同的逻辑不适用于身份验证令牌cookie,因为服务器期望它在请求标头中,并且攻击者不必做任何特殊的事情来将其放在那里.

  • @developius 发送 cookie 不足以满足 CSRF 保护。cookie 包含服务器发送的 csrf 令牌。合法客户端必须从 cookie 中读取 csrf 令牌,然后将其传递到请求中的某个地方,例如标头或负载中。CSRF 保护检查 cookie 中的值是否与请求中的值匹配,否则请求被拒绝。因此,攻击者确实需要读取cookie。 (4认同)
  • 这个答案非常切中题主的问题,而且非常清楚。+1 谢谢。 (4认同)
  • CSRF用于在客户端验证用户,而不是像您建议的那样通常从服务器端保护站点. (2认同)

met*_*att 9

我对答案的最佳猜测是:考虑如何将CSRF令牌从服务器下载到浏览器的这3个选项.

  1. 在请求正文中(不是HTTP标头).
  2. 在自定义HTTP标头中,而不是Set-Cookie.
  3. 作为cookie,在Set-Cookie标头中.

我认为第一个,请求正文(虽然我在问题中链接的Express教程演示),但对于各种各样的情况来说并不那么容易; 并非所有人都动态生成每个HTTP响应; 你最终需要在生成的响应中放置令牌的地方可能差异很大(在隐藏的表单输入中;在JS代码的片段中或其他JS代码可访问的变量;甚至可能在URL中看起来通常是一个不好的地方放置CSRF令牌).因此,虽然可以使用一些自定义,但#1是一个难以做到一刀切的方法.

第二个,自定义标头,很有吸引力,但实际上并不起作用,因为虽然JS可以获取它调用的XHR的标头,但它无法获取它加载的页面的标头.

这留下了第三个,一个由Set-Cookie标头携带的cookie,作为一种易于在所有情况下使用的方法(任何人的服务器都能够设置每个请求的cookie头,并且无论何种类型的数据在请求正文中).因此,尽管它有缺点,但它是框架广泛实施的最简单方法.

  • 我可能会说明显的,这是否意味着cookie不能正确无误? (7认同)
  • 仅适用于 ajax 请求(其中 JS 需要知道 csrf cookie 的值,以便在第二个通道中的下一个请求(作为表单数据或标题)中重新发送它)。如果会话 cookie 已经是 HttpOnly(以防止 XSS),则没有理由要求 csrf 令牌为 HttpOnly,因为 csrf 令牌在没有关联会话的情况下本身没有价值。 (2认同)