什么是Rails中的真实性令牌?


当前回答

真实性令牌是rails防止“跨站请求伪造(CSRF或XSRF)攻击”的方法。

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

其他回答

什么是CSRF?

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

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

场景:

Visit your bank's site, log in. Then visit the attacker's site (e.g. sponsored ad from an untrusted organization). Attacker's page includes form with same fields as the bank's "Transfer Funds" form. Attacker knows your account info, and has pre-filled form fields to transfer money from your account to attacker's account. Attacker's page includes Javascript that submits form to your bank. When form gets submitted, browser includes your cookies for the bank site, including the session token. Bank transfers money to attacker's account. The form can be in an iframe that is invisible, so you never know the attack occurred. This is called Cross-Site Request Forgery (CSRF).

CSRF的解决方案:

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

会发生什么

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

为什么会这样

Since the authenticity token is stored in the session, the client cannot know its value. This prevents people from submitting forms to a Rails app without viewing the form within that app itself. Imagine that you are using service A, you logged into the service and everything is OK. Now imagine that you went to use service B, and you saw a picture you like, and pressed on the picture to view a larger size of it. Now, if some evil code was there at service B, it might send a request to service A (which you are logged into), and ask to delete your account, by sending a request to http://serviceA.example/close_account. This is what is known as 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请求不检查真实性令牌。为什么?因为HTTP规范规定GET请求是幂等的,不应该在服务器上创建、修改或破坏资源,请求应该是幂等的(如果多次运行同一个命令,每次都应该得到相同的结果)。

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

教训

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


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

什么是authentication_token ?

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

为什么authentication_token是必要的?

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

如何将authentication_token添加到表单?

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

真实性令牌用于防止跨站点请求伪造攻击(Cross-Site Request Forgery attack, CSRF)。要理解真实性令牌,首先必须理解CSRF攻击。

CSRF

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

黑客只需向服务器发送一个HTTP请求GET /transfer?数量= $ 1000000 &account-to = 999999,对吧?

错了。黑客的攻击没用。服务器基本上会想?

嗯?这个想要转移的人是谁。肯定不是账户的主人。

服务器如何知道这一点?因为没有session_id cookie来验证请求者。

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

哦,那是无名氏。他在两分半钟前成功登陆。他可以出发了。

黑客可能会想:

嗯。正常的HTTP请求不会起作用,但如果我能得到session_id cookie,我就很好了。

用户浏览器为银行设置了一堆cookie。域的例子。每次用户向银行提出请求时。例如域,所有的cookie都会被发送。包括session_id cookie。

因此,如果黑客能够让您发出get请求,将资金转移到他的帐户,他就成功了。他怎么能骗你这么做呢? 与跨站点请求伪造。

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

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

当用户的浏览器遇到图像标记时,它将向该url发出GET请求。由于请求来自他的浏览器,它将发送与bank.example相关的所有cookie。如果用户最近登录了银行。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)攻击——就是这样。但这是不同时期的不同弱点。

Beware the Authenticity Token mechanism can result in race conditions if you have multiple, concurrent requests from the same client. In this situation your server can generate multiple authenticity tokens when there should only be one, and the client receiving the earlier token in a form will fail on it's next request because the session cookie token has been overwritten. There is a write up on this problem and a not entirely trivial solution here: http://www.paulbutcher.com/2007/05/race-conditions-in-rails-sessions-and-how-to-fix-them/