为什么将CSRF预防令牌放在Cookie中很常见?

我试图通过CSRF和适当的方式来了解整个问题,以防止它。 (资源我已阅读,理解并同意:OWASP CSRF预防CHGat表,关于CSRF的问题。)

据我所知,围绕CSRF的漏洞是通过假设(从Web服务器的角度来看)传入的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的处理方式,但是会把它推广到更多的JS客户端。)

为什么使用Set-Cookie作为CSRF令牌的下游传输很常见/为什么这是一个好主意? 我想所有这些框架的作者仔细考虑了他们的选择,并没有得到这个错误。 但乍一看,使用cookie来解决cookies本质上的设计限制看起来很愚蠢。 事实上,如果您使用cookie作为往返传输(Set-Cookie:header下游服务器告诉浏览器CSRF令牌,并且Cookie:header上游浏览器将其返回给服务器),则您将重新引入此漏洞正在尝试修复。

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


一个很好的理由是,一旦收到CSRF cookie,就可以在客户端脚本的整个应用程序中使用,以便在常规表单和AJAX POST中使用。 这对于AngularJS所使用的大量JavaScript应用程序来说是有意义的(使用AngularJS并不要求应用程序将是单页面应用程序,所以当状态需要在不同的页面请求之间流动时,CSRF值通常不能在浏览器中持续存在)。

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

请求正文方法

  • 用户成功登录。
  • 服务器发布auth cookie。
  • 用户点击导航到表单。
  • 如果尚未为此会话生成,则服务器将生成CSRF令牌,并将其存储在用户会话中并将其输出到隐藏字段。
  • 用户提交表单。
  • 服务器检查隐藏字段与会话存储的令牌匹
  • 优点:

  • 简单实施。
  • 适用于AJAX。
  • 与表单一起工作。
  • Cookie实际上可以只是HTTP。
  • 缺点:

  • 所有表单都必须以HTML格式输出隐藏字段。
  • 任何AJAX POSTs也必须包含该值。
  • 该页面必须事先知道它需要CSRF令牌,因此它可以将其包含在页面内容中,因此所有页面都必须在某处包含令牌值,这可能会耗费大量网站实施时间。
  • 自定义HTTP标题(下游)

  • 用户成功登录。
  • 服务器发布auth cookie。
  • 用户点击导航到表单。
  • 在浏览器中加载页面,然后发出AJAX请求来检索CSRF令牌。
  • 服务器生成CSRF标记(如果尚未为会话生成),将其存储在用户会话中并将其输出到标题。
  • 用户提交表单(令牌通过隐藏字段发送)。
  • 服务器检查隐藏字段与会话存储的令牌匹
  • 优点:

  • 适用于AJAX。
  • Cookie可以是HTTP Only。
  • 缺点:

  • 没有AJAX请求就无法获取标题值。
  • 所有表单必须具有动态添加到其HTML的值。
  • 任何AJAX POSTs也必须包含该值。
  • 该页面必须首先发出AJAX请求才能获取CSRF令牌,因此每次都会有额外的往返行程。
  • 也可以将令牌简单地输出到保存额外请求的页面。
  • 自定义HTTP标头(上游)

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

  • 适用于AJAX。
  • Cookie可以是HTTP Only。
  • 缺点:

  • 不适用于表单。
  • 所有AJAX POST都必须包含标题。
  • 自定义HTTP头(上游和下游)

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

  • 适用于AJAX。
  • Cookie可以是HTTP Only。
  • 缺点:

  • 不适用于表单。
  • 所有AJAX POSTs也必须包含该值。
  • 该页面必须首先发出AJAX请求才能获取CRSF令牌,因此每次都会有额外的往返行程。
  • 设置Cookie

  • 用户成功登录。
  • 服务器发布auth cookie。
  • 用户点击导航到表单。
  • 服务器生成CSRF令牌,将其存储在用户会话中并将其输出到cookie。
  • 用户通过AJAX或通过HTML表单提交表单。
  • 服务器检查自定义标题(或隐藏表单域)与会话存储标记匹配。
  • Cookie在浏览器中可用于其他AJAX和表单请求,而无需向服务器提出额外请求来检索CSRF令牌。
  • 优点:

  • 简单实施。
  • 适用于AJAX。
  • 与表单一起工作。
  • 不一定需要AJAX请求来获取cookie值。 任何HTTP请求都可以检索它,它可以通过JavaScript附加到所有表单/ AJAX请求。
  • 一旦CSRF令牌被检索到,因为它存储在一个cookie中,这个值可以被重用,而不需要额外的请求。
  • 缺点:

  • 所有表单必须具有动态添加到其HTML的值。
  • 任何AJAX POSTs也必须包含该值。
  • 该cookie将针对每个请求提交(即所有GET,图像,CSS,JS等,不涉及CSRF流程),从而增加请求大小。
  • Cookie不能只是HTTP。
  • 所以cookie方法是相当动态的,它提供了一种简单的方法来检索cookie值(任何HTTP请求)并使用它(JS可以自动将值添加到任何表单中,并且可以在AJAX请求中作为标题或作为形式值)。 一旦接收到会话的CSRF令牌,就不需要重新生成它,因为采用CSRF攻击的攻击者没有检索该令牌的方法。 如果恶意用户尝试使用上述任何方法读取用户的CSRF令牌,则相同来源策略将阻止此操作。 如果恶意用户试图检索CSRF令牌服务器端(例如通过curl ),那么这个令牌将不会被关联到同一用户帐户,因为请求中将缺少受害者的auth会话cookie(这将是攻击者的 - 因此它将不会与服务器端与受害者会话相关联)。

    除了同步器令牌模式外,还有双重提交Cookie CSRF预防方法,当然它使用cookie来存储一种CSRF令牌。 这更容易实现,因为它不需要CSRF令牌的任何服务器端状态。 CSRF令牌实际上可以是使用此方法时的标准认证cookie,并且该值通常与Cookie一起通过cookie提交,但该值也会在隐藏字段或标头中重复,攻击者无法将其复制为他们无法读取价值。 然而,建议选择另一个cookie,除了身份验证cookie之外,还可以通过标记HttpOnly来确保身份验证cookie的安全。 所以这是你为什么使用基于cookie的方法找到CSRF预防的另一个常见原因。


    使用cookie向客户端提供CSRF令牌不允许攻击成功,因为攻击者无法读取cookie的值,因此无法放入服务器端CSRF验证所要求的位置。

    攻击者将能够通过请求标头中的身份验证令牌cookie和CSRF cookie向服务器发送请求。 但是服务器并没有将CSRF标记作为请求头中的cookie来查找,而是查看请求的有效载荷。 即使攻击者知道将CSRF令牌放入有效载荷的位置,他们也必须读取它的值才能将其置于那里。 但浏览器的跨域策略会阻止从目标网站读取任何cookie值。

    相同的逻辑不适用于auth令牌cookie,因为服务器在请求标头中预期它,并且攻击者不必做任何特殊的事情就可以将它放在那里。


    关于答案,我的最佳猜测:考虑如何从服务器获取CSRF令牌到浏览器的3个选项。

  • 在请求正文中(不是HTTP头)。
  • 在自定义HTTP标头中,不是Set-Cookie。
  • 作为Cookie,在Set-Cookie标头中。
  • 我认为第一个请求主体(虽然我在问题中关联了Express教程),但它不适合各种各样的情况; 不是每个人都动态地生成每个HTTP响应; 最终需要将令牌放入生成的响应中的方式可能差异很大(以隐藏的形式输入;在JS代码的片段或其他JS代码可访问的变量中;甚至可能在URL中,尽管这似乎通常是不好的地方放置CSRF令牌)。 因此,尽管可以进行一些定制工作,但#1是一个难以实施一刀切的方法。

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

    这留下了第三个,一个由Set-Cookie头部携带的cookie,作为一种易于在所有情况下使用的方法(任何人的服务器都能够设置每个请求的cookie标头,并且不管什么样的数据在请求主体中)。 所以尽管它的缺点,它是框架广泛实施的最简单的方法。

    链接地址: http://www.djcxy.com/p/21673.html

    上一篇: Why is it common to put CSRF prevention tokens in cookies?

    下一篇: Security for a single admin website?