协慌网

登录 贡献 社区

理解 Rails 真实性令牌

我正在遇到有关 Rails 中的 Authenticity Token 的一些问题,因为我现在已经多次了。

但我真的不想只是解决这个问题而继续下去。我真的很想了解真实性令牌。那么,我的问题是,您是否有关于此主题的完整信息来源,或者您是否会花时间在此详细解释?

答案

怎么了

当用户查看表单以创建,更新或销毁资源时,Rails 应用程序会创建一个随机的authenticity_token ,将此令牌存储在会话中,并将其放在表单中的隐藏字段中。当用户提交表单时,Rails 会查找authenticity_token ,将其与存储在会话中的那个进行比较,如果匹配,则允许继续请求。

为什么会这样

由于真实性令牌存储在会话中,因此客户端无法知道其值。这可以防止人们在不查看该应用程序本身内的表单的情况下向 Rails 应用程序提交表单。想象一下,您正在使用服务 A,您登录该服务,一切正常。现在想象一下你去使用服务 B,你看到了你喜欢的图片,并按下图片来查看更大尺寸的图片。现在,如果服务 B 上有一些恶意代码,它可能会向服务 A(您已登录)发送请求,并要求删除您的帐户,方法是向http://serviceA.com/close_account发送请求。这就是所谓的CSRF(Cross Site Request Forgery)

如果服务 A 使用真实性令牌,则此攻击向量不再适用,因为来自服务 B 的请求将不包含正确的真实性令牌,并且将不允许继续。

API 文档描述了有关元标记的详细信息:

使用protect_from_forgery方法打开 CSRF 保护,该方法检查令牌并在会话与预期不匹配时重置会话。默认情况下,会为新的 Rails 应用程序生成对此方法的调用。 token 参数默认名为authenticity_token 。必须通过在 HTML 头中包含csrf_meta_tags ,将此标记的名称和值添加到呈现表单的每个布局中。

笔记

请记住,Rails 只验证非幂等方法(POST,PUT / PATCH 和 DELETE)。不检查 GET 请求的真实性令牌。为什么?因为 GET 请求的 HTTP 规范状态是幂等,并在服务器应该创建,改变或破坏资源,应该将请求幂等(如果您运行相同的命令多次,你应该每次都得到相同的结果)。

此外,真正的实现在开始时定义更复杂,确保更好的安全性。 Rails 不会为每个表单发出相同的存储令牌。它也不是每次都生成和存储不同的令牌。它在会话中生成并存储加密哈希,并发布新的加密令牌,每次呈现页面时,可以与存储的加密令牌进行匹配。请参阅request_forgery_protection.rb

教训

使用authenticity_token来保护您的非幂等方法(POST,PUT / PATCH 和 DELETE)。还要确保不允许任何可能修改服务器上资源的 GET 请求。


编辑:检查@erturne关于 GET 请求是幂等的评论 。他以比我在这里做的更好的方式解释它。

真品令牌旨在让您知道您的表单是从您的网站提交的。它是从运行它的机器生成的,具有唯一的标识符,只有您的机器可以知道,从而有助于防止跨站点请求伪造攻击。

如果您只是在使用 rails 拒绝访问 AJAX 脚本时遇到困难,可以使用

<%= form_authenticity_token %>

在创建表单时生成正确的标记。

您可以在文档中阅读更多相关信息。

什么是 CSRF?

真实性令牌是跨站请求伪造(CSRF)的对策。你问什么是 CSRF?

这是攻击者可能在不知道会话令牌的情况下劫持会话的方式。

场景

  • 访问您银行的网站,登录。
  • 然后访问攻击者的网站(例如,来自不受信任的组织的赞助广告)。
  • 攻击者的页面包含与银行 “转移资金” 表单相同字段的表单。
  • 攻击者知道您的帐户信息,并预先填写了表单字段,以便将资金从您的帐户转移到攻击者的帐户。
  • 攻击者的页面包含向您的银行提交表单的 Javascript。
  • 表单提交后,浏览器会包含您的银行网站的 Cookie,包括会话令牌。
  • 银行将钱转移到攻击者的帐户。
  • 表单可以在不可见的 iframe 中,因此您永远不会知道发生了攻击。
  • 这称为跨站请求伪造(CSRF)。

CSRF 解决方案

  • 服务器可以标记来自服务器本身的表单
  • 每个表单必须包含一个额外的身份验证令牌作为隐藏字段。
  • 令牌必须是不可预测的(攻击者无法猜测)。
  • 服务器在其页面中的表单中提供有效令牌。
  • 服务器在发布表单时检查令牌,拒绝没有适当令牌的表单。
  • 示例令牌:使用服务器密钥加密的会话标识符。
  • Rails 自动生成这样的标记:请参阅每个表单中的 authenticity_token 输入字段。