2010-03-06 139 views
66

跨站请求伪造(CSRF)通常用以下方法之一预防:如何在RESTful应用程序中防止CSRF?

  • 检查引荐 - RESTful的,但不可靠
  • 插入令牌到形式存储在服务器会话令牌 - 没有真正的RESTful
  • 隐蔽一次的URI - 不是的RESTful出于同样的原因如令牌
  • 手动发送密码此请求(不与HTTP认证中使用的缓存的密码) - 的RESTful但不方便

我的想法是使用用户秘密,一个神秘但静态的表单id和JavaScript来生成令牌。

<form method="POST" action="/someresource" id="7099879082361234103"> 
    <input type="hidden" name="token" value="generateToken(...)"> 
    ... 
</form> 
  1. GET /usersecret/john_doe由JavaScript取出从已认证用户。
  2. 回复:OK 89070135420357234586534346这个秘密在概念上是静态的,但可以每天/每小时更改一次...以提高安全性。这是唯一保密的事情。
  3. 阅读所有用户的神秘(但对所有用户都是静态的!)form id,与用户秘密一起处理它:generateToken(7099879082361234103, 89070135420357234586534346)
  4. 将表单连同生成的令牌一起发送到服务器。
  5. 由于服务器知道用户密码和表单ID,因此可以在发送和比较两个结果之前运行与客户端所做的相同的generateToken函数。只有当两个值相等时,动作才会被授权。

这种方法有什么问题,尽管它没有JavaScript没有用吗?

附录:

+0

您的usersecret对用户来说并不是唯一的,攻击者只需要获取该数字并调整其脚本以使用新计算即可。如果你根本没有状态,你如何验证用户? – Mike 2010-03-06 10:19:42

+0

用户密码对于每个用户都是唯一的,只能在认证后进行检索(HTTP基本或摘要认证或证书认证) – deamon 2010-03-07 08:27:50

回答

21

这里有很多答案,其中有不少问题。

事情你不应该这样做:

  1. 如果你需要阅读从JavaScript会话令牌,你正在做一些可怕的错误。您的会话标识符cookie应始终对其进行HTTPOnly设置,因此它不适用于脚本。

    这一个保护措施使得XSS的影响大大降低,因为攻击者将不再能够获取登录用户会话令牌,对于所有意图和目的,这些会话令牌都等同于应用程序中的凭证。你不想要一个错误来给王国的钥匙。

  2. 不应将会话标识符写入页面的内容。这与您设置HTTPOnly的原因相同。这意味着您的会话令牌不能是您的会话ID。他们需要不同的价值观。

事情你应该做的:

  1. 按照OWASP's guidance

  2. 具体来说,如果这是一个REST的应用程序,你可以require double-submission of CSRF tokens

简单加密创造的东西随机存储在ASCII十六进制或Base64编码中,a nd在服务器返回页面时将其添加为cookie和表单。在服务器端确保cookie值与表单值匹配。 Voila,你已经杀死了CSRF,为你的用户避免了额外的提示,而不是让自己面对更多的漏洞。

+0

具有会话cookie的XSS与具有可从JavaScript读取的令牌的XSS一样脆弱。如果仍然可以制作将用户帐户中的资金转移到我的帐户的AJAX请求,并且服务器很乐意接受它。 – ghayes 2016-07-20 00:47:10

+2

@ghayes我不同意。您的会话令牌比您的CSRF令牌敏感得多。通过您的会话令牌,我可以像您一样从我的机器上完全访问应用程序。使用CSRF令牌,我可能会有一个预先编写脚本的敏感操作列表,这些操作会在您的浏览器中执行。第二种情况要困难得多,需要知道应用程序,需要较长的时间才能执行,并且这些操作仅限于事先规划的内容。第一种情况下,任何网站都需要一行代码,并且攻击者可以在他的机器上使用Cookie管理器应用程序。 – Doug 2016-10-12 21:09:38

+0

这里值得一提。通过在服务器上进行严格的“跨源HTTP请求”检查和来自API的http返回头,可以限制攻击者可能对登录用户造成的自动损害。 – LessQuesar 2017-06-23 13:44:36

6

静态形式ID提供任何保护;攻击者可以自己抓取它。请记住,攻击者不限制在客户端上使用JavaScript;他可以获取静态表单ID服务器端。

我不确定我完全理解提出的辩护; GET /usersecret/john_doe从哪里来?是页面JavaScript的那部分?这是文字提出的网址吗?如果是这样,我假设username不是秘密,这意味着如果浏览器或插件错误允许跨域GET请求,evil.ru可以恢复用户机密。为什么不在用户身份验证中将用户密码存储在cookie中,而不是让任何可以跨域GET的用户检索它?

在我实施我自己的认证系统之前,我会仔细阅读"Robust Defenses for Cross-Site Forgery",我想要抵制CSRF。事实上,我会重新考虑实施我自己的认证系统。

+1

表单ID类似于公钥。你是对的,'GET/usersecret/john_doe'是JavaScript的一部分。用户名本身并不是秘密,但是通过经过验证的(!)用户使用此请求获取ID。 谢谢你的链接。 – deamon 2010-03-07 09:02:50

9

您肯定需要服务器上的某些状态来进行身份验证/授权。它不一定是http会话,你可以将它存储在分布式缓存(如memcached)或数据库中。

如果您使用cookie进行身份验证,最简单的解决方案是双击提交cookie值。在提交表单之前,请先阅读cookie中的会话ID,将其存储在隐藏字段中,然后提交。在服务器端,请确认请求中的值与会话标识(您从Cookie获得的值)相同。来自其他域的邪恶脚本将无法从cookie中读取会话ID,从而阻止CSRF。

该方案在会话中使用单个标识符。

如果您想获得更多保护,请为每个表单生成一个唯一的ID。

另外,不要在JS中生成令牌。任何人都可以复制代码并从不同的域运行它来攻击你的站点。

+1

验证不需要会话,如HTTP验证所示。生成令牌的JavaScript代码不是秘密 - 只有用户秘密必须是秘密的。 – deamon 2010-03-07 08:31:31

+3

@Sri虽然我同意会话是处理安全性和性能的最佳方式。这不是RESTful,因为它要求服务器跟踪每个用户的状态,这可能会导致可伸缩性问题。 – rook 2012-04-06 00:15:30

+1

如果页面容易受到XSS攻击,说明双重cookie提交不起作用是否正确?因为那样你就可以直接从域本身提交表单,并且价值将通过cookie和表单发送。 – 2012-09-11 14:33:53

3

CSRF Prevention Cheat Sheet中有几种方法可用于宁静服务。最RESTful无状态CSRF缓解措施是使用Origin或HTTP referer来确保请求来自您信任的域。

+2

这是一个危险的建议,虽然很难欺骗HTTP引用程序,但这并非不可能,也不保证引用程序的头文件(并且不会发送引用程序头文件会破坏您的应用程序)。 – RelaXNow 2012-05-22 10:31:22

+3

@RelaXNow看看我写的这个CSRF漏洞利用框架:https://github.com/TheRook/CSRF-Request-Builder。它允许你指定任意的http头,以及正文。但是,它**不能更改http referer **,因为这被Flash禁止。 CSRF预防备忘单非常好,你应该阅读我的帖子中的链接。 – rook 2012-05-23 09:15:58

+0

公平点,在CSRF的情况下,攻击者将无法(据我所知)欺骗受害者的Referer头部,但头部仍然不能保证,并且只有在您可以保证它总是会被发送(就像公司的内部申请一样)。 – RelaXNow 2012-05-23 14:40:08

7

我是否获得这一权利:

  • 你想对CSRF保护用户通过cookie登录。
  • 同时,您还希望获得来自应用程序的Basic,OAuth和Digest验证请求的RESTful接口。

那么,为什么不检查用户是否通过cookie的登录并申请CSRF才

我不确定但有可能让另一个网站伪造诸如基本身份验证或标头之类的东西吗?

据我所知,CSRF是全部是关于cookies? RESTful认证不会发生在Cookie上。

+0

我也在想这个!根据这篇文章https://mathieu.fenniak。net/is-your-web-api-susceptible-to-a-csrf-exploit /如果有人通过cookie /会话进入,应该可以切换CSRF检查,并且如果请求正在通过某种无状态身份验证方案Basic ..等。 – CMCDragonkai 2013-09-26 01:24:03

+2

请小心使用基本身份验证 - 它实际上等同于通过cookie登录的用户,因为浏览器将在后续请求上发送提供的授权标头,以方便用户使用。 – 2015-08-04 01:17:02

0

这种方法有什么问题,尽管它 不工作没有JavaScript?

如果您发送给客户端,您的用户密码不是秘密。我们通常使用这些秘密来生成散列并将它们与表单一起发送,然后等待它们进行比较。

如果您想成为RESTful,请求必须包含有关如何处理它的每个信息。该方法可以做到这一点:

  • 添加CSRF标记饼干与您的REST客户端,并与您的形式发送同样的隐藏输入。如果服务和客户端位于不同的域中,则必须共享凭据。在服务上,您必须比较两个令牌,如果它们相同,则请求有效...

  • 您可以将csrf标记Cookie添加到您的REST服务中,并发送相同的标记以及你的资源(隐藏的输入等等)。其他一切与以前解决方案的结尾相同。该解决方案处于RESTfulness的边缘。(直到客户端不调用服务来修改cookie是可以的,如果cookie只有http,客户端不应该知道它,如果不是,那么客户端应该设置它。)你可以做更多的事情如果您为每个表单添加不同的标记并将过期时间添加到Cookie,则可能会出现复杂的解决方案。您也可以使用表单返回到期时间,以便您知道令牌验证失败的原因。

  • 您可以在服务上拥有资源状态的用户密码(每个用户不同)。通过构建表示,您可以为每个表单生成一个令牌(和到期时间)。您可以根据实际的令牌(以及过期时间,方法,url等)和用户密码生成哈希,并将该哈希与表单一起发送。你当然保密“用户秘密”,所以你永远不会用表单发送。之后,如果您的服务获得请求,则可以再次从请求参数和用户密钥生成哈希,并对它们进行比较。如果不匹配,该请求是无效的......

没有人会保护你,如果你的REST客户端的JavaScript是注射的,所以你必须要检查HTML实体的所有用户的内容,并删除所有这些,或者始终使用TextNode而不是innerHTML。你必须保护自己免受SQL注入和HTTP头注入。切勿使用简单的FTP来刷新您的网站。等等...有很多方法可以将恶意代码注入到您的网站中...

我差点忘了提及GET请求总是用于由服务和客户端读取。通过服务,这是显而易见的,通过客户端设置浏览器中的任何url必须导致资源或多个资源的表示,它绝不应该在资源上调用POST/PUT/DELETE方法。例如GET http://my.client.com/resource/delete -> DELETE http://my.api.com/resource是一个非常糟糕的解决方案。但是,如果你想阻碍CSRF,这是非常基本的技能。

相关问题