提问



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


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

最佳参考


会发生什么


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


为什么会发生


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


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


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



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



备注


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


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


吸取


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





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

其它参考1


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


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


<%= form_authenticity_token %>


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


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

其它参考2


什么是CSRF?



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


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


方案:



  • 访问您银行的网站,登录。

  • 然后访问攻击者的网站(例如来自不受信任的组织的赞助广告)。

  • 攻击者的页面包含与银行转移资金表格相同字段的表格。

  • 攻击者知道您的帐户信息,并预先填写了表单字段,以便将资金从您的帐户转移到攻击者的帐户。

  • 攻击者的页面包含向您的银行提交表单的Javascript。

  • 当表单提交时,浏览器会包含您的银行网站的Cookie,包括会话令牌。

  • 银行将钱转移到攻击者的帐户。

  • 表单可以在不可见的iframe中,因此您永远不会知道发生了攻击。

  • 这称为跨站请求伪造(CSRF)。



CSRF解决方案:



  • 服务器可以标记来自服务器本身的表单

  • 每个表单都必须包含一个额外的身份验证令牌作为隐藏字段。

  • 令牌必须是不可预测的(攻击者无法猜测)。

  • 服务器在其页面中的表单中提供有效的令牌。

  • 服务器在发布表单时检查令牌,拒绝没有正确令牌的表单。

  • 示例令牌:使用服务器密钥加密的会话标识符。

  • Rails自动生成此类令牌:请参阅每个表单中的authenticity_token输入字段。


其它参考3


将被阻止的最小攻击示例


在我的网站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参数,并且我无法猜到它,因为它是一个巨大的随机数。


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


同步器令牌模式因同源策略而起作用:如果我可以从evil.com向您的银行发出XHR GET请求,并且读取结果,我将能够读取令牌然后再发出请求。我已在以下网址进一步解释:https://security.stackexchange.com/a/72569/53321 [52] [53]


我强烈建议您阅读OWASP指南,关于此问题以及任何其他安全问题。[54]


Rails如何发送令牌


涵盖在:Rails:csrf_meta_tag如何工作?


基本上:



  • form_tag这样的HTML帮助器为表单添加一个隐藏字段,如果它不是GET表单

  • AJAX由jquery-ujs自动处理,jquery-ujs通过csrf_meta_tags(存在于默认模板中)中添加到标题中的meta元素读取标记,并将其添加到任何请求中[56]


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



其他预防方法



  • 检查是否存在某些标题,例如X-Requested-With:


    • X-Requested-With标题的重点是什么?

    • https://security.stackexchange.com/questions/23371/csrf-protection-with-custom-headers-and-without-validating-token

    • X-Requested-With标头服务器是否足以检查ajax驱动的应用程序的CSRF?


  • 检查Origin标题的值:https://security.stackexchange.com/questions/91165/why-is-the-synchronizer-token-pattern-preferred-over-the-origin-header -check到

  • 重新验证:再次询问用户密码。这应该针对每个关键操作(银行登录和汇款,大多数网站的密码更改)进行,以防您的网站获得XSS。缺点是用户必须多次输入密码,这很烦人,并且增加了键盘记录/肩上冲浪的机会。


其它参考4


Authenticity Token是防止跨站请求伪造(CSRF或XSRF)攻击的方法。 [58] [60] [61]]] [62]


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

其它参考5


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


 <%= token_tag nil %>


创造


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


随地

其它参考6


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

其它参考7


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


CSRF



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


[64]


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


[65]


错误。黑客攻击不会起作用。服务器基本上会想到?



  咦?这个人试图启动转移是谁。它不是帐户的所有者,这是肯定的。



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


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



  哦,那是John Doe。他在2.5分钟前成功登录。他很高兴。



黑客可能会想:



  嗯。一个正常的HTTP请求不会起作用,但是如果我可以得到那个session_id cookie,那我就是金色的。



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


因此,如果黑客能够你发出将资金转入他账户的GET请求,他就会成功。他怎么能欺骗你这样做?
 使用跨站点请求伪造。


实际上它非常简单。黑客可以让你访问他的网站。在他的网站上,他可以有以下图像标记:


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


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


[66]



  好吧,只是不要访问危险的网站,你会没事的。



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



  它并不是那么糟糕。只有GET请求才是脆弱的。



不对。可以动态生成发送POST请求的表单。这是Rails安全指南中的例子:[67]


<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没有看到与请求一起发送的真实性令牌,它就不会认为请求是安全的。


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


[68]


一个跨站点脚本(XSS)攻击 - 这是怎么回事。但这是另一天的不同漏洞。

其它参考8


需要authenticity_token的方法



  authenticity_token在幂等方法(如post,put和delete)的情况下是必需的,因为Idempotent方法正在影响数据。



为什么需要



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


其它参考9


什么是authentication_token?


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


为什么需要authentication_token?


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


如何将authentication_token添加到表单?


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