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