为什么将CSRF预防标记放在Cookie中很常见?

我试图用CSRF理解整个问题并采取适当的方式来预防它。 (资源我已经阅读,理解,并同意: OWASP CSRF预防CHGat表 , 有关CSRF的问题 。)

据我所知,围绕CSRF的漏洞是通过假设(从web服务器的angular度来看)传入的HTTP请求中的有效会话cookie反映了经过身份validation的用户的愿望而引入的。 但是,所有来源域的cookie都被魔术般的附加到了浏览器的请求中,所以真正的所有服务器都可以通过在请求中存在有效的会话cookie来推断出请求来自具有经过validation的会话的浏览器; 它不能进一步假设在浏览器中运行的代码 ,或者它是否真的反映了用户的意愿。 防止这种情况的方法是在请求中包含额外的身份validation信息(“CSRF令牌”),通过除浏览器自动cookie处理以外的其他方式进行。 松散地说,会话cookieauthentication用户/浏览器,并且CSRF令牌authentication在浏览器中运行的代码。

因此,简而言之,如果您使用会话Cookie对Web应用程序的用户进行身份validation,则还应该为每个响应添加一个CSRF令牌,并在每个(变异)请求中要求匹配的CSRF令牌。 然后,CSRF令牌从服务器到浏览器返回到服务器,向服务器certificate发出请求的页面已被该服务器(由该服务器生成)批准。

关于我的问题,这是关于在该往返中用于该CSRF令牌的具体传输方法。

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

(另一种方法是Express推荐的方法,其中由服务器生成的CSRF令牌通过服务器端模板扩展包含在响应主体中,直接附加到将提供给服务器的代码/标记,例如作为一个隐藏的表单input,这个例子是一个更为web 1.0的处事方式,但是会把它推广到一个更重的JS客户端。

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

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

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

在典型的应用程序中,考虑您描述的每种方法的一些利弊的情况和过程。 这些基于同步器令牌模式 。

请求身体方法

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

优点

  • 简单实施。
  • 适用于AJAX。
  • 与表单一起工作。
  • Cookie实际上只能是HTTP 。

缺点

  • 所有表单都必须以HTML格式输出隐藏字段。
  • 任何AJAX POSTs也必须包含该值。
  • 该页面必须事先知道它需要CSRF令牌,因此它可以将其包含在页面内容中,因此所有页面都必须在某个地方包含令牌值,这可能会耗费大量的时间来执行大型站点。

自定义HTTP头(下游)

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

优点

  • 适用于AJAX。
  • Cookie只能是HTTP 。

缺点

  • 没有AJAX请求就无法获取标题值。
  • 所有表单必须具有dynamic添加到其HTML的值。
  • 任何AJAX POSTs也必须包含该值。
  • 该页面必须首先发出一个AJAX请求才能获得CSRF令牌,这意味着每次都会有一次额外的往返行程。
  • 也可以将令牌简单地输出到保存额外请求的页面。

自定义HTTP头(上游)

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

优点

  • 适用于AJAX。
  • Cookie只能是HTTP 。

缺点

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

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

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

优点

  • 适用于AJAX。
  • Cookie只能是HTTP 。

缺点

  • 不适用于表单。
  • 所有的AJAX POSTs也必须包含这个值。
  • 该页面必须先创build一个AJAX请求才能获取CRSF标记,因此每次都需要进行额外的往返。

设置Cookie

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

优点

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

缺点

  • 所有表单必须具有dynamic添加到其HTML的值。
  • 任何AJAX POSTs也必须包含该值。
  • 这个cookie将会被提交给每一个请求(比如所有的GET,图片,CSS,JS等等,都不参与CSRF过程)。
  • Cookie不能只是HTTP 。

因此,cookie方法是相当dynamic的,提供了一种简单的方法来检索cookie值(任何HTTP请求)并使用它(JS可以自动将值添加到任何表单,并且可以在AJAX请求中作为标题或作为forms值)。 一旦接收到会话的CSRF令牌,就不需要重新生成它,因为采用CSRF攻击的攻击者没有检索该令牌的方法。 如果恶意用户试图通过上述任何方法读取用户的CSRF令牌,则“ 同源策略”将阻止这种情况。 如果恶意用户试图检索CSRF令牌服务器端(例如通过curl ),那么这个令牌将不会被关联到相同的用户帐户,因为请求中将缺less受害者的auth会话cookie(这将是攻击者的 – 因此它将不会与服务器端与受害者的会话相关联)。

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

使用cookie来保存CSRF令牌不会成功进行攻击,因为攻击者无法读取cookie的值,因此无法放入服务器端CSRFvalidation所要求的位置。

攻击者可以通过请求头中的身份validation令牌cookie和CSRF cookie向服务器发起请求。 但是,服务器并没有在请求头中寻找CSRF令牌作为cookie,而是在请求的有效载荷中寻找。 即使攻击者知道将CSRF令牌放在有效载荷的什么位置,也必须读取它的值才能将其放在那里。 但是浏览器的跨域策略会阻止从目标网站读取任何cookie值。

同样的逻辑不适用于auth令牌cookie,因为服务器期望它在请求头中,攻击者不需要做任何特殊的事情就可以把它放在那里。

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

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

我认为第一个请求主体(虽然在问题中关联了Express教程 ),但是对于各种各样的情况来说,它并不是很方便; 不是每个人都dynamic地生成每个HTTP响应; 你最终需要将令牌放在生成的响应中可能差别很大(以隐藏的formsinput;在JS代码片段或其他JS代码可访问的variables中;甚至可能在URL中,尽pipe这似乎通常是一个糟糕的地方放置CSRF令牌)。 因此,虽然可以在一些定制化的情况下工作,但#1是一个难以适应的方法。

第二个,自定义标题,是有吸引力的,但实际上并没有工作,因为虽然JS可以得到它调用的XHR标题,它不能得到它加载页面的标题 。

这留下了第三个,一个由Set-Cookie头部携带的cookie,作为一种在所有情况下都很容易使用的方法(任何人的服务器都可以设置每个请求的cookie头文件,而不pipe是什么样的数据在请求正文中)。 所以尽pipe它的缺点,它是框架实施得最广泛的最简单的方法。