提问人:Ricardo Acras 提问时间:6/3/2009 最后编辑:Mateen UlhaqRicardo Acras 更新时间:9/21/2023 访问量:226326
了解 Rails 真实性令牌
Understanding the Rails Authenticity Token
答:
真实性令牌的设计使您知道您的表单是从您的网站提交的。它是从运行它的计算机生成的,具有只有您的计算机才能知道的唯一标识符,从而有助于防止跨站点请求伪造攻击。
如果您只是在 rails 拒绝 AJAX 脚本访问时遇到困难,您可以使用
<%= form_authenticity_token %>
在创建表单时生成正确的令牌。
您可以在文档中阅读有关它的更多信息。
是 rails 防止“跨站请求伪造(CSRF 或 XSRF)攻击”的方法。Authenticity Token
简单来说,它确保对 Web 应用的 PUT / POST / DELETE(可以修改内容的方法)请求是从客户端的浏览器发出的,而不是从有权访问在客户端创建的 cookie 的第三方(攻击者)发出的。
会发生什么
当用户查看表单以创建、更新或销毁资源时,Rails 应用程序会创建一个随机的,将此令牌存储在会话中,并将其放置在表单的隐藏字段中。当用户提交表单时,Rails 会查找 ,将其与会话中存储的表单进行比较,如果它们匹配,则允许请求继续。authenticity_token
authenticity_token
为什么会这样
由于真实性令牌存储在会话中,因此客户端无法知道其值。这样可以防止人们向 Rails 应用提交表单,而无需在该应用本身中查看表单。
想象一下,您正在使用服务 A,您登录了该服务,一切正常。现在想象一下,你去使用服务 B,你看到了一张你喜欢的图片,然后按下图片查看它的更大尺寸。现在,如果服务 B 上存在一些恶意代码,它可能会向服务 A(您已登录)发送请求,并通过向 发送请求来要求删除您的帐户。这就是所谓的 CSRF(跨站点请求伪造)。http://serviceA.example/close_account
如果服务 A 使用真实性令牌,则此攻击媒介不再适用,因为来自服务 B 的请求将不包含正确的真实性令牌,并且不允许继续。
API 文档描述了有关元标记的详细信息:
CSRF 保护是用该方法打开的, 检查令牌并在会话不匹配时重置会话 是意料之中的。为新的 Rails 生成对此方法的调用 默认情况下,应用程序。 token 参数是默认命名的。名称 并且必须将此令牌的值添加到呈现的每个布局中 表单,包含在 HTML 标题中。
protect_from_forgery
authenticity_token
csrf_meta_tags
笔记
请记住,Rails 只验证不验证幂等方法(POST、PUT/PATCH 和 DELETE)。不检查 GET 请求的真实性令牌。为什么?因为 HTTP 规范规定 GET 请求是幂等的,不应在服务器上创建、更改或销毁资源,并且请求应该是幂等的(如果多次运行相同的命令,则每次都应得到相同的结果)。
此外,实际实现在开始时定义要复杂一些,以确保更好的安全性。Rails 不会对每个表单都发出相同的存储令牌。它也不会每次都生成和存储不同的令牌。它在会话中生成并存储加密哈希,并颁发新的加密令牌,每次呈现页面时,这些令牌都可以与存储的令牌进行匹配。请参阅 request_forgery_protection.rb。
课
用于保护非幂等方法(POST、PUT/PATCH 和 DELETE)。此外,请确保不允许任何可能修改服务器上资源的 GET 请求。authenticity_token
查看@erturne关于 GET 请求幂等的注释。他比我在这里做的更好。
评论
请注意,如果来自同一客户端的多个并发请求,真实性令牌机制可能会导致争用条件。在这种情况下,您的服务器可以生成多个真实性令牌,而应该只有一个,并且以表单接收早期令牌的客户端将在下一个请求中失败,因为会话 cookie 令牌已被覆盖。 这里有一篇关于这个问题的文章和一个不完全平凡的解决方案:http://www.paulbutcher.com/2007/05/race-conditions-in-rails-sessions-and-how-to-fix-them/
什么是CSRF?
真实性令牌是跨站点请求伪造 (CSRF) 的对策。你问什么是CSRF?
攻击者可以在不知道会话令牌的情况下劫持会话。
场景:
- 访问您银行的网站,登录。
- 然后访问攻击者的网站(例如,来自不受信任组织的赞助广告)。
- 攻击者的页面包含与银行的“转账资金”表单具有相同字段的表单。
- 攻击者知道您的帐户信息,并预先填写了表单字段,用于将资金从您的帐户转移到攻击者的帐户。
- 攻击者的页面包含向银行提交表单的 Javascript。
- 提交表单后,浏览器会包含银行网站的 cookie,包括会话令牌。
- 银行将资金转入攻击者的账户。
- 表单可以位于不可见的 iframe 中,因此您永远不知道攻击发生了。
- 这称为跨站点请求伪造 (CSRF)。
CSRF解决方案:
- 服务器可以标记来自服务器本身的表单
- 每个表单都必须包含一个额外的身份验证令牌作为隐藏字段。
- 令牌必须是不可预测的(攻击者无法猜测它)。
- 服务器在其页面的表单中提供有效的令牌。
- 服务器在表单发布时检查令牌,拒绝没有正确令牌的表单。
- 示例令牌:使用服务器密钥加密的会话标识符。
- Rails 会自动生成这样的标记:查看每个表单中的authenticity_token输入字段。
评论
因为是如此重要,在 Rails 3.0+ 中你可以使用Authenticity Token
<%= token_tag nil %>
创建
<input name="authenticity_token" type="hidden" value="token_value">
无论何处
评论
XSS
value="token_value"
需要authenticity_token
的方法
authenticity_token
在幂等方法(如 post、put 和 delete)的情况下是必需的,因为幂等方法会影响数据。
为什么需要它
这是防止邪恶行为所必需的。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是如何工作的?
基本上:
HTML 帮助程序,例如在表单中添加隐藏字段(如果它不是 GET 表单)
form_tag
AJAX 由 jquery-ujs 自动处理,它从添加到标头的元素中读取令牌(存在于默认模板中),并将其添加到发出的任何请求中。
meta
csrf_meta_tags
uJS 还会尝试在过时的缓存片段中的表单中更新令牌。
其他预防方法
- 检查是否存在某些标头,例如:
X-Requested-With
- 检查标头的值:https://security.stackexchange.com/questions/91165/why-is-the-synchronizer-token-pattern-preferred-over-the-origin-header-check-to
Origin
- 重新身份验证:再次要求用户输入密码。对于每个关键操作(银行登录和汇款,大多数网站的密码更改),都应该这样做,以防您的网站被XSSed。缺点是用户必须多次输入密码,这很烦人,并增加了键盘记录/肩部冲浪的机会。
评论
什么是authentication_token?
这是 rails 应用程序使用的随机字符串,用于确保用户从应用程序页面请求或执行操作,而不是从其他应用程序或站点请求或执行操作。
为什么需要authentication_token?
保护您的应用或网站免遭跨站点请求伪造。
如何将authentication_token添加到表单中?
如果使用 form_for 标记生成表单,则会自动添加authentication_token,否则您可以使用 .<%= csrf_meta_tag %>
真实性令牌用于防止跨站点请求伪造攻击 (CSRF)。要了解真实性令牌,必须首先了解 CSRF 攻击。
CSRF的
假设您是 的作者。您的网站上有一个表单,用于通过 GET 请求将资金转入其他帐户:bank.example
黑客可以向服务器发送一个HTTP请求,说,对吧?GET /transfer?amount=$1000000&account-to=999999
错。黑客攻击是行不通的。服务器基本上会想吗?
哼?这个试图发起转移的人是谁。它不是帐户的所有者,这是肯定的。
服务器如何知道这一点?因为没有 cookie 对请求者进行身份验证。session_id
当您使用用户名和密码登录时,服务器会在您的浏览器上设置一个 cookie。这样,您就不必使用用户名和密码对每个请求进行身份验证。当您的浏览器发送 cookie 时,服务器会知道:session_id
session_id
哦,那是约翰·多伊。他在 2.5 分钟前成功登录。他很高兴。
黑客可能会想:
嗯。普通的HTTP请求是行不通的,但如果我能得到那个cookie,我就会成为金牌。
session_id
用户浏览器为域设置了一堆 cookie。每次用户向域发出请求时,都会发送所有 cookie。包括 cookie。bank.example
bank.example
session_id
因此,如果黑客可以让你发出 GET 请求,将钱转入他的账户,他就会成功。他怎么会骗你这样做? 与跨站点请求伪造。
实际上,这很简单。黑客可以让你访问他的网站。在他的网站上,他可以有以下图像标签:
<img src="http://bank.example/transfer?amount=$1000000&account-to=999999">
当用户浏览器遇到该图像标签时,它将向该 URL 发出 GET 请求。由于请求来自他的浏览器,它将发送与 .如果用户最近登录了...cookie将被设置,服务器将认为用户打算将1,000,000美元转移到帐户999999!bank.example
bank.example
session_id
好吧,只要不要去危险的地方,你就会没事的。
这还不够。如果有人将该图片发布到 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="✓" />
<input name="authenticity_token" type="hidden" value="J7CBxfHalt49OSHp27hblqK20c9PgwJ108nDHX/8Cts=" />
Form contents
</form>
具体而言,会生成以下内容:
<input name="authenticity_token" type="hidden" value="J7CBxfHalt49OSHp27hblqK20c9PgwJ108nDHX/8Cts=" />
为了防止 CSRF 攻击,如果 Rails 没有看到与请求一起发送的真实性令牌,它就不会认为请求是安全的。
攻击者应该如何知道这个令牌是什么?每次生成表单时,都会随机生成不同的值:
跨站点脚本 (XSS) 攻击 - 就是这样。但对于不同的一天来说,这是一个不同的漏洞。
在 Ruby on Rails 中,真实性令牌(通常称为 CSRF(跨站点请求伪造)令牌)是一种安全功能,用于保护 Web 应用程序免受某些类型的攻击,尤其是 CSRF 攻击。
评论