ChatGPT解决这个技术问题 Extra ChatGPT

了解 Rails 真实性令牌

我遇到了一些关于 Rails 中的 Authenticity Token 的问题。

我真的很想了解 Authenticity 令牌。

您是否有关于此主题的完整信息来源,或者您会花时间在这里详细解释吗?

另请参阅:“为什么 Google 在他们的 JSON 响应中添加 while(1)?” stackoverflow.com/questions/2669690/…
我也将其作为对答案的编辑:指向 github 存储库的链接,允许点击引用:pix.realquadrant.com/authenticity-token

S
Stephen Ostermiller

怎么了

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

为什么会发生

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

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

API docs 描述了有关元标记的详细信息:

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

笔记

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

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

课程

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

检查 the comment by @erturne 是否 GET 请求是幂等的。他以比我在这里所做的更好的方式解释它。


@Faisal,那么攻击者是否有可能简单地读取/捕获服务 A 表单的“隐藏”元素并获取为用户生成的唯一令牌 - 假设他们已经获得了对用户启动的会话的访问权限对于服务 A?
@marcamillion:如果有人在服务 A 上劫持了您的会话,那么真实性令牌将无法保护您。劫机者将能够提交请求并允许继续。
@zabba:如果提交的表单没有正确的令牌,Rails 会引发 ActionController::InvalidAuthenticityToken 异常。您可以从异常中救援并执行您想要的任何处理。
重新“还要确保不要发出任何可能修改服务器上资源的 GET 请求。” -- 这包括不在路由中使用 match(),这可能会允许 GET 请求到控制器操作,这些操作旨在仅接收 POST
“......并且请求应该是幂等的(如果您多次运行相同的命令,则每次都应该得到相同的结果)。”这里只是一个微妙的澄清。安全意味着没有副作用。幂等意味着无论调用多少次服务都会产生相同的副作用。所有安全服务本质上都是幂等的,因为没有副作用。在当前时间资源上多次调用 GET 每次都会返回不同的结果,但它是安全的(因此是幂等的)。
e
eikes

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

如果您只是在拒绝您的 AJAX 脚本访问时遇到障碍,您可以使用

<%= form_authenticity_token %>

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

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


L
Lutz Prechelt

什么是 CSRF?

Authenticity Token 是针对跨站点请求伪造 (CSRF) 的对策。你问什么是 CSRF?

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

设想:

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

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

攻击者的页面包括与银行的“转账”表单具有相同字段的表单。

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

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

提交表单后,浏览器会包含您的银行网站 cookie,包括会话令牌。

银行将钱转入攻击者的账户。

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

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

CSRF 解决方案:

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

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

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

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

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

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

Rails 会自动生成这样的令牌:查看每个表单中的authenticity_token 输入字段。


这是同一解释的一个版本,它不太精确但也不太抽象:stackoverflow.com/a/33829607/2810305
我不确定,但是现代浏览器是否允许向另一个域发送非幂等请求(POST/PUT/DELETE)?我想,浏览器本身必须有这样的保护措施
@divideByZero(哦,好名字!)CORS 标头的形式提供了一些保护。一个站点可以指定它希望从哪些域接收请求(并且某些浏览器/api 甚至更具限制性),但我不确定它何时被采用,或者是否真的旧浏览器都支持它,并且可能也希望拥有这种如果域将其 CORS 设置保留为 *. developer.mozilla.org/en-US/docs/Web/HTTP/CORS
S
Stephen Ostermiller

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

CSRF

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

https://i.stack.imgur.com/QV7s3.png

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

https://i.stack.imgur.com/acVPX.png

错误的。黑客攻击是行不通的。服务器基本上会怎么想?

嗯?这家伙是谁试图发起转移。这不是帐户的所有者,这是肯定的。

服务器怎么知道这个?因为没有 session_id cookie 对请求者进行身份验证。

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

哦,那是约翰·多伊。他在 2.5 分钟前成功登录。他可以走了。

黑客可能会想:

唔。一个普通的 HTTP 请求是行不通的,但是如果我能拿到那个 session_id cookie,我会很高兴的。

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

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

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

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

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

https://i.stack.imgur.com/UGrgL.png

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

这还不够。如果有人将该图像发布到 Facebook 并出现在您的墙上怎么办?如果它被注入到您正在访问的站点中,会受到 XSS 攻击怎么办?

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

不对。可以动态生成发送 POST 请求的表单。以下是 Rails Guide on Security 中的示例:

<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="&#x2713;" />
  <input name="authenticity_token" type="hidden" value="J7CBxfHalt49OSHp27hblqK20c9PgwJ108nDHX/8Cts=" />
  Form contents
</form>

具体来说,会生成以下内容:

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

为了防止 CSRF 攻击,如果 Rails 没有看到随请求一起发送的真实性令牌,它不会认为请求是安全的。

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

https://i.stack.imgur.com/4AJHB.gif

跨站点脚本 (XSS) 攻击 - 就是这样。但对于不同的日子,这是一个不同的漏洞。


S
Stephen Ostermiller

可以防止的最小攻击示例: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: How Does csrf_meta_tag Work?

基本上:

如果表单不是 GET 表单,HTML 助手(如 form_tag)会为您添加隐藏字段

AJAX 由 jquery-ujs 自动处理,它从由 csrf_meta_tags(存在于默认模板中)添加到您的标头的元元素中读取令牌,并将其添加到发出的任何请求中。 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驱动的应用程序?

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-to

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


谢谢,但是您关于依赖同源策略不能首先读取 CSRF 令牌的观点似乎有缺陷。所以首先你说你可以发布到不同的来源但不能从中读取,这看起来很奇怪,但我想这是正确的,但你可以注入一个带有获取页面的图像或脚本标签并链接一个处理程序来解析响应明白了吗?
@bjm88 在哪里注入脚本?在您的网站上,还是在被攻击的网站上?如果网站被攻击,允许脚本注入是一个众所周知的安全漏洞,并有效地典当网站。每个网站都必须通过输入卫生来对抗它。对于图像,我看不出它们如何用于攻击。在攻击站点上:您可以修改浏览器以允许读取,从而随意自动典当 :-) 但体面的浏览器默认阻止它,试一试。
a
andi

Authenticity Token 是 rails 对 prevent 'cross-site request forgery (CSRF or XSRF) attacks' 的方法。

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


Y
Yuan He

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

 <%= token_tag nil %>

去创造

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

任何地方


这对我很有帮助。我实际上是在尝试在登录页面上执行 XSS,不是出于恶意目的,而是为了创建一个带有预填充用户名的新会话。现在我知道我可以使用 value="token_value"
j
jdp

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


G
Gupta

需要 authenticity_token 的方法

如果使用幂等方法,如 post、put 和 delete,则需要authentity_token,因为幂等方法会影响数据。

为什么需要它

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


其实,不是相反吗? GET 是幂等的,因为它的调用不应改变系统的状态,其中 PUT POST 和 DELETE 动词不是幂等动词,因为它们会改变系统状态。 IE:如果不是幂等方法,则需要authenticity_token。
@Jean-Daube,uma:幂等意味着如果执行两次,则动作只发生一次。 GET、PUT和DELETE 幂等性:w3.org/Protocols/rfc2616/rfc2616-sec9.html这里的关键属性不是幂等性,而是方法是否改变了数据,这称为“安全方法”。
n
notapatch

什么是 authentication_token ?

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

为什么需要 authentication_token ?

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

如何将 authentication_token 添加到表单中?

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