如何在RESTful应用程序中防止CSRF?

跨站点请求伪造(CSRF)通常通过以下方法之一进行阻止:

  • 检查引用 – RESTful但不可靠
  • 将令牌插入表单并将令牌存储在服务器会话中 – 不是真正的RESTful
  • 神秘的一次性URIs – 与令牌相同的原因不是RESTful
  • 手动为这个请求发送密码(不是与HTTPauthentication一起使用的caching密码) – RESTful但不方便

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

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

这种方法有什么问题,尽pipe它没有JavaScript不起作用。

附录:

  • 无状态的CSRF保护

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

你不应该做的事情:

  1. 如果你需要从JavaScript中读取会话标记,那么你正在做一些可怕的错误。 您的会话标识符cookie应始终设置HTTPOnly,使其不可用于脚本。

    这种保护使得XSS的影响大大降低,因为攻击者将不再能够获得login的用户会话令牌,而对于所有的意图和目的,这都与应用程序中的凭证等效。 你不希望有一个错误把钥匙给王国。

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

你应该做的事情:

  1. 遵循OWASP的指导 :

  2. 具体而言,如果这是一个REST应用程序,则可以要求双重提交CSRF令牌 :

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

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

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

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

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

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

我是否正确:

  • 您希望通过cookielogin的用户免受CSRF攻击。
  • 同时,您需要基于应用程序的基本,OAuth和摘要authentication请求的RESTful界面。

那么,为什么不检查用户是否通过cookielogin, 然后才应用CSRF呢?

我不确定,但另一个网站可以伪造像基本身份validation或标题的东西吗?

据我所知,CSRF是关于cookie的 ? REST风格的身份validation不会发生在Cookie中。

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

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

在我实施我自己的authentication系统之前,我会仔细阅读“强大的跨站点伪造防御” ,因为我想抵制CSRF。 实际上,我会重新考虑实施我自己的authentication系统。

CSRF预防备忘单中有几个方法可以用于安静的服务。 最RESTful无状态CSRF缓解是使用Origin或HTTP referer来确保请求来自您信任的域。

这种方法有什么问题,尽pipe它没有JavaScript不起作用。

如果您发送给客户端,您的用户密码不是秘密。 我们通常使用这样的秘密来生成哈希表并将它们发送出去,然后等待它们进行比较。

如果你想成为RESTful,请求必须包含如何处理它的每一个信息。 你可以这样做的方式:

  • 向您的REST客户端添加一个csrf标记cookie,并将隐藏input中的相同标记与您的表单一起发送。 如果服务和客户端位于不同的域中,则必须共享凭据。 在服务你必须比较2个标记,如果他们是相同的,请求是有效的…

  • 您可以将csrf标记cookie添加到您的REST服务中,并将相同的标记与您资源的表示(隐藏input等)一起发送。 其他的一切和以前解决scheme的结尾是一样的。 这个解决scheme处于RESTfulness的边缘。 (可以直到客户端没有调用服务来修改cookie,如果cookie只有http,客户端不应该知道,如果不是,那么客户端应该设置它。)你可以做更多复杂的解决scheme,如果您添加不同的令牌到每个表单,并添加过期时间到cookie。 您也可以将表格的过期时间发送回来,这样您可以知道令牌validation失败的原因。

  • 您可以在服务上拥有资源状态的用户密码(每个用户都不相同)。 通过构build表示,您可以为每个表单生成一个令牌(和到期时间)。 您可以从实际的令牌(以及过期时间,方法,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是一个非常糟糕的解决scheme。 但是,如果你想阻碍CSRF,这是非常基本的技能。