了解Rails真实性令牌


981

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

但是我真的不想仅仅解决这个问题然后继续。我真的很想了解真实性令牌。好吧,我的问题是,您是否有关于此主题的完整信息来源,或者您愿意花时间在此处进行详细说明?


7
另请参阅:“为什么Google会将while(1)放在他们的JSON响应前面?” stackoverflow.com/questions/2669690/…–
Chloe

Answers:


1462

怎么了

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

为什么会发生

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

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

API文档介绍了有关元标记的详细信息:

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

笔记

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

而且,如开始定义的那样,实际的实现要复杂一些,以确保更好的安全性。Rails不会为每种表单发布相同的存储令牌。每次都不会生成并存储不同的令牌。它在会话中生成并存储加密哈希,并在每次呈现页面时发出新的加密令牌,这些令牌可以与存储的令牌进行匹配。请参阅request_forgery_protection.rb

经验教训

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


编辑:通过@erturne检查有关GET请求是幂等的评论。他用比我在这里做的更好的方式解释了这一点。


25
@Faisal,那么,攻击者是否有可能会简单地读取/捕获服务A表单的“隐藏”元素并获取为用户生成的唯一令牌-假设他们已经获得了由用户启动的会话的访问权限服务A?
marcamillion

11
@marcamillion:如果有人在服务A劫持了您的会话,那么真实性令牌将无法保护您。劫机者将能够提交请求,并将其继续进行。
Faisal

12
@zabba:如果提交的表单没有适当的标记,Rails会引发ActionController :: InvalidAuthenticityToken异常。您可以从异常中抢救并进行所需的任何处理。
Faisal'1

5
“还请确保不要发出任何可能会修改服务器资源的GET请求。” -这包括在路由中不使用match(),这可能会允许对仅接收POST的控制器操作进行GET请求
Steven Soroka'4

102
“ ...并且请求应该是幂等的(如果多次运行相同的命令,则每次都应获得相同的结果)。” 这里只是一个微妙的澄清。安全意味着没有副作用。幂等意味着相同的副作用,无论调用服务多少次。由于没有副作用,因此所有安全服务本质上都是幂等的。多次在当前时间资源上调用GET每次都会返回不同的结果,但这是安全的(因此是幂等的)。
erturne

137

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

如果您在拒绝Rails拒绝AJAX脚本访问方面遇到困难,则可以使用

<%= form_authenticity_token %>

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

您可以在文档中阅读有关它的更多信息。


88

什么是CSRF?

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

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

场景

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

CSRF解决方案

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

1
这是相同解释的一个版本,其精确度较低,但摘要也较抽象:stackoverflow.com/a/33829607/2810305
Lutz Prechelt

我不确定,但是,现代浏览器是否允许不将幂等请求(POST / PUT / DELETE)发送到另一个域?我想,在浏览器本身中必须有防止这种情况的方法
–dividerByZero

45

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

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

<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如何工作?

基本上:

  • HTML帮助程序(例如form_tag,如果不是GET表单)会为表单添加一个隐藏字段

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

    uJS还尝试以过期的缓存片段形式更新令牌。

其他预防方法


谢谢,但是关于依靠相同的原始策略不能首先仅读取CSRF令牌的观点似乎存在缺陷。所以首先您说您可以POST到其他来源,但无法读取它,这看起来很奇怪,但我想这是正确的,但是您可以在页面上插入带有get的图像或脚本标签,并链接处理程序以解析响应并得到它吗?
bjm88 '17

@ bjm88注入脚本在哪里?在您的网站上还是在受攻击的网站上?如果是受攻击的网站,则允许脚本注入是众所周知的安全漏洞,并有效地典当该网站。每个网站都必须通过输入卫生措施与之抗争。对于图像,我看不到如何将其用于攻击。在攻击网站上:您可以修改浏览器以允许读取,从而随意随意进行典当:-),但是默认情况下不错的浏览器会阻止它,请尝试一下。
Ciro Santilli冠状病毒审查六四事件法轮功

43

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

CSRF

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

在此处输入图片说明

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

在此处输入图片说明

错误。黑客攻击将无法进行。服务器基本上会怎么想?

??这个人是谁在尝试发起转移?可以肯定的是,它不是帐户的所有者。

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

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

哦,那是约翰·多伊。他于2.5分钟前成功登录。他很好走。

黑客可能会认为:

嗯 普通的HTTP请求将无法正常工作,但是如果我能亲身体验该session_idcookie,那将是无与伦比的。

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

因此,如果黑客可以诱使提出将钱转入他的帐户的GET请求,那么他将成功。他怎么会骗你呢?与跨站点请求伪造。

实际上,这很简单。黑客可能只是让您访问他的网站。在他的网站上,他可以具有以下图像标签:

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

当用户浏览器遇到该图片标签时,它将向该网址发出GET请求。由于该请求来自他的浏览器,因此它将与一起发送与之关联的所有cookie bank.com。如果用户最近登录到bank.com...,session_id将设置cookie,服务器将认为用户打算将$ 1,000,000转移到帐户999999!

在此处输入图片说明

好吧,只是不要访问危险场所,您会没事的。

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

也不是那么坏。只有GET请求容易受到攻击。

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

<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</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)攻击-就是这样。但这是一天之内的另一种漏洞。


39

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

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


34

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

 <%= token_tag nil %>

去创造

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

任何地方


这对我很有帮助。我实际上试图XSS在登录页面上进行此操作,不是出于恶意目的,而是使用预先填写的用户名创建一个新会话。现在我知道我可以使用了value="token_value"
Michael-Clay Shirky在哪里,2014年

27

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


11

方法authenticity_token需要的地方

authenticity_token 对于幂等方法(例如post,put和delete)是必需的,因为幂等方法会影响数据。

为什么需要

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


3
其实不是吗?GET是幂等的,因为它的调用不应更改系统的状态,而PUT POST和DELETE动词不是幂等的动词,因为它们会更改系统状态。IE:如果不是NOT幂等方法,则需要authenticity_token。
让-西奥

2
@ Jean-Daube,乌干达:幂等的意思是,如果做两次,动作只会发生一次。GET,PUT和DELETE 幂等的:w3.org/Protocols/rfc2616/rfc2616-sec9.html此处的关键属性不是幂等,但是如果方法更改或不更改数据,则称为“安全方法”。
Ciro Santilli冠状病毒审查六四事件法轮功

6

什么是authentication_token?

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

为什么需要authentication_token?

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

如何在表格中添加一个authentication_token?

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.