【问题标题】:What is a CSRF token? What is its importance and how does it work?什么是 CSRF 代币?它的重要性是什么,它是如何工作的?
【发布时间】:2011-07-09 14:15:24
【问题描述】:

我正在编写一个应用程序(Django,它就是这样),我只是想知道“CSRF 令牌”到底是什么以及它如何保护数据。

如果不使用 CSRF 令牌,post 数据是否不安全?

【问题讨论】:

  • 它是所有表单提交和副作用 URL 中的一个秘密的、用户特定的令牌,以防止跨站点请求伪造。更多信息:en.wikipedia.org/wiki/Cross-site_request_forgery
  • 似乎在保护一个问题和禁止它太宽泛之间有一条细线:D
  • 来自OWASP Cross-Site Request Forgery (CSRF) Prevention Cheat Sheet:“跨站点脚本不是 CSRF 工作所必需的。但是,任何跨站点脚本漏洞都可以用来破坏所有 CSRF 缓解技术 [... ]。这是因为 XSS 有效负载可以使用 XMLHttpRequest [...] 简单地读取站点上的任何页面。必须不存在 XSS 漏洞以确保无法绕过 CSRF 防御。"
  • 这是一个非常好的视频示例:youtube.com/watch?v=hW2ONyxAySY tl;dw:CSRF 令牌使请求输入先验不可预测。因此,攻击者无法轻易复制它。

标签: csrf


【解决方案1】:

网站在制作表单页面时会生成一个唯一令牌。需要此令牌才能将数据发布/获取回服务器。

由于令牌是由您的站点生成的,并且仅在生成带有表单的页面时提供,因此其他一些站点无法模仿您的表单——它们没有令牌,因此无法发布到您的站点.

【讨论】:

  • 用户能否在源中获取令牌输出,获取发送给他们的 cookie,然后从第三方站点提交?
  • @JackMarchetti 是的。但这会很昂贵,因为每次您想从第三方网站提交表单时,您都必须加载页面并解析出令牌。如果您担心这种攻击向量,CSRF 令牌应该与其他形式的安全性完美结合
  • 我和@JackMarchetti 有同样的问题,不清楚的是 - 如果每次登录时 CSRF 令牌都发生变化。如果它保持不变,什么会阻止攻击者首先登录,获取请求令牌,然后将该令牌插入攻击中?
  • @PaulPreibisch 它应该在每次页面加载时更改 - 而不是在每次登录时更改。这样,攻击者每次想要提交表单时都必须请求该页面。让它变得更加困难。
  • @tkone,这并没有真正让它变得更加困难。如果只是双倍的努力和时间。它不添加任何类型的禁止处理。诀窍是将 CSRF 令牌与特定于域的 cookie 相关联,并将此 cookie 与表单一起发送。 cookie 和表单发布数据都必须通过 POST 请求发送到服务器。这种方式需要 Cookie 劫持攻击才能模拟合法请求。
【解决方案2】:

这一切的根源是确保请求来自网站的实际用户。为表单生成一个 csrf 令牌,并且必须与用户的会话相关联。它用于向服务器发送请求,令牌在服务器中验证它们。这是防止 csrf 的一种方法,另一种方法是检查引荐来源标头。

【讨论】:

  • 不要依赖referer header,很容易被伪造。
  • 这是正确答案!令牌必须绑定到服务器上的会话。比较 Cookie + Form 数据,比如投票率最高的答案表明是完全错误的。这些组件都构成了客户端构建的请求的一部分。
  • 其实,没有。令牌必须绑定到服务器的每个请求。如果您只将其绑定到会话,那么您将面临有人窃取会话令牌并使用该令牌提交请求的风险。因此,为了最大程度的安全,令牌必须与每个 http 请求相关联。
【解决方案3】:

是的,发布的数据是安全的。但该数据的来源不是。这样,在浏览攻击者的网页时,有人可以用 JS 欺骗用户登录您的网站。

为了防止这种情况,django 会在 cookie 和表单数据中发送一个随机密钥。 然后,当用户 POST 时,它会检查两个密钥是否相同。万一用户被骗,第三方网站无法获取您网站的cookies,从而导致验证错误。

【讨论】:

  • @DmitryShevchenko 嗨,想了解这种 cookie+form-input 方法与仅在服务器端验证引用者有何不同?我发现的所有示例都与黑客诱使用户从他的网站发帖到实际网站有关。
  • 好的,我找到了为什么不使用引荐来源网址。它在许多情况下被阻止,因为它有时被认为包含敏感信息。公司及其代理人通常会这样做。但是,如果使用 HTTPS,则更有可能不会被阻止。
  • 更换referrer很容易,我不会说这是一个可靠的信息。但是,CSRF 令牌是使用服务器密钥生成的,通常与用户绑定
  • 我真的不明白为什么这是一个安全威胁。用户将登录到另一个站点...但原始站点将无法检索该信息。对吗?
  • 好吧,假设我在 Facebook.com 中注入了“bank.com/transfer?from=x&to=y”的恶意 iframe。如果您是 bank.com 的客户并且您访问 Facebook,该 iframe 将使用您的 cookie 加载银行页面(因为浏览器会将它们发送到已知域)并进行汇款。在你什么都不知道的情况下。
【解决方案4】:

简单的跨站请求伪造(CSRF)

  • 假设您当前登录您的网上银行www.mybank.com
  • 假设来自mybank.com 的汇款将导致(概念上)http://www.mybank.com/transfer?to=<SomeAccountnumber>;amount=<SomeAmount> 形式的请求。 (不需要您的帐号,因为您的登录信息暗示了它。)
  • 您访问www.cute-cat-pictures.org,却不知道这是一个恶意网站。
  • 如果该站点的所有者知道上述请求的形式(简单!)并正确猜测您已登录到mybank.com(需要一些运气!),他们可以在他们的页面上包含类似http://www.mybank.com/transfer?to=123456;amount=10000 的请求(其中123456 是他们的开曼群岛账户号码,10000 是您之前认为自己很高兴拥有的金额。
  • 已检索到该www.cute-cat-pictures.org 页面,因此您的浏览器将发出该请求。
  • 您的银行无法识别请求的来源:您的网络浏览器将连同您的www.mybank.com cookie 一起发送请求,并且看起来完全合法。你的钱到手了!

这是一个没有 CSRF 令牌的世界。

现在有了更好的 CSRF 令牌

  • 传输请求扩展了第三个参数:http://www.mybank.com/transfer?to=123456;amount=10000;token=31415926535897932384626433832795028841971
  • 该令牌是一个巨大的、无法猜测的随机数,mybank.com 在向您提供它时会将其包含在他们自己的网页中。每次他们向任何人提供任何页面时,它都不同
  • 攻击者无法猜出令牌,无法说服您的网络浏览器交出它(如果浏览器工作正常...),因此攻击者将无法创建一个有效的请求,因为带有错误令牌(或没有令牌)的请求将被www.mybank.com 拒绝。

结果:您保留了 10000 货币单位。我建议你把其中的一部分捐给维基百科。

(您的里程可能会有所不同。)

comment worth readingSOFe 编辑:

值得注意的是,由于 HTTP 访问控制,来自www.cute-cat-pictures.org 的脚本通常无法从www.mybank.com 访问您的反 CSRF 令牌。对于一些不合理地为每个网站响应发送标头 Access-Control-Allow-Origin: * 而不知道其用途的人来说,此说明非常重要,因为他们无法使用其他网站的 API。

【讨论】:

  • 显然这个令牌最好命名为 anti-CSRF 令牌,但这个名字可能已经够复杂了。
  • @LutzPrechelt 谢谢。为什么javascript无法从浏览器获取任何真实性令牌?
  • 值得注意的是,由于 HTTP 访问控制,来自www.cute-cat-pictures.org 的脚本通常无法从www.mybank.com 访问您的反 CSRF 令牌。对于某些不合理地为每个网站响应发送标头 Access-Control-Allow-Origin: * 而不知道其用途的人来说,此说明非常重要,因为他们无法使用其他网站的 API。
  • @AugustinRiedinger 如果攻击者在他的计算机上打开网页 - 因为他们没有登录用户的 cookie - 他们将不会收到相应的 csrf 令牌(每个 csrf 令牌应该只对特定的用户会话)。如果攻击者试图在用户的计算机上加载包含令牌的网页,并在cute-cat-pictures网站中放置一个脚本,浏览器将阻止他读取www.mybank.com(和令牌),因为同源政策。
  • @LutzPrechelt 我认为令牌总是不同是不够的,它必须与会话配对,并且服务器必须检查它收到的令牌是否是为服务器生成的会话由接收到的 cookie 标识。否则,黑客可以自己访问 mybank 并获得一些有效的令牌。因此,如果您对每个表单都使用新令牌,则必须将其与服务器上的 sessionid 配对保存。每个会话使用相同的令牌可能更容易。
【解决方案5】:

The Cloud Under blog has a good explanation of CSRF tokens.(已存档)

想象一下,您有一个类似简化版 Twitter 的网站,托管在 a.com 上。 登录的用户可以将一些文本(一条推文)输入到正在生成的表单中 作为 POST 请求发送到服务器,并在它们命中时发布 提交按钮。在服务器上,用户由 cookie 标识 包含他们唯一的会话 ID,因此您的服务器知道谁发布了 推文。

表格可以这么简单:

 <form action="http://a.com/tweet" method="POST">
   <input type="text" name="tweet">
   <input type="submit">
 </form> 

现在想象一下,一个坏人复制并粘贴这个表格到他的恶意 网站,比方说 b.com。该表格仍然可以使用。只要 当用户登录到您的 Twitter 时(即他们有一个有效的 a.com 的会话 cookie),POST 请求将被发送到 http://a.com/tweet 并在用户单击时照常处理 提交按钮。

到目前为止,只要让用户意识到这不是一个大问题 表单到底做了什么,但是如果我们的坏人调整了表单怎么办 像这样:

 <form action="https://example.com/tweet" method="POST">
   <input type="hidden" name="tweet" value="Buy great products at http://b.com/#iambad">
   <input type="submit" value="Click to win!">
 </form> 

现在,如果您的某个用户最终访问了坏人的网站并点击了 “点击赢!”按钮,表单提交到 您的网站,用户可以通过会话 ID 正确识别 cookie 和隐藏的推文被发布。

如果我们的坏人更坏,他会让无辜的用户提交 当他们使用 JavaScript 打开他的网页时,这个表单,甚至可能 完全隐藏在一个不可见的 iframe 中。这基本上是 跨站请求伪造。

一个表单可以很容易地从任何地方提交到任何地方。 通常这是一个共同的特征,但还有更多的情况 只允许从域提交表单很重要 它属于哪里。

如果您的 Web 应用程序无法区分,情况会更糟 在 POST 和 GET 请求之间(例如,在 PHP 中使用 $_REQUEST 代替 $_POST)。不要那样做!可以提交数据更改请求 就像&lt;img src="http://a.com/tweet?tweet=This+is+really+bad"&gt; 一样简单, 嵌入到恶意网站甚至电子邮件中。

如何确保只能从我自己的网站提交表单? 这就是 CSRF 令牌的用武之地。CSRF 令牌是随机的, 难以猜测的字符串。在包含您要保护的表单的页面上, 服务器会生成一个随机字符串,即 CSRF 令牌,将其添加到 形成一个隐藏字段并以某种方式记住它,或者通过存储 它在会话中或通过设置包含该值的 cookie。现在 表单看起来像这样:

    <form action="https://example.com/tweet" method="POST">
      <input type="hidden" name="csrf-token" value="nc98P987bcpncYhoadjoiydc9ajDlcn">
      <input type="text" name="tweet">
      <input type="submit">
    </form> 

当用户提交表单时,服务器只需比较 已发布字段 csrf-token 的值(名称不 问题)与服务器记住的 CSRF 令牌。如果两个字符串 相等,服务器可以继续处理表单。否则 服务器应立即停止处理表单并以 错误。

为什么会这样?我们的坏人有几个原因 上例无法获取 CSRF 令牌:

将静态源代码从我们的页面复制到其他网站 将无用,因为隐藏字段的值随 每个用户。没有坏人的网站知道当前用户的 您的服务器将始终拒绝 POST 请求的 CSRF 令牌。

因为坏人的恶意页面是由您用户的浏览器加载的 来自不同的域(b.com 而不是 a.com),坏人没有 有机会编写一个 JavaScript 来加载内容,因此我们的 用户当前来自您网站的 CSRF 令牌。那是因为网络 浏览器默认不允许跨域 AJAX 请求。

坏人也无法访问你服务器设置的cookie, 因为域不匹配。

我应该什么时候防止跨站点请求伪造?如果你可以的话 确保不要将 GET、POST 和其他请求方法混为 如上所述,一个好的开始是通过以下方式保护所有 POST 请求 默认。

您不必保护 PUT 和 DELETE 请求,因为 如上所述,浏览器无法提交标准 HTML 表单 使用这些方法。

另一方面,JavaScript 确实可以发出其他类型的请求, 例如使用 jQuery 的 $.ajax() 函数,但请记住,对于 AJAX 请求 工作域必须匹配(只要你没有明确 否则配置您的网络服务器)。

这意味着,通常您甚至不必将 CSRF 令牌添加到 AJAX 请求,即使它们是 POST 请求,但您必须进行 确保仅在以下情况下绕过 Web 应用程序中的 CSRF 检查 POST 请求实际上是一个 AJAX 请求。你可以这样做 寻找像 X-Requested-With 这样的标头的存在,它是 AJAX 请求通常包括。您还可以设置另一个自定义标题和 检查它在服务器端的存在。这是安全的,因为 浏览器不会将自定义标题添加到常规 HTML 表单提交中 (见上文),所以坏人先生没有机会模拟这种行为 用表格。

如果您对 AJAX 请求有疑问,因为出于某种原因,您 无法检查像 X-Requested-With 这样的标头,只需传递 生成 CSRF 令牌到您的 JavaScript 并将令牌添加到 AJAX 要求。有几种方法可以做到这一点;要么将其添加到 有效载荷就像一个常规的 HTML 表单一样,或者添加一个自定义标题到 AJAX 请求。只要您的服务器知道在哪里寻找它 一个传入的请求,并且能够将它与它的原始值进行比较 从会话或 cookie 中记住,您已排序。

【讨论】:

  • 感谢您提供详细信息。在 post 请求期间,站点必须将 csrf 令牌发送到服务器,那么客户端什么时候将这个 csrf 令牌发送到服务器?是在发出预检选项请求时吗?请详细说明这部分..
  • @Dan b.com 怎么可以访问另一个站点 a.com 的 cookie?
  • 很好的解释!
猜你喜欢
  • 2010-11-08
  • 2013-02-13
  • 2018-06-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-10-15
  • 2020-08-06
相关资源
最近更新 更多