首页 文章

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

提问于
浏览
190

我'm trying to understand the whole issue with CSRF and appropriate ways to prevent it. (Resources I'已阅读,理解并同意:OWASP CSRF Prevention CHeat SheetQuestions about CSRF . )

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

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

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

看起来很常见(例如在AngularJSDjangoRails中)将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令牌对我来说似乎非常不理想 .

3 回答

  • 7

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

    在典型应用程序中考虑以下场景和过程,以了解您描述的每种方法的优缺点 . 这些基于Synchronizer Token Pattern .

    请求身体方法

    • 用户成功登录 .

    • 服务器发出auth cookie .

    • 用户点击以导航到表单 .

    • 如果尚未为此会话生成,则服务器会生成CSRF令牌,将其存储在用户会话中并将其输出到隐藏字段 .

    • 用户提交表单 .

    • 服务器检查隐藏字段匹配会话存储令牌 .

    Advantages:

    • 易于实施 .

    • 适用于AJAX .

    • 使用表单 .

    • Cookie实际上可以是HTTP Only .

    Disadvantages:

    • 所有表单必须以HTML格式输出隐藏字段 .

    • 任何AJAX POST都必须包含该值 .

    • 页面必须事先知道它需要CSRF令牌,因此它可以将其包含在页面内容中,因此所有页面都必须包含某处的令牌值,这可能会使实现大型站点的时间过长 .

    自定义HTTP标头(下游)

    • 用户成功登录 .

    • 服务器发出auth cookie .

    • 用户点击以导航到表单 .

    • 页面在浏览器中加载,然后发出AJAX请求以检索CSRF令牌 .

    • Server生成CSRF令牌(如果尚未为会话生成),将其存储在用户会话中并将其输出到标头 .

    • 用户提交表单(令牌通过隐藏字段发送) .

    • 服务器检查隐藏字段匹配会话存储令牌 .

    Advantages:

    Disadvantages:

    • 没有AJAX请求获取标头值不起作用 .

    • 所有表单必须动态地将值添加到其HTML中 .

    • 任何AJAX POST也必须包含该值 .

    • 该页面必须首先发出一个AJAX请求以获取CSRF令牌,因此这意味着每次额外的往返 .

    • 可能只是将令牌输出到页面,这将节省额外的请求 .

    自定义HTTP标头(上游)

    • 用户成功登录 .

    • 服务器发出auth cookie .

    • 用户点击以导航到表单 .

    • 如果尚未为此会话生成,则服务器会生成CSRF令牌,将其存储在用户会话中,并将其输出到页面内容中的某个位置 .

    • 用户通过AJAX提交表单(令牌通过标头发送) .

    • 服务器检查自定义标头匹配会话存储标记 .

    Advantages:

    Disadvantages:

    • 不适用于表单 .

    • 所有AJAX POST必须包含 Headers .

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

    • 用户成功登录 .

    • 服务器发出auth cookie .

    • 用户点击以导航到表单 .

    • 页面在浏览器中加载,然后发出AJAX请求以检索CSRF令牌 .

    • Server生成CSRF令牌(如果尚未为会话生成),将其存储在用户会话中并将其输出到标头 .

    • 用户通过AJAX提交表单(令牌通过标头发送) .

    • 服务器检查自定义标头匹配会话存储标记 .

    Advantages:

    Disadvantages:

    • 不适用于表单 .

    • 所有AJAX POST也必须包含该值 .

    • 该页面必须首先发出一个AJAX请求以获取CRSF令牌,因此这意味着每次额外的往返 .

    Set-Cookie

    • 用户成功登录 .

    • 服务器发出auth cookie .

    • 用户点击以导航到表单 .

    • Server生成CSRF令牌,将其存储在用户会话中并将其输出到cookie .

    • 用户通过AJAX或HTML表单提交表单 .

    • 服务器检查自定义标头(或隐藏的表单字段)与会话存储的标记匹配 .

    • Cookie在浏览器中可用于其他AJAX和表单请求,而无需向服务器请求检索CSRF令牌 .

    Advantages:

    • 易于实施 .

    • 适用于AJAX .

    • 使用表单 .

    • 不一定需要AJAX请求才能获取cookie值 . 任何HTTP请求都可以检索它,并且可以通过JavaScript将其附加到所有表单/ AJAX请求 .

    • 一旦检索到CSRF令牌,因为它存储在cookie中,可以重复使用该值而无需其他请求 .

    Disadvantages:

    • 所有表单必须动态地将值添加到其HTML中 .

    • 任何AJAX POST也必须包含该值 .

    • 将为每个请求(即所有未参与CSRF过程的图像,CSS,JS等的GET)提交cookie,从而增加请求大小 .

    • Cookie不能是HTTP Only .

    所以cookie方法是相当动态的,提供了一种简单的方法来检索cookie值(任何HTTP请求)并使用它(JS可以自动将值添加到任何表单中,并且它可以作为 Headers 或作为 Headers 用于AJAX请求中 . 形式 Value ) . 一旦收到会话的CSRF令牌,就不需要重新生成它,因为使用CSRF漏洞的攻击者没有检索此令牌的方法 . 如果恶意用户尝试在上述任何方法中读取用户的CSRF令牌,则Same Origin Policy将阻止此操作 . 如果恶意用户尝试检索CSRF令牌服务器端(例如,通过 curl ),则此令牌将不会与受害者's auth session cookie will be missing from the request (it would be the attacker'相关联的同一用户帐户 - 因此它赢得了't be associated server side with the victim'的会话 .

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

  • 29

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

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

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

  • 189

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

    • 在请求正文中(不是HTTP标头) .

    • 在自定义HTTP标头中,而不是Set-Cookie .

    • 作为Cookie,在Set-Cookie标头中 .

    我认为第一个,请求体(虽然由the Express tutorial I linked in the question展示),并不像各种各样的情况那样便携;不是每个人都动态地生成每个HTTP响应;你最终需要在生成的响应中放置令牌的地方可能有很大差异(在隐藏的表单输入中;在JS代码的片段中或其他JS代码可访问的变量;甚至在URL中虽然看起来通常是一个不好的地方)放置CSRF令牌) . 因此,虽然可以通过一些定制工作,但#1是一个很难做到一刀切的方法 .

    第二个,自定义 Headers ,很有吸引力,但实际上并不起作用,因为while JS can get the headers for an XHR it invoked, it can't get the headers for the page it loaded from .

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

相关问题