什么是CSRF令牌?它的重要性是什么?它是如何工作的?

时间:2011-03-05 22:17:08

标签: csrf

我正在编写一个应用程序(Django,它发生了),我只是想知道实际上是什么" CSRF令牌"它是如何保护数据的。如果不使用CSRF令牌,帖子数据是否不安全?

5 个答案:

答案 0 :(得分:1281)

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

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

这是世界没有CSRF令牌

现在更好的 CSRF令牌

  • 使用第三个参数www.mybank.com扩展转移请求。
  • 该令牌是一个巨大的,不可能猜到的随机数,http://www.mybank.com/transfer?to=123456;amount=10000;token=31415926535897932384626433832795028841971在他们为您提供服务时会在其自己的网页上包含该数字。每次向任何人提供任何页面时,不同
  • 攻击者无法猜出令牌,无法说服您的网络浏览器放弃它(如果浏览器正常工作......),那么攻击者将能够创建有效请求,因为mybank.com会拒绝带有错误令牌(或没有令牌)的请求。

结果:您保留www.mybank.com货币单位。我建议你把一些捐赠给维基百科。

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

来自评论的编辑值得一读:

值得注意的是,由于HTTP访问控制,来自10000的脚本通常无法从www.cute-cat-pictures.org访问您的反CSRF令牌。对于那些不合理地为每个网站响应发送标题www.mybank.com而不知道它是什么的人来说,这个注释很重要,只是因为他们无法使用其他网站的API。

答案 1 :(得分:211)

是的,帖子数据是安全的。但该数据的起源并非如此。这样,有人可以欺骗用户使用JS登录到您的站点,同时浏览攻击者的网页。

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

答案 2 :(得分:65)

网站在制作表单页面时会生成唯一标记。将令牌发送/返回服务器需要此令牌。

由于令牌是由您的网站生成的,并且仅在生成带有表单的页面时提供,因此其他一些网站无法模仿您的表单 - 他们将无法使用令牌,因此无法发布到您的网站

答案 3 :(得分:16)

让我举个例子......

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

表格可以简单:

 <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>

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

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

可以轻松地从任何地方向任何地方提交表单。通常这是一个常见的功能,但是在更多情况下,只允许从其所属的域提交表单非常重要。

如果您的Web应用程序不区分POST和GET请求(例如在PHP中使用$ _REQUEST而不是$ _POST),事情会更糟。不要那样做!数据更改请求可以像http://a.com/tweet?tweet=This+is+really+bad一样简单地提交,嵌入恶意网站甚至电子邮件中。

如何确保表单只能从我自己的网站提交? 这是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令牌有几个原因:

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

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

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

我应该何时防止跨网站请求伪造? 如果您可以确保不混淆GET,POST和其他请求方法,如上所述,一个良好的开端是默认保护所有POST请求。

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

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

这意味着,即使它们是POST请求,您甚至不必向AJAX请求添加CSRF令牌,但是如果POST请求,您必须确保只绕过Web应用程序中的CSRF检查实际上是一个AJAX请求。你可以通过寻找像X-Requested-With这样的头文件来实现这一点,这些头文件通常包括AJAX请求。您还可以设置另一个自定义标头并检查它在服务器端的存在。这是安全的,因为浏览器不会将自定义标头添加到常规HTML表单提交中(见上文),因此Bad Guy先生没有机会用表单模拟此行为。

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

答案 4 :(得分:5)

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