关于Ruby:了解Rails的真实性令牌

Understanding the Rails Authenticity Token

我在Rails中遇到了一些关于真实性令牌的问题,现在我已经有很多次了。

但我真的不想仅仅解决这个问题然后继续。我真的很想了解真实性标记。好吧,我的问题是,你有关于这个主题的完整信息来源吗,或者你会花时间在这里详细解释一下吗?


发生什么事

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

为什么会发生

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

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

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

CSRF protection is turned on with the protect_from_forgery method,
which checks the token and resets the session if it doesn't match what
was expected. A call to this method is generated for new Rails
applications by default.
The token parameter is named authenticity_token by default. The name
and value of this token must be added to every layout that renders
forms by including csrf_meta_tags in the HTML head.

笔记

请记住,rails只验证非等幂方法(post、put/patch和delete)。不检查GET请求的真实性令牌。为什么?因为HTTP规范声明GET请求是等量的,不应该在服务器上创建、更改或破坏资源,并且请求应该是等量的(如果多次运行同一命令,则每次都应该得到相同的结果)。

而且,真正的实现在一开始就有点复杂,以确保更好的安全性。Rails不会对每个表单都发出相同的存储令牌。它也不会每次生成和存储不同的令牌。它在会话中生成和存储加密哈希,并在每次呈现页面时发布新的加密令牌,这些令牌可以与存储的令牌匹配。参见请求伪造保护.rb。

教训

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

编辑:检查@erturne关于get请求为等幂的注释。他解释得比我解释得好。


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

如果您很难使用拒绝Ajax脚本访问的Rails,那么可以使用

1
<%= form_authenticity_token %>

在创建表单时生成正确的令牌。

您可以在文档中了解更多关于它的信息。


什么是CSRF?

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

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

脚本:

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

CSRF解决方案:

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


可防止的最小攻击示例

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

1
2
3
4
5
6
7
8
9
10
11
<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>

如果您是通过会话cookies登录到您的银行,那么cookies将被发送,并且在您甚至不知道的情况下进行转账。

也就是说,如果CSRF代币发挥作用:

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

因此,真实浏览器上的表单应该是:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<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参数,而且由于它是一个巨大的随机数,所以我无法猜测它。

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

同步器令牌模式的工作是因为同一个源策略:如果我可以从evil.com向您的银行发出一个xhr-get请求,并读取结果,我就可以读取一个令牌,然后再进行请求。我在https://security.stackexchange.com/a/72569/53321中对此作了进一步解释。

我强烈建议您阅读有关此和任何其他安全问题的OWASP指南。

Rails如何发送令牌

在:rails:csrf-meta-tag如何工作?

基本上:

  • form_tag这样的HTML帮助程序,如果表单不是get表单,就为您在表单中添加一个隐藏字段。

  • jquery ujs会自动处理ajax,它从EDOCX1(默认模板中存在)添加到头中的meta元素中读取令牌,并将其添加到任何请求中。

    UJS还尝试更新过期缓存片段中表单中的令牌。

其他预防方法

  • 检查是否存在某些头段,如X-Requested-With
    • x-requested-with头有什么意义?
    • https://security.stackexchange.com/questions/23371/csrf-protection-with-custom-headers-and-without-validating-token
    • 对于Ajax驱动的应用程序,带有头服务器的x-requested-check是否足以抵御CSRF?
  • 检查Origin头的值:https://security.stackexchange.com/questions/91165/why-is-the-synchronizer-token-pattern-preferred-over-the-origin-header-check-to
  • 重新验证:再次询问用户密码。这应该针对每一个关键操作(银行登录和转账,大多数网站的密码更改),以防您的网站曾经被XSSED。缺点是用户必须多次输入密码,这很烦人,并且增加了键盘记录/肩上冲浪的机会。


Authenticity Token是rails防止"跨站点请求伪造(csrf或xsrf)攻击"的方法。

简单来说,它可以确保对Web应用程序的Put/Post/Delete(可以修改内容的方法)请求是从客户端浏览器发出的,而不是从能够访问客户端创建的cookie的第三方(攻击者)发出的。


令牌is used to the工作真实防止跨站请求(CSRF)forgery人来袭。了解茶叶工作真实你必须明白CSRF的令牌,第一人来袭。P></CSRF

我想你是bank.comthe author of。你有你的网站形式由that is used to转移到不同的帐户与金钱:get requestP></

enter image description hereP></

黑客可以只发送http request to the服务器安GET /transfer?amount=$1000000&account-to=999999说,对吗?P></

enter image description hereP></

错误的。《黑客攻击不会工作。茶叶基本服务器将认为?P></

Huh? Who is this guy trying to initiate a transfer. It's not the owner of the account, that's for sure.

how does the服务器知道this?因为没有session_idauthenticating the requester饼干。P></

当你登录你的用户名和密码与服务器的集合,在你的浏览器的cookie session_id在线。我的方式,你不authenticate have to each with your request的用户名和密码。当你的浏览器session_id末茶饼干,茶服务器知道:P></

Oh, that's John Doe. He signed in successfully 2.5 minutes ago. He's good to go.

黑客会想:P></

Hmm. A normal HTTP request won't work, but if I could get my hand on that session_id cookie, I'd be golden.

has the users of cookies浏览器帮for the bank.com集域。在每一个时间让the user to the request of the cookie的域bank.com,得到在我的。including the session_id饼干。P></

如果我能让你知道黑客get that the request to Make Money into transfers他的帐户,他是成功的。如何做你知道他欺骗我"?与forgery跨网站请求。P></

这是很简单的,真的。我只是想你get the黑客访问他的网站。在他的网站,他可以有下面的图像标签:P></

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

在用户的浏览器来that when the图像标签,它会使get request to that URL。和since the request来从他的浏览器,它会发送cookie -它bank.comof the Associated with。如果有recently the user to bank.comsigned中……session_idwill be the cookie服务器集,and the the user to that will)!万美元的帐户转移到999999。P></

enter image description hereP></

Well, just don't visit dangerous sites and you'll be fine.

那不是足够的。如果输入图像到Facebook和EN posts that appears在线你的墙吗?如果它进入你访问网站的注入和XSS攻击?P></

It's not so bad. Only GET requests are vulnerable.

not true。形态后,在末dynamically request can be generated。这是the example from the rails在线安全指南:P></

1
2
3
4
5
6
7
8
<a href="http://www.harmless.com/" 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

工作真实令牌

当你ApplicationController:This hasP></

1
protect_from_forgery with: :exception

本:P></

1
2
3
<%= form_tag do %>
  Form contents
<% end %>

compiled into this is:P></

1
2
3
4
5
<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>

特别是,the following is generated:P></

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

CSRF的保护对人来袭,if the rails不喜欢在欧洲工作真实与请求令牌,它不考虑安全the request。P></

attacker to how is an应该知道令牌is this?在不同的时间值randomly each is generated the form is generated:P></

enter image description hereP></

跨网站脚本攻击(XSS)-这是诀窍。但这是不同的在不同的弹性for a Day。P></


由于Authenticity Token非常重要,在rails 3.0+中可以使用

1
 <%= token_tag nil %>

创造

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

在任何地方


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


需要authenticity_token的方法

authenticity_token is required in case of idempotent methods like post, put and delete, Because Idempotent methods are affecting to data.

为什么需要

It is required to prevent from evil actions. authenticity_token is stored in session, whenever a form is created on web pages for creating or updating to resources then a authenticity token is stored in hidden field and it sent with form on server. Before executing action user sent authenticity_token is cross checked with authenticity_token stored in session. If authenticity_token is same then process is continue otherwise it does not perform actions.


什么是身份验证令牌?

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

为什么需要身份验证令牌?

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

如何向表单添加身份验证令牌?

如果您生成的表单使用表单_进行标记,则会自动添加身份验证_令牌,否则您可以使用<%= csrf_meta_tag %>