了解Rails真实性令牌

Rails中的真实令牌是什么?

218729 次浏览

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

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

<%= form_authenticity_token %>

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

您可以在留档中阅读更多信息。

Authenticity Token防止跨站点请求伪造(CSRF或XSRF)攻击的rails方法。

简而言之,它确保对Web应用程序的PUT/POST/DELETE(可以修改内容的方法)请求是从客户端浏览器发出的,而不是来自有权访问在客户端创建的cookie的第三方(攻击者)。

会发生什么

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

为什么会发生

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

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

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

CSRF保护使用protect_from_forgery方法打开,它检查令牌并重置会话,如果它不匹配什么为新Rails生成对此方法的调用默认情况下的应用程序。默认情况下,令牌参数名为authenticity_token。名称必须将此令牌的值添加到呈现的每个布局中通过在超文本标记语言中包含csrf_meta_tags来形成。

备注

请记住,Rails只验证不是幂等的方法(POST、PUT/PATCH和DELETE)。GET请求不会检查真实性令牌。为什么?因为HTTP规范指出GET请求是幂等的,应该没有创建、更改或销毁服务器上的资源,并且请求应该是幂等的(如果你多次运行同一个命令,每次都应该得到相同的结果)。

此外,真正的实现比一开始定义的要复杂一点,以确保更好的安全性。Rails不会对每种表单发出相同的存储令牌。它也不会每次生成和存储不同的令牌。它在会话中生成和存储加密哈希,并在每次呈现页面时发出新的加密令牌,这些令牌可以与存储的令牌匹配。参见request_forgery_protection.rb

教训

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


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

请注意,如果您有来自同一客户端的多个并发请求,则真实令牌机制可能会导致竞争条件。在这种情况下,您的服务器可以生成多个真实性令牌,而此时应该只有一个,并且在表单中接收早期令牌的客户端将在下一个请求中失败,因为会话cookie令牌已被覆盖。这里有一篇关于这个问题的文章和一个不完全平凡的解决方案:http://www.paulbutcher.com/2007/05/race-conditions-in-rails-sessions-and-how-to-fix-them/

什么是CSRF?

身份令牌是针对跨站点请求伪造(CSRF)的一种对策。你问什么是CSRF?

这是一种攻击者甚至可以在不知道会话令牌的情况下劫持会话的方式。

场景

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

CSRF解决方案

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

因为Authenticity Token非常重要,在Rails 3.0+中,您可以使用

 <%= token_tag nil %>

来创建

<input name="authenticity_token" type="hidden" value="token_value">

任何地方

方法authenticity_token是必需的

authenticity_token是在幂等方法的情况下需要的,比如post、put和delete-in,因为幂等方法会影响数据。

为什么需要

需要防止恶意操作。authenticity_token存储在会话中,每当在网页上创建表单以创建或更新资源时,真实性令牌存储在隐藏字段中,并与服务器上的表单一起发送。在执行操作之前,用户发送authenticity_token与存储在会话中的authenticity_token进行交叉检查。如果authenticity_token相同,则继续进程,否则它不执行操作。

可以防止的最小攻击示例:CSRF

在我的网站上evil.example我说服你提交以下表格:

<form action="http://bank.com/transfer" method="post"><p><input type="hidden" name="to"      value="ciro"></p><p><input type="hidden" name="ammount" value="100"></p><p><button type="submit">CLICK TO GET PRIZE!!!</button></p></form>

如果您通过会话cookie登录您的银行,则会发送cookie并在您不知情的情况下进行转账。

这就是CSRF令牌发挥作用的原因:

  • 通过返回表单的GET响应,Rails发送了一个非常长的随机隐藏参数
  • 当浏览器发出POST请求时,它会发送参数,服务器只有在匹配时才会接受它

因此,真实浏览器上的表单看起来像:

<form action="http://bank.com/transfer" method="post"><p><input type="hidden" name="authenticity_token" value="j/DcoJ2VZvr7vdf8CHKsvjdlDbmiizaOb5B8DMALg6s=" ></p><p><input type="hidden" name="to"                 value="ciro"></p><p><input type="hidden" name="ammount"            value="100"></p><p><button type="submit">Send 100$ to Ciro.</button></p></form>

因此,我的攻击会失败,因为它没有发送authenticity_token参数,而且我不可能猜到它,因为它是一个巨大的随机数。

这种预防技术被称为同步器令牌模式

同源政策

但是,如果攻击者使用JavaScript发出两个请求,一个用于读取令牌,另一个用于进行传输呢?

仅靠同步器令牌模式不足以防止这种情况!

这就是同源政策拯救的地方,正如我在https://security.stackexchange.com/questions/8264/why-is-the-same-origin-policy-so-important/72569#72569中解释的那样

Rails如何发送令牌

覆盖:Rails:csrf_meta_tag如何工作?

基本上:

  • 如果表单不是GET表单,超文本标记语言助手(如form_tag)会为表单添加一个隐藏字段

  • AJAX由jquery-ujs自动处理,它从csrf_meta_tags添加到标头的meta元素(存在于默认模板中)中读取令牌,并将其添加到发出的任何请求中。

    uJS还尝试在过时的缓存片段中更新表单中的令牌。

其他预防办法

什么是authentication_token?

这是rails应用程序使用的随机字符串,以确保用户是从应用程序页面请求或执行操作,而不是从另一个应用程序或站点。

为什么authentication_token是必要的?

保护您的应用程序或网站免受跨站点请求伪造。

如何向表单添加authentication_token?

如果您使用form_for标签生成表单,则会自动添加authentication_token,否则您可以使用<%= csrf_meta_tag %>

真实性令牌用于防止跨站点请求伪造攻击(CSRF)。要了解真实性令牌,您必须首先了解CSRF攻击。

CSRF

假设您是bank.example的作者。您的网站上有一个表单,用于通过GET请求将资金转移到另一个帐户:

输入图片描述

黑客可以向服务器发送一个HTTP请求,上面写着GET /transfer?amount=$1000000&account-to=999999,对吧?

输入图片描述

错了。黑客攻击不起作用。服务器基本上会思考?

嗯?这家伙是谁,想发起转账。肯定不是账户的主人。

服务器是怎么知道的?因为没有session_id cookie来验证请求者。

当您使用您的用户名和密码登录时,服务器会在您的浏览器上设置一个session_id cookie。这样,您就不必使用您的用户名和密码对每个请求进行身份验证。当您的浏览器发送session_id cookie时,服务器知道:

哦,那是无名氏。他在2.5分钟前成功登录。他可以走了。

黑客可能会想:

嗯。正常的HTTP请求是行不通的,但如果我能拿到那个session_id cookie,我会很高兴的。

用户浏览器为bank.example域设置了一堆cookie。每次用户向bank.example域发出请求时,所有cookie都会一起发送。包括session_id cookie。

因此,如果黑客可以让发出GET请求,将资金转入他的账户,他就成功了。他怎么能欺骗你这样做呢?跨站请求伪造。

其实很简单。黑客可以让你访问他的网站。在他的网站上,他可能有以下图片标签:

<img src="http://bank.example/transfer?amount=$1000000&account-to=999999">

当用户浏览器遇到该图像标签时,它会向该URL发出GET请求。由于请求来自他的浏览器,它会发送与bank.example关联的所有cookie。如果用户最近登录到bank.example……session_id cookie将被设置,服务器会认为用户打算将1,000,000美元转移到帐户999999!

输入图片描述

好吧,只是不要去危险的地方,你会没事的。

这还不够。如果有人将该图像发布到Facebook并出现在您的墙上怎么办?如果它被注入您正在访问的XSS攻击网站怎么办?

还不错。只有GET请求容易受到攻击。

不正确。可以动态生成发送POST请求的表单。这是Rails安全指南中的示例:

<a href="http://www.harmless.example/" onclick="var f = document.createElement('form');f.style.display = 'none';this.parentNode.appendChild(f);f.method = 'POST';f.action = 'http://www.example.com/account/destroy';f.submit();return false;">To the harmless survey</a>

真实性令牌

当你的ApplicationController有这个:

protect_from_forgery with: :exception

这个:

<%= form_tag do %>Form contents<% end %>

被编译成这样:

<form accept-charset="UTF-8" action="/" method="post"><input name="utf8" type="hidden" value="&#x2713;" /><input name="authenticity_token" type="hidden" value="J7CBxfHalt49OSHp27hblqK20c9PgwJ108nDHX/8Cts=" />Form contents</form>

具体而言,产生了以下内容:

<input name="authenticity_token" type="hidden" value="J7CBxfHalt49OSHp27hblqK20c9PgwJ108nDHX/8Cts=" />

为了防止CSRF攻击,如果Rails没有看到与请求一起发送的真实性令牌,它将不会认为请求是安全的。

攻击者如何知道这个令牌是什么?每次生成表单时都会随机生成一个不同的值:

输入图片描述

跨站脚本攻击(XSS)攻击-就是这样。但这是不同日子的不同漏洞。