我试图了解 CSRF 的整个问题以及防止它发生的适当方法。 (我已阅读、理解并同意的资源: OWASP CSRF Prevention CHeat Sheet 、 Questions about CSRF 。)
据我了解,CSRF 的漏洞是由以下假设引入的:(从网络服务器的角度来看)传入 HTTP 请求中的有效 session cookie 反射(reflect)了经过身份验证的用户的愿望。但是原始域的所有 cookie 都被浏览器神奇地附加到请求中,因此实际上所有服务器都可以从请求中存在有效 session cookie 中推断出该请求来自具有经过身份验证的 session 的浏览器;它无法进一步假设有关在该浏览器中运行的代码的任何信息,或者它是否真的反射(reflect)了用户的意愿。防止这种情况的方法是在请求中包含额外的身份验证信息(“CSRF token ”),通过浏览器的自动 cookie 处理以外的其他方式携带。粗略地说, session cookie 对用户/浏览器进行身份验证,CSRF token 对浏览器中运行的代码进行身份验证。
所以简而言之,如果您使用 session cookie 来验证 Web 应用程序的用户,您还应该向每个响应添加一个 CSRF token ,并在每个(变异)请求中要求一个匹配的 CSRF token 。然后,CSRF token 从服务器到浏览器返回服务器,向服务器证明发出请求的页面已被该服务器批准(甚至由该服务器生成)。
关于我的问题,这是关于该往返中用于该 CSRF token 的特定传输方法。
似乎很常见(例如在 AngularJS 、 Django 、 Rails 中)将 CSRF token 作为 cookie 从服务器发送到客户端(即在 Set-Cookie header 中),然后让客户端中的 Javascript 将其刮掉cookie 并将其附加为单独的 XSRF-TOKEN header 以发送回服务器。
(另一种方法是例如 Express 推荐的方法,其中服务器生成的 CSRF token 通过服务器端模板扩展包含在响应正文中,直接附加到将提供回服务器的代码/标记,例如作为一个隐藏的表单输入。这个例子是一种更像 web 1.0-ish 的做事方式,但可以很好地推广到更重 JS 的客户端。)
为什么使用 Set-Cookie 作为 CSRF token 的下游传输如此普遍/为什么这是个好主意?我想所有这些框架的作者都仔细考虑了他们的选择并且没有弄错。但乍一看,使用 cookie 来解决本质上对 cookie 的设计限制似乎很愚蠢。事实上,如果你使用 cookie 作为往返传输(Set-Cookie: header 下游的服务器告诉浏览器 CSRF token ,和 Cookie: header 上游的浏览器将其返回给服务器)你会重新引入你的漏洞正在尝试修复。
我意识到上面的框架没有在整个 CSRF token 的往返过程中使用 cookie;他们在下游使用 Set-Cookie,然后在上游使用其他东西(例如 X-CSRF-Token header ),这确实关闭了漏洞。但即使使用 Set-Cookie 作为下游传输也有潜在的误导性和危险性;浏览器现在会将 CSRF token 附加到每个请求,包括真正的恶意 XSRF 请求;充其量这会使请求比它需要的更大,最坏的情况是一些善意但被误导的服务器代码实际上可能会尝试使用它,这将非常糟糕。此外,由于 CSRF token 的实际预期接收者是客户端 Javascript,这意味着该 cookie 不能仅使用 http 来保护。因此,在 Set-Cookie header 中向下游发送 CSRF token 对我来说似乎不太理想。
最佳答案
一个很好的理由,你已经有点接触了,一旦收到 CSRF cookie,它就可以在客户端脚本中的整个应用程序中使用,用于常规表单和 AJAX POST。这在诸如 AngularJS 使用的 JavaScript 重应用程序中是有意义的(使用 AngularJS 并不要求应用程序将是单页面应用程序,因此当状态需要在 CSRF 值的不同页面请求之间流动时它会很有用通常无法在浏览器中持久化)。
考虑典型应用程序中的以下场景和流程,了解您描述的每种方法的一些优缺点。这些基于 Synchronizer Token Pattern .
请求正文方法
优点:
缺点:
自定义 HTTP header (下游)
标题。
优点:
缺点:
自定义 HTTP header (上游)
优点:
缺点:
自定义 HTTP header (上游和下游)
标题。
优点:
缺点:
设置 Cookie
优点:
缺点:
因此 cookie 方法是相当动态的,它提供了一种简单的方法来检索 cookie 值(任何 HTTP 请求)并使用它(JS 可以自动将值添加到任何表单,它可以在 AJAX 请求中用作 header 或作为形式值)。一旦收到 session 的 CSRF token ,就无需重新生成它,因为使用 CSRF 漏洞的攻击者无法检索此 token 。如果恶意用户试图通过上述任何一种方法读取用户的 CSRF token ,那么这将被 Same Origin Policy 阻止。 .如果恶意用户尝试检索 CSRF token 服务器端(例如,通过
curl
),则此 token 将不会与同一用户帐户相关联,因为请求中将缺少受害者的身份验证 session cookie(这将是攻击者的 -因此它不会将服务器端与受害者的 session 相关联)。以及 Synchronizer Token Pattern还有Double Submit Cookie CSRF 预防方法,当然是使用 cookie 来存储一种 CSRF token 。这更容易实现,因为它不需要 CSRF token 的任何服务器端状态。使用这种方法时,CSRF token 实际上可以是标准的身份验证 cookie,并且该值像往常一样通过 cookie 与请求一起提交,但该值也在隐藏字段或 header 中重复,攻击者无法将其复制为他们首先无法读取值。但是,建议选择另一个 cookie,而不是身份验证 cookie,以便可以通过标记为 HttpOnly 来保护身份验证 cookie。所以这是您使用基于 cookie 的方法发现 CSRF 预防的另一个常见原因。
关于security - 为什么将 CSRF 预防 token 放在 cookie 中很常见?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20504846/