我正在编写一个应用程序(Django,它发生了),我只是想知道什么是“CSRF令牌”以及它如何保护数据。
如果不使用CSRF令牌,发布数据是否不安全?
该站点在制作表单页面时会生成一个唯一令牌。将数据发布/获取回服务器需要此令牌。
由于令牌由您的站点生成,并且仅在生成具有表单的页面时提供,因此其他一些站点无法模仿您的表单-它们不会拥有令牌,因此无法发布到您的站点。
这一切的根源是确保请求来自网站的实际用户。为表单生成一个csrf令牌,并且必须与用户的会话相关联。它用于向服务器发送请求,令牌在其中验证它们。这是防止csrf的一种方法,另一种方法是检查引用者标头。
是的,帖子数据是安全的。但该数据的来源不是。这样,有人就可以欺骗使用JS的用户登录您的网站,同时浏览攻击者的网页。
为了防止这种情况,django会在cookie和表单数据中发送一个随机密钥。然后,当用户POST时,它会检查两个密钥是否相同。如果用户被欺骗,第三方网站无法获取您网站的cookie,从而导致身份验证错误。
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
这是世界没有CSRF令牌。
现在为更好的使用CSRF令牌:
http://www.mybank.com/transfer?to=123456;amount=10000;token=31415926535897932384626433832795028841971
结果:你保留你的10000货币单位。我建议你捐一些给维基百科。
(您的里程可能会有所不同。
编辑自值得阅读的评论bySOFe
值得注意的是,由于HTTP权限改造,来自www.cute-cat-pictures.org的脚本通常无法访问来自www.mybank.com的反CSRF令牌。对于一些人来说,这一点很重要,他们不合理地为每个网站响应发送标头Access-Control-Allow-Origin: *,而不知道它的用途,只是因为他们不能使用来自另一个网站的API。
Access-Control-Allow-Origin: *
Cloud Under博客对CSRF令牌有很好的解释。(存档)
想象一下,你有一个网站,就像一个简化的Twitter,托管在a.com.登录用户可以在正在使用的表单中输入一些文本(推文)作为POST请求发送到服务器,并在它们击中提交按钮。在服务器上,用户由cookie识别包含他们唯一的会话ID,因此您的服务器知道谁发布了推文。形式可以这么简单: <form action="http://a.com/tweet" method="POST"><input type="text" name="tweet"><input type="submit"></form>
想象一下,你有一个网站,就像一个简化的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时(即他们有一个有效的会话cookiea.com),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>
现在想象一下,一个坏人复制并粘贴这个表单给他的恶意网站,比方说b.com.表格仍然有效。只要当用户登录到您的Twitter时(即他们有一个有效的会话cookiea.com),POST请求将被发送到http://a.com/tweet并在用户单击提交按钮。
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和隐藏的Tweet会被发布。如果我们的坏人更坏,他会让无辜的用户提交这种形式,只要他们使用JavaScript打开他的网页,甚至可能完全隐藏在一个看不见的iFrame中。这基本上是跨站请求伪造。一个表单可以很容易地从任何地方提交到任何地方。一般来说,这是一个共同的特征,但有更多的情况下只允许从域提交表单是很重要的它属于哪里?如果您的Web应用程序没有区分,情况会更糟在POST和GET请求之间(例如在PHP中使用$_REQUEST_POST美元)。不要这样做!可以提交数据更改请求就像<img src="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>
现在,如果你的一个用户最终访问了坏人的网站并点击了“点击获胜!”按钮,表单提交给您的网站,用户通过会话ID正确识别cookie和隐藏的Tweet会被发布。
如果我们的坏人更坏,他会让无辜的用户提交这种形式,只要他们使用JavaScript打开他的网页,甚至可能完全隐藏在一个看不见的iFrame中。这基本上是跨站请求伪造。
一个表单可以很容易地从任何地方提交到任何地方。一般来说,这是一个共同的特征,但有更多的情况下只允许从域提交表单是很重要的它属于哪里?
如果您的Web应用程序没有区分,情况会更糟在POST和GET请求之间(例如在PHP中使用$_REQUEST_POST美元)。不要这样做!可以提交数据更改请求就像<img src="http://a.com/tweet?tweet=This+is+really+bad">一样简单。嵌入到恶意网站甚至电子邮件中。
<img src="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令牌的值(名称不问题)与服务器记住的CSRF令牌。如果两个字符串相等时,服务器可以继续处理表单。否则服务器应立即停止处理表单并响应错误。为什么会这样?有几个原因为什么我们的坏人上面的示例无法获取CSRF令牌:将静态源代码从我们的页面复制到不同的网站将是无用的,因为隐藏字段的值会随着每个用户。如果坏人的网站不知道当前用户的CSRF令牌您的服务器将始终拒绝POST请求。因为坏人的恶意页面是由你的用户浏览器加载的从不同的领域(b.com而不是a.com),坏人没有有机会编写JavaScript,加载内容,因此我们的您网站上用户当前的CSRF令牌。这是因为网络浏览器默认不允许跨域AJAX请求。坏人也无法访问您的服务器设置的cookie,因为域名不匹配。什么时候应该防止跨站点请求伪造?如果可以的话确保您不会将GET、POST和其他请求方法混淆为如上所述,一个好的开始是通过以下方式保护所有POST请求默认。您不必保护PUT和DELETE请求,因为如上所述,标准超文本标记语言表单不能由浏览器提交使用这些方法。另一方面,JavaScript确实可以发出其他类型的请求,例如,使用jQuery的$. ajax()函数,但请记住,对于AJAX请求要工作,域必须匹配(只要您没有明确否则配置您的Web服务器)。这意味着,通常您甚至不必向AJAX添加CSRF令牌请求,即使它们是POST请求,但您必须确保仅在以下情况下绕过Web应用程序中的CSRF检查POST请求实际上是一个AJAX请求。您可以通过寻找像X-Request est-With这样的标头的存在,AJAX请求通常包括。您还可以设置另一个自定义标头并检查它是否存在于服务器端。这是安全的,因为浏览器不会将自定义标题添加到常规超文本标记语言表单提交中(见上文),所以坏家伙先生没有机会模拟这种行为#36825;的形式如果您对AJAX请求有疑问,因为出于某种原因您无法检查像X-Request est-With这样的标头,只需传递生成的CSRF令牌到您的JavaScript并将令牌添加到AJAX有几种方法可以做到这一点;要么将其添加到有效载荷就像常规的超文本标记语言表单一样,或者添加一个自定义标头AJAX请求。只要您的服务器知道在哪里查找它一个传入的请求,并能够将其与它的原始值进行比较从会话或cookie中记住,您已排序。
当用户提交表单时,服务器只需比较张贴的字段csrf令牌的值(名称不问题)与服务器记住的CSRF令牌。如果两个字符串相等时,服务器可以继续处理表单。否则服务器应立即停止处理表单并响应错误。
为什么会这样?有几个原因为什么我们的坏人上面的示例无法获取CSRF令牌:
将静态源代码从我们的页面复制到不同的网站将是无用的,因为隐藏字段的值会随着每个用户。如果坏人的网站不知道当前用户的CSRF令牌您的服务器将始终拒绝POST请求。
因为坏人的恶意页面是由你的用户浏览器加载的从不同的领域(b.com而不是a.com),坏人没有有机会编写JavaScript,加载内容,因此我们的您网站上用户当前的CSRF令牌。这是因为网络浏览器默认不允许跨域AJAX请求。
坏人也无法访问您的服务器设置的cookie,因为域名不匹配。
什么时候应该防止跨站点请求伪造?如果可以的话确保您不会将GET、POST和其他请求方法混淆为如上所述,一个好的开始是通过以下方式保护所有POST请求默认。
您不必保护PUT和DELETE请求,因为如上所述,标准超文本标记语言表单不能由浏览器提交使用这些方法。
另一方面,JavaScript确实可以发出其他类型的请求,例如,使用jQuery的$. ajax()函数,但请记住,对于AJAX请求要工作,域必须匹配(只要您没有明确否则配置您的Web服务器)。
这意味着,通常您甚至不必向AJAX添加CSRF令牌请求,即使它们是POST请求,但您必须确保仅在以下情况下绕过Web应用程序中的CSRF检查POST请求实际上是一个AJAX请求。您可以通过寻找像X-Request est-With这样的标头的存在,AJAX请求通常包括。您还可以设置另一个自定义标头并检查它是否存在于服务器端。这是安全的,因为浏览器不会将自定义标题添加到常规超文本标记语言表单提交中(见上文),所以坏家伙先生没有机会模拟这种行为#36825;的形式
如果您对AJAX请求有疑问,因为出于某种原因您无法检查像X-Request est-With这样的标头,只需传递生成的CSRF令牌到您的JavaScript并将令牌添加到AJAX有几种方法可以做到这一点;要么将其添加到有效载荷就像常规的超文本标记语言表单一样,或者添加一个自定义标头AJAX请求。只要您的服务器知道在哪里查找它一个传入的请求,并能够将其与它的原始值进行比较从会话或cookie中记住,您已排序。