我正在编写一个应用程序(Django,它确实发生了),我只想了解“ CSRF令牌”实际上是什么以及它如何保护数据。如果不使用CSRF令牌,发布数据是否不安全?

评论

它是所有表单提交和副作用URL中针对特定用户的秘密令牌,可防止跨站点请求伪造。此处提供更多信息:en.wikipedia.org/wiki/Cross-site_request_forgery

似乎在保护问题和因过于笼统而禁止它之间存在一条细线:D

OWASP跨站点请求伪造(CSRF)预防备忘单:“跨站点脚本不是CSRF起作用所必需的。但是,任何跨站点脚本漏洞都可以用来击败所有CSRF缓解技术。这是因为XSS有效负载可以使用XMLHttpRequest [...]来简单地读取站点上的任何页面。必须确保不存在XSS漏洞以确保CSRF防御不会被绕开。“

#1 楼

简单地说,跨站请求伪造(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,它将看起来非常合法。有钱了!

这是没有CSRF令牌的世界。

现在有了CSRF令牌的更好的人:


传输请求扩展了第三个参数:http://www.mybank.com/transfer?to=123456;amount=10000;token=31415926535897932384626433832795028841971
该令牌是一个巨大的,无法猜测的随机数,当mybank.com提供给您时,它将包含在他们自己的网页中。每次向任何人提供任何页面时,它都是不同的。
攻击者无法猜测令牌,无法说服您的Web浏览器将其交出(如果浏览器正常工作...),等等攻击者将无法创建有效的请求,因为www.mybank.com将拒绝具有错误令牌(或没有令牌)的请求。

结果:您保留了10000货币单位。我建议您将其中的一些捐赠给Wikipedia。

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

编辑后的评论值得阅读:

值得注意的是,由于HTTP访问控制,www.cute-cat-pictures.org中的脚本通常无法访问www.mybank.com中的反CSRF令牌。对于有些人,他们不了解每个网站的响应而仅仅由于无法使用其他网站的API而对每个网站响应不合理地发送标头Access-Control-Allow-Origin: *,这对某些人来说很重要。

评论


显然,该令牌在理想情况下将被命名为anti-CSRF令牌,但是名称可能确实很复杂。

– Lutz Prechelt
16年5月27日在19:15

@LutzPrechelt谢谢。为什么javascript无法从浏览器获取任何真实性令牌?

– BKSpurgeon
16年8月10日在6:27

值得注意的是,由于HTTP访问控制,www.cute-cat-pictures.org中的脚本通常无法从www.mybank.com中访问您的反CSRF令牌。对于一些不合理地发送标头Access-Control-Allow-Origin的人来说,此说明很重要:*对于每个网站响应,却不知道它的用途,只是因为他们不能使用其他网站的API。

– SOFe
16年11月5日在14:45

@AugustinRiedinger如果攻击者在其计算机上打开网页-由于他们没有已登录用户的cookie,则他们将不会收到相应的csrf令牌(每个csrf令牌仅对特定的用户会话有效)。如果攻击者尝试在用户计算机上加载包含令牌的网页,并在可爱猫咪图片网站上放置脚本,则浏览器将阻止他读取www.mybank.com(和令牌),因为相同的原产地政策。

–马塞尔
18-2-8在6:15



@LutzPrechelt我认为令牌总是不同是不够的,它必须与会话配对,并且服务器必须检查它收到的令牌是为服务器通过接收到的cookie标识的会话生成的。否则,黑客可以自己访问mybank并获得一些有效令牌。因此,如果在每种形式中都使用新令牌,则必须将其与会话ID配对保存在服务器上。每个会话使用相同的令牌可能更容易。

–马塞尔
18-2-8在6:30



#2 楼

是的,帖子数据是安全的。但是该数据的来源不是。这样,当浏览攻击者的网页时,有人可以诱骗JS用户登录到您的站点。

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

评论


@DmitryShevchenko您好,尝试了解cookie + form-input的这种方法与仅在服务器端验证引荐来源网址有何不同?我发现的所有示例都与一个黑客欺骗用户从其站点发布到实际站点有关。

– Ethan
2013年1月10日14:36

好的,我发现了为什么不使用引荐来源网址的原因。在很多情况下,它被阻止,因为有时它会保存敏感信息。企业及其代理通常会这样做。但是,如果使用HTTPS,则很有可能不会被阻止。

– Ethan
13年10月10日在21:26

更改引荐来源网址很容易,我不会说这是可靠的信息。但是,CSRF令牌是使用服务器密钥生成的,通常与用户绑定

–德米特里·舍甫琴科
13年1月11日,下午2:05

我真的不明白为什么这是安全威胁。用户将登录到另一个站点...,但是原始站点将无法获取该信息。对?

– Aakil Fernandes
14年8月13日在22:31

好吧,假设我在一个Facebook.com中注入了“ bank.com/transfer?from=x&to=y”的恶意iframe。如果您是bank.com的客户并且访问了Facebook,则该iframe将加载带有Cookie的银行页面(因为浏览器会将其发送到已知域)并进行汇款。你什么都不知道

–德米特里·舍甫琴科
14年8月14日在6:34

#3 楼

该网站在制作表单页面时会生成一个唯一的令牌。此令牌是将数据发布/获取回服务器所必需的。

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

评论


用户能否获取源中的令牌输出,获取发送给他们的cookie,然后从第三方站点提交?

–杰克·马尔凯蒂(Jack Marchetti)
14年4月23日在0:51

@JackMarchetti是的。但这样做的成本很高,因为每次您想从第三方网站提交表单时,都必须加载页面并解析出令牌。如果您担心这种攻击方式,则CSRF令牌应理想地与其他形式的安全性结合使用

– tkone
2014年4月23日在11:36

我和@JackMarchetti有相同的问题,不清楚的是-如果CSRF令牌在每次登录时更改。如果保持不变,那么什么将阻止攻击者首先登录,获取请求令牌,然后将该令牌插入攻击中?

– Paul Preibisch
2014年5月5日在7:28

@PaulPreibisch,它应在每次页面加载时更改-而不是每次登录时更改。这样,攻击者每次想要提交表单时都必须请求页面。使其变得更加困难。

– tkone
2014年5月7日23:31

@tkone,这真的并没有使它变得更加困难。如果只是将工作量和时间加倍。它不添加任何种类的禁止处理。技巧还在于将CSRF令牌与特定于域的cookie关联,并将此cookie与表单一起发送。 Cookie和表单发布数据都必须根据POST请求发送到服务器。这种方式将需要Cookie劫持攻击才能模拟合法请求。

– Pedro Cordeiro
15年10月27日在16:18

#4 楼

Cloud Under博客很好地解释了CSRF令牌。


假设您在a.com上托管了一个类似简化Twitter的网站。
登录的用户可以输入一些内容。将文本(推文)转换为表单,然后以POST请求的形式发送到服务器,并在点击
提交按钮时发布。在服务器上,用户通过包含唯一会话ID的cookie
进行标识,因此您的服务器知道谁发布了Tweet。

表单可以很简单:

 <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正确识别,并且隐藏的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> 


用户提交表单时,服务器只需比较发布字段csrf-的值令牌(名称无关紧要)与服务器记住的CSRF令牌。如果两个字符串
相等,则服务器可以继续处理该表单。否则,
服务器应立即停止处理表单并出现错误


为什么这样做?上面的
示例中的坏人无法获得CSRF令牌的原因有很多:

将静态源代码从我们的页面复制到其他网站会是以下原因:没有用,因为每个用户的隐藏字段的值都会更改。如果坏人的网站不知道当前用户的CSRF令牌,则您的服务器将始终拒绝POST请求。

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

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

我应该什么时候防止跨站点请求伪造?如果您可以
确保不将GET,POST和其他请求方法混为一谈,
,那么一个好的开始就是默认保护所有POST请求。
>
您不必保护PUT和DELETE请求,因为如上所述,
不能使用这些方法由浏览器提交标准HTML表单。

另一方面,JavaScript确实可以发出其他类型的请求,例如
使用jQuery的$ .ajax()函数,但是请记住,要使AJAX请求正常工作,域必须匹配(只要您没有明确地
配置Web服务器,否则)。

这意味着,即使它们是POST请求,即使您甚至不必向AJAX请求中添加CSRF令牌,但您必须确保
仅绕过CSRF检查在Web应用程序中,如果
POST请求实际上是AJAX请求。您可以通过
查找是否存在标头(如X-Requested-With)来实现,该标头通常包含AJAX
请求。您还可以设置另一个自定义标头,然后
检查它在服务器端是否存在。这是安全的,因为
浏览器不会将自定义标头添加到常规HTML表单提交中
(请参见上文),因此Bad Guy先生没有机会使用表单来模拟这种行为。
br />
如果您对AJAX请求有疑问,由于某种原因您
不能检查X-Requested-With之类的标头,只需将
生成的CSRF令牌传递给您JavaScript,并将令牌添加到AJAX
请求中。有几种方法可以做到这一点。可以将其添加到
有效负载中,就像常规HTML表单一样,或者将自定义标头添加到
AJAX请求。只要您的服务器知道要在传入请求中查找的位置,并且能够将其与会话或cookie记住的原始值进行比较,就可以对您进行排序。


评论


感谢您提供详细信息。在发布请求期间,站点必须将csrf令牌发送到服务器,因此客户端何时将此csrf令牌发送到服务器?是在发出预检选项请求时吗?请详细说明这一点。

–Sm Srikanth
18年11月30日在10:25

@Dan b.com如何访问另一个站点a.com的cookie?

– zakir
19年2月9日在21:13

#5 楼

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

评论


不要依赖引用标头,它很容易被伪造。

– kag
15年5月29日在18:06

这是正确的答案!令牌必须绑定到服务器上的会话。比较Cookie和Form数据(如投票最多的答案)完全是错误的。这些组件都构成客户端构造的请求的一部分。

–李·戴维斯(Lee Davis)
2015年9月8日在16:04

其实没有令牌必须绑定到服务器的每个请求。如果仅将其绑定到会话,则冒着有人窃取会话令牌并使用该令牌提交请求的风险。因此,为了最大程度的安全,令牌必须绑定到每个http请求。

–chrisl08
16-4-7在16:10