什么是CSRF令牌?

What is a CSRF token ? What is its importance and how does it work?

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


单词交叉站点请求铁匠

  • 假设你目前正在网上银行
  • 假定从mybank.com转账的金额会在一个(概念上)http://www.mybank.com/transfer?to=;amount=形式的请求中产生。(你的帐户号码不需要,因为它是在你的住所内。)
  • 你参观了www.cute-cat-pictures.org,不知道这是个恶意的地方。
  • 如果这个网站的主人知道上次请求的形式(简单!)并正确地看着你被安置在mybank.com(需要一些运气!)他们可以在他们的页面上包括一个类似http://www.mybank.com/transfer?to=123456;amount=10000(EDOCX1〕〔6〕)的请求,是他们开曼群岛帐户和10000的号码,是你预先认为自己很有可能拥有的一部分。
  • 您检索的页面,所以您的浏览器会满足您的要求。
  • 您的银行无法识别这一请求的起源:您的网页浏览器将随您的EDOCX1你的钱在那里!

这是没有CSRF托肯斯的世界。

Now for the better one with CSRF tokens:

  • 传输请求以第三个理由扩展:http://www.mybank.com/transfer?to=123456;amount=10000;token=31415926535897932384626433832795028841971
  • 这是一个不可能的随机数字,它将包括在他们自己的网页上,当他们为你服务。他们为任何人服务的每一页都是不同的。
  • 进攻者不能预见到突击,不能让你的网页浏览器说服它投降(如果浏览器作业正确……),因此进攻者不能提出有效的请求,因为与错误的托肯(或没有托肯)一起提出的要求将被EDOCX1[……]拒绝。

结果:你保持你的10000货币单位。我建议你捐赠一些维基百科。

(你的长途跋涉可能会变)

Edit from comment worth reading:

应该注意到,从www.cute-cat-pictures.org中的脚本通常不会从www.mybank.com中访问您的反CSRF。对于那些没有理由的人来说,这一点很重要,因为他们无法从另一个网站上使用API。


是的,邮件数据是安全的。但数据的来源不是。这是一种有人可以在浏览攻击者的网页时将JS编入你的网页的作弊方式。

为了防止Django会在饼干中随机输入密钥和数据。然后,当用户邮件时,如果两个键是相同的,它会检查。如果你的使用者是编织者,第三党网站不能得到你的网站饼干,这就造成了秋天的错误。


网站生成了一个独一无二的图案页。这个token需要邮寄/返回到服务器。

自从你的网站生成了token,并提供了只有当你的网页生成时,其他的网页不会模仿你的形式--他们不会有token,therefore can't post to your site.


让我举个例子…好的。

想象一下你有一个简单的twitter网站,托管在a.com上。登录的用户可以在表单中输入一些文本(tweet),这些文本将作为post请求发送到服务器,并在按下submit按钮时发布。在服务器上,用户由一个包含其唯一会话ID的cookie标识,因此您的服务器知道谁发布了tweet。好的。

形式可以这么简单:好的。

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

想象一下,一个坏人把这个表格复制粘贴到他的恶意网站上,比如说b.com。表单仍然有效。只要用户登录到你的twitter(即他们有一个有效的a.com会话cookie),发布请求就会发送到http://a.com/tweet,并在用户单击提交按钮时像往常一样进行处理。好的。

到目前为止,只要用户知道表单的具体功能,这不是一个大问题,但是如果我们的坏人像这样修改表单会怎么样:好的。

1
2
3
4
5
 <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>

现在,如果你的一个用户最终出现在坏人的网站上,点击"点击取胜!"按钮,表单被提交到您的网站,用户被cookie中的会话ID正确识别,隐藏的tweet被发布。好的。

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

表单可以很容易地从任何地方提交到任何地方。一般来说,这是一个常见的特性,但在许多情况下,只允许从表单所属的域提交表单是很重要的。好的。

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

如何确保只能从我自己的网站提交表单?这就是CSRF令牌的来源。CSRF令牌是一个随机的、难以猜测的字符串。在具有要保护的表单的页面上,服务器将生成一个随机字符串CSRF令牌,将其作为隐藏字段添加到表单中,并通过将其存储在会话中或通过设置包含该值的cookie以某种方式记住它。现在表单如下所示:好的。

1
2
3
4
5
6
    <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令牌(名称无关紧要)的值与服务器记住的csrf令牌进行比较。如果两个字符串都相等,服务器可以继续处理表单。否则,服务器应立即停止处理表单并以错误响应。好的。

为什么会这样?以上示例中的坏人无法获得CSRF令牌有几个原因:好的。

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

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

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

我应该在什么时候防止跨站点请求伪造?如果您可以确保不混淆上面描述的get、post和其他请求方法,那么一个好的开始就是默认地保护所有post请求。好的。

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

另一方面,javascript确实可以发出其他类型的请求,例如使用jquery的$.ajax()函数,但请记住,对于要工作的Ajax请求,域必须匹配(只要您不显式配置Web服务器)。好的。

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

如果您对Ajax请求有疑问,因为某些原因您无法检查像x-requested-with这样的头,只需将生成的CSRF令牌传递给您的javascript,并将该令牌添加到Ajax请求中。有几种方法可以做到这一点:要么像常规HTML表单一样将其添加到有效负载中,要么向Ajax请求添加自定义头。只要您的服务器知道在传入请求中从何处查找它,并且能够将其与它从会话或cookie中记住的原始值进行比较,您就可以进行排序。好的。好啊.


这一切的根源在于确保请求来自网站的实际用户。a CSRF token is generated for the forms and must be tied to the user's sessions.它被用来向服务器提出请求,在这一过程中,服务器的效力被削弱。这是对付CSRF的一种保护方式,另一种方式将是查验Referrer Header。