好吧,伙计们,我正在编写一个Django应用程序,我只是想知道实际上是什么csrf令牌以及它如何保护数据 . 如果不使用csrf令牌,帖子数据是不安全的?
我知道如何使用csrf_token但我只需要一些信息它是如何工作的 .
假设您目前已登录网上银行 www.mybank.com
www.mybank.com
假设从 mybank.com 汇款将导致(概念上) http://www.mybank.com/transfer?to=<SomeAccountnumber>;amount=<SomeAmount> 表格的请求 . (不需要您的帐号,因为您的登录信息暗示了这一点 . )
mybank.com
http://www.mybank.com/transfer?to=<SomeAccountnumber>;amount=<SomeAmount>
您访问 www.cute-cat-pictures.org ,不知道它是恶意网站 .
www.cute-cat-pictures.org
如果该网站的所有者知道上述请求的形式(简单!)并且正确猜测您已登录 mybank.com (需要一些运气!),他们可以在其页面上包含 http://www.mybank.com/transfer?to=123456;amount=10000 之类的请求(其中 123456 是他们的开曼群岛帐户和 10000 是您之前认为您很乐意拥有的金额) .
http://www.mybank.com/transfer?to=123456;amount=10000
123456
10000
您检索到了 www.cute-cat-pictures.org 页面,因此您的浏览器将发出该请求 .
您的银行无法识别此请求的来源:您的网络浏览器会将您的请求与您的 www.mybank.com Cookie一起发送,看起来非常合法 . 你有钱!
这是世界 without CSRF tokens .
现在为了更好的一个 with CSRF tokens :
使用第三个参数扩展传输请求: http://www.mybank.com/transfer?to=123456;amount=10000;token=31415926535897932384626433832795028841971 .
http://www.mybank.com/transfer?to=123456;amount=10000;token=31415926535897932384626433832795028841971
该令牌是一个巨大的,不可能猜到的随机数, mybank.com 将在他们为您提供服务时将其包含在他们自己的网页上 . 每次他们向任何人提供任何页面时都会有所不同 .
攻击者无法猜测令牌,无法说服您的网络浏览器放弃它(如果浏览器正常工作......),因此攻击者将无法创建有效请求,因为请求具有 www.mybank.com 将拒绝错误的令牌(或没有令牌) .
结果:您保留 10000 货币单位 . 我建议你把一些捐赠给维基百科 .
(你的旅费可能会改变 . )
EDIT from comment worth reading:
值得注意的是,由于HTTP访问控制,来自 www.cute-cat-pictures.org 的脚本通常无法从 www.mybank.com 访问您的反CSRF令牌 . 这个注释对于那些不合理地为每个网站响应发送 Headers Access-Control-Allow-Origin: * 而不知道它是什么的人来说非常重要,因为他们不能使用来自其他网站的API .
Access-Control-Allow-Origin: *
是的,帖子数据是安全的 . 但该数据的起源并非如此 . 这样,有人可以欺骗用户使用JS登录到您的站点,同时浏览攻击者的网页 .
为了防止这种情况,django将在cookie和表单数据中发送随机密钥 . 然后,当用户POST时,它将检查两个键是否相同 . 如果用户被欺骗,第三方网站无法获取您网站的cookie,从而导致身份验证错误 .
该网站在制作表单页面时会生成唯一标记 . 将令牌/数据发回/返回服务器需要此令牌 .
由于令牌是由您的网站生成的,并且仅在生成带有表单的页面时提供,因此某些其他网站无法模仿您的表单 - 他们将无法使用令牌,因此无法发布到您的网站 .
让我给你举个例子…
想象一下,你有一个像简化的Twitter这样的网站,托管在a.com上 . 签名用户可以将一些文本(推文)输入到作为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>
现在,如果你的一个用户最终进入坏人的网站并点击“点击赢取!”按钮,表单将提交到您的网站,用户通过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中记住的原始值进行比较,您就会进行排序 .
它的根本是确保请求来自站点的实际用户 . 为表单生成csrf令牌,并且必须绑定到用户的会话 . 它用于向服务器发送请求,令牌在其中验证它们 . 这是一种防止csrf的方法,另一种方法是检查引用者头 .
5 回答
跨站请求伪造(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一起发送,看起来非常合法 . 你有钱!这是世界 without CSRF tokens .
现在为了更好的一个 with CSRF tokens :
使用第三个参数扩展传输请求:
http://www.mybank.com/transfer?to=123456;amount=10000;token=31415926535897932384626433832795028841971
.该令牌是一个巨大的,不可能猜到的随机数,
mybank.com
将在他们为您提供服务时将其包含在他们自己的网页上 . 每次他们向任何人提供任何页面时都会有所不同 .攻击者无法猜测令牌,无法说服您的网络浏览器放弃它(如果浏览器正常工作......),因此攻击者将无法创建有效请求,因为请求具有
www.mybank.com
将拒绝错误的令牌(或没有令牌) .结果:您保留
10000
货币单位 . 我建议你把一些捐赠给维基百科 .(你的旅费可能会改变 . )
EDIT from comment worth reading:
值得注意的是,由于HTTP访问控制,来自
www.cute-cat-pictures.org
的脚本通常无法从www.mybank.com
访问您的反CSRF令牌 . 这个注释对于那些不合理地为每个网站响应发送 HeadersAccess-Control-Allow-Origin: *
而不知道它是什么的人来说非常重要,因为他们不能使用来自其他网站的API .是的,帖子数据是安全的 . 但该数据的起源并非如此 . 这样,有人可以欺骗用户使用JS登录到您的站点,同时浏览攻击者的网页 .
为了防止这种情况,django将在cookie和表单数据中发送随机密钥 . 然后,当用户POST时,它将检查两个键是否相同 . 如果用户被欺骗,第三方网站无法获取您网站的cookie,从而导致身份验证错误 .
该网站在制作表单页面时会生成唯一标记 . 将令牌/数据发回/返回服务器需要此令牌 .
由于令牌是由您的网站生成的,并且仅在生成带有表单的页面时提供,因此某些其他网站无法模仿您的表单 - 他们将无法使用令牌,因此无法发布到您的网站 .
让我给你举个例子…
想象一下,你有一个像简化的Twitter这样的网站,托管在a.com上 . 签名用户可以将一些文本(推文)输入到作为POST请求发送到服务器的表单中,并在他们点击提交按钮时发布 . 在服务器上,用户由包含其唯一会话ID的cookie标识,因此您的服务器知道谁发布了推文 .
表格可以简单:
现在想象一下,一个坏人将这个表格复制并粘贴到他的恶意网站上,比如说b.com . 表格仍然有效 . 只要用户登录到您的Twitter(即他们为a.com提供了有效的会话cookie),POST请求就会发送到http://a.com/tweet并在用户单击提交按钮时照常处理 .
到目前为止,这不是一个大问题,只要用户了解表单的确切功能,但如果我们的坏人调整表单如下:
现在,如果你的一个用户最终进入坏人的网站并点击“点击赢取!”按钮,表单将提交到您的网站,用户通过cookie中的会话ID正确识别,隐藏的推文获取出版 .
如果我们的坏人更糟糕,他会让无辜的用户在使用JavaScript打开他的网页时提交此表单,甚至可能完全隐藏在一个看不见的iframe中 . 这基本上是跨站点请求伪造 .
可以轻松地从任何地方向任何地方提交表单 . 通常这是一个常见的功能,但是在更多情况下,仅允许从其所属的域提交表单非常重要 .
如果您的Web应用程序无法区分,情况会更糟在POST和GET请求之间(例如在PHP中使用$ _REQUEST而不是$ _POST) . 不要那样做!数据更改请求可以像http://a.com/tweet?tweet=This+is+really+bad一样简单地提交,嵌入恶意网站甚至电子邮件中 .
如何确保表格只能从我自己的网站提交?这就是CSRF令牌的用武之地.CSRF令牌是一个随机的,难以猜测的字符串 . 在具有您要保护的表单的页面上,服务器将生成随机字符串,CSRF令牌,将其作为隐藏字段添加到表单中,并通过将其存储在会话中或通过设置cookie以某种方式记住它包含值 . 现在表单看起来像这样:
当用户提交表单时,服务器只需将发布的字段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中记住的原始值进行比较,您就会进行排序 .
它的根本是确保请求来自站点的实际用户 . 为表单生成csrf令牌,并且必须绑定到用户的会话 . 它用于向服务器发送请求,令牌在其中验证它们 . 这是一种防止csrf的方法,另一种方法是检查引用者头 .