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

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


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

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


这一切的根本是确保请求来自网站的实际用户。将为表单生成csrf令牌,该令牌必须与用户的会话绑定。它用于向服务器发送请求,令牌在服务器中验证请求。这是防止csrf的一种方法,另一种方法是检查referrer标头。


是的,后期数据是安全的。但这些数据的来源并非如此。通过这种方式,有人可以用JS欺骗用户登录到您的网站,同时浏览攻击者的网页。

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


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

假设您当前登录到www.mybank.com的网上银行假设从mybank.com转账将导致(从概念上)表单请求http://www.mybank.com/transfer?to=<SomeAccountnumber>;amount=<SomeAmount>。(不需要您的帐号,因为它是由您的登录暗示的。)你访问www.cute-cat-pictures.org,并不知道它是一个恶意网站。如果该网站的所有者知道上述请求的形式(简单http://www.mybank.com/transfer?to=123456;amount=10000(其中123456是其开曼群岛账户的号码,10000是您之前认为您很高兴拥有的金额)。您检索到了www.cute-cat-pictures.org页面,因此您的浏览器将发出该请求。您的银行无法识别该请求的来源:您的web浏览器会将该请求与您的www.mybank.com cookie一起发送,并且看起来完全合法。你的钱来了!

这是一个没有CSRF代币的世界。

现在,使用CSRF代币获得更好的结果:

传输请求用第三个参数扩展:http://www.mybank.com/transfer?to=123456;金额=10000;令牌=31415926535897932384626433832795028841971。这个令牌是一个巨大的、无法猜测的随机数,mybank.com将在他们自己的网页上向您提供它。每次他们向任何人提供任何页面都是不同的。攻击者无法猜测令牌,无法说服您的web浏览器放弃令牌(如果浏览器工作正常…),因此攻击者将无法创建有效的请求,因为www.mybank.com将拒绝使用错误令牌(或没有令牌)的请求。

结果:你保留了10000个货币单位。

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

编辑来自SOFe值得阅读的评论:

值得注意的是,由于HTTP访问控制,www.cute-cat-pictures.org中的脚本通常无法访问www.mybank.com中的反CSRF令牌。对于某些人来说,这一点很重要,因为他们无法使用另一个网站的API,所以他们不知道每个网站的响应都会不合理地发送一个标题Access-Control-Allow-Origin:*。


Cloud Under博客对CSRF代币有很好的解释。(存档)

想象一下,你有一个网站,像一个简化的Twitter,托管在.com上。登录用户可以将一些文本(推文)输入到作为POST请求发送到服务器,并在它们命中提交按钮。在服务器上,用户由cookie标识包含其唯一会话ID,以便您的服务器知道谁发布了推特。表格可以这么简单:<form action=“http://a.com/tweet“method=”POST“><input-type=“text”name=“tweet”><input-type=“submit”></form>

现在想象一下,一个坏人复制并粘贴这个表单给他的恶意网站,比方说b.com。表单仍然有效。只要当用户登录到您的Twitter时(即用于.com的会话cookie),POST请求将发送到http://a.com/tweet并在用户单击提交按钮。到目前为止,这不是一个大问题,只要用户知道表格的确切功能,但如果我们的坏人修改了表格呢这样地:<form action=“https://example.com/tweet“method=”POST“><input-type=“hidden”name=“tweet”value=“在http://b.com/#iambad"><input type=“submit”value=“单击即可获胜!”></form>

现在,如果你的一个用户在坏人的网站上点击单击“点击获胜!”按钮,表单将提交给您的网站,用户通过会话ID正确识别cookie和隐藏的推特被发布。如果我们的坏人更坏,他会让无辜的用户屈服一旦他们使用JavaScript打开他的网页,甚至可能完全隐藏在一个看不见的iframe中。这基本上是跨站点请求伪造。表单可以很容易地从任何地方提交到任何地方。一般来说,这是一个常见的特征,但还有很多情况下只允许从域提交表单很重要它所属的位置。如果您的web应用程序无法区分在POST和GET请求之间(例如,在PHP中使用$_REQUEST$_POST的)。不要那样做!可以提交数据更改请求就像<img src=“http://a.com/tweet?tweet=This+是+真的+坏的“>,嵌入恶意网站甚至电子邮件中。我如何确保表格只能从我自己的网站提交?这就是CSRF令牌的来源。CSRF令牌是随机的,难以猜测的字符串。在包含要保护的表单的页面上服务器将生成一个随机字符串CSRF令牌,并将其添加到形式作为隐藏字段,并以某种方式记住它,或者通过存储或者通过设置包含该值的cookie。现在表单将如下所示:<form action=“https://example.com/tweet“method=”POST“><input type=“hidden”name=“csrf token”value=“nc98P987bcpncYhoadjustiydc9ajDlcn”><input-type=“text”name=“tweet”><input-type=“submit”></form>

当用户提交表单时,服务器只需比较posted字段csrf令牌的值(名称不matter)与服务器记住的CSRF令牌进行比较。如果两个字符串则服务器可以继续处理表单。否则服务器应立即停止处理表单,并用错误为什么这样做?有几个原因可以解释为什么我们上面的示例无法获得CSRF令牌:将静态源代码从我们的页面复制到其他网站将是无用的,因为隐藏字段的值随每个用户。如果坏人的网站不知道当前用户的CSRF令牌您的服务器将始终拒绝POST请求。因为坏人的恶意页面是由用户的浏览器加载的从另一个域(b.com而不是a.com),坏人没有有机会编写一个JavaScript,加载内容用户的当前CSRF令牌。这是因为网络默认情况下,浏览器不允许跨域AJAX请求。坏人也无法访问服务器设置的cookie,因为域不匹配。我什么时候应该防止跨站点请求伪造?如果可以的话确保您不会混淆GET、POST和其他请求方法如上所述,一个好的开始是通过以下方式保护所有POST请求违约您不必保护PUT和DELETE请求,因为如上所述,浏览器无法提交标准HTML表单使用这些方法。另一方面,JavaScript确实可以发出其他类型的请求,例如,使用jQuery的$.ajax()函数,但请记住,对于ajax请求要工作,域必须匹配(只要您不明确否则配置web服务器)。这意味着,通常您甚至不必向AJAX添加CSRF令牌请求,即使它们是POST请求,但您必须确保您仅在以下情况下绕过web应用程序中的CSRF检查POST请求实际上是AJAX请求。你可以这样做寻找一个像X-Requested-With这样的头,AJAX请求通常包括。您还可以设置另一个自定义标头和检查其在服务器端的存在。这是安全的,因为浏览器不会向常规HTML表单提交添加自定义标题(见上文),所以坏蛋先生没有机会模仿这种行为使用表单。如果您对AJAX请求有疑问,因为某些原因无法检查像X-Requested-With这样的标头,只需传递生成CSRF令牌到JavaScript,并将令牌添加到AJAX要求有几种方法可以做到这一点;将其添加到有效载荷,就像常规HTML表单一样,或者向AJAX请求。只要您的服务器知道在哪里查找它并能够将其与原始值进行比较从会话或cookie中记住,您已排序。