了解 Rails 真实性令牌

发布于 2024-07-22 09:21:06 字数 24 浏览 2 评论 0原文

Rails 中的真实性令牌是什么?

What is the Authenticity Token in Rails?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(11

執念 2024-07-29 09:21:06

会发生什么

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

为什么会发生

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

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

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

使用protect_from_forgery方法开启CSRF保护,
它检查令牌并在与内容不匹配时重置会话
是预料之中的。 为新的 Rails 生成对此方法的调用
默认情况下的应用程序。
默认情况下,令牌参数名为 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 请求。


检查@erturne关于 GET 请求幂等的评论。 他以比我在这里所做的更好的方式解释了这一点。

What happens

When the user views a form to create, update, or destroy a resource, the Rails app creates a random authenticity_token, stores this token in the session, and places it in a hidden field in the form. When the user submits the form, Rails looks for the authenticity_token, compares it to the one stored in the session, and if they match the request is allowed to continue.

Why it happens

Since the authenticity token is stored in the session, the client cannot know its value. This prevents people from submitting forms to a Rails app without viewing the form within that app itself.
Imagine that you are using service A, you logged into the service and everything is OK. Now imagine that you went to use service B, and you saw a picture you like, and pressed on the picture to view a larger size of it. Now, if some evil code was there at service B, it might send a request to service A (which you are logged into), and ask to delete your account, by sending a request to http://serviceA.example/close_account. This is what is known as CSRF (Cross Site Request Forgery).

If service A is using authenticity tokens, this attack vector is no longer applicable, since the request from service B would not contain the correct authenticity token, and will not be allowed to continue.

API docs describes details about meta tag:

CSRF protection is turned on with the protect_from_forgery method,
which checks the token and resets the session if it doesn't match what
was expected. A call to this method is generated for new Rails
applications by default.
The token parameter is named authenticity_token by default. The name
and value of this token must be added to every layout that renders
forms by including csrf_meta_tags in the HTML head.

Notes

Keep in mind, Rails only verifies not idempotent methods (POST, PUT/PATCH and DELETE). GET request are not checked for authenticity token. Why? because the HTTP specification states that GET requests is idempotent and should not create, alter, or destroy resources at the server, and the request should be idempotent (if you run the same command multiple times, you should get the same result every time).

Also the real implementation is a bit more complicated as defined in the beginning, ensuring better security. Rails does not issue the same stored token with every form. Neither does it generate and store a different token every time. It generates and stores a cryptographic hash in a session and issues new cryptographic tokens, which can be matched against the stored one, every time a page is rendered. See request_forgery_protection.rb.

Lessons

Use authenticity_token to protect your not idempotent methods (POST, PUT/PATCH, and DELETE). Also make sure not to allow any GET requests that could potentially modify resources on the server.


Check the comment by @erturne regarding GET requests being idempotent. He explains it in a better way than I have done here.

绝情姑娘 2024-07-29 09:21:06

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

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

<%= form_authenticity_token %>

在创建表单时使用它来生成正确的令牌。

您可以在文档中了解更多相关信息。

The authenticity token is designed so that you know your form is being submitted from your website. It is generated from the machine on which it runs with a unique identifier that only your machine can know, thus helping prevent cross-site request forgery attacks.

If you are simply having difficulty with rails denying your AJAX script access, you can use

<%= form_authenticity_token %>

to generate the correct token when you are creating your form.

You can read more about it in the documentation.

↘紸啶 2024-07-29 09:21:06

什么是 CSRF?

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

攻击者甚至可以在不知道会话令牌的情况下劫持会话。

场景

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

CSRF 解决方案

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

What is CSRF?

The Authenticity Token is a countermeasure to Cross-Site Request Forgery (CSRF). What is CSRF, you ask?

It's a way that an attacker can potentially hijack sessions without even knowing session tokens.

Scenario:

  • Visit your bank's site, log in.
  • Then visit the attacker's site (e.g. sponsored ad from an untrusted organization).
  • Attacker's page includes form with same fields as the bank's "Transfer Funds" form.
  • Attacker knows your account info, and has pre-filled form fields to transfer money from your account to attacker's account.
  • Attacker's page includes Javascript that submits form to your bank.
  • When form gets submitted, browser includes your cookies for the bank site, including the session token.
  • Bank transfers money to attacker's account.
  • The form can be in an iframe that is invisible, so you never know the attack occurred.
  • This is called Cross-Site Request Forgery (CSRF).

CSRF solution:

  • Server can mark forms that came from the server itself
  • Every form must contain an additional authentication token as a hidden field.
  • Token must be unpredictable (attacker can't guess it).
  • Server provides valid token in forms in its pages.
  • Server checks token when form posted, rejects forms without proper token.
  • Example token: session identifier encrypted with server secret key.
  • Rails automatically generates such tokens: see the authenticity_token input field in every form.
jJeQQOZ5 2024-07-29 09:21:06

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

CSRF

假设您是 bank.example 的作者。 您的网站上有一个表单,用于通过 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.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 !

输入图像描述这里

好吧,只要不要访问危险的网站就可以了。

这还不够。 如果有人将该图像发布到 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

This:

<%= 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) 攻击 - 就是这样。 但这是不同日子的不同漏洞。

The authenticity token is used to prevent Cross-Site Request Forgery attacks (CSRF). To understand the authenticity token, you must first understand CSRF attacks.

CSRF

Suppose that you are the author of bank.example. You have a form on your site that is used to transfer money to a different account with a GET request:

enter image description here

A hacker could just send an HTTP request to the server saying GET /transfer?amount=$1000000&account-to=999999, right?

enter image description here

Wrong. The hackers attack won't work. The server will basically think?

Huh? Who is this guy trying to initiate a transfer. It's not the owner of the account, that's for sure.

How does the server know this? Because there's no session_id cookie authenticating the requester.

When you sign in with your username and password, the server sets a session_id cookie on your browser. That way, you don't have to authenticate each request with your username and password. When your browser sends the session_id cookie, the server knows:

Oh, that's John Doe. He signed in successfully 2.5 minutes ago. He's good to go.

A hacker might think:

Hmm. A normal HTTP request won't work, but if I could get my hand on that session_id cookie, I'd be golden.

The users browser has a bunch of cookies set for the bank.example domain. Every time the user makes a request to the bank.example domain, all of the cookies get sent along. Including the session_id cookie.

So if a hacker could get you to make the GET request that transfers money into his account, he'd be successful. How could he trick you into doing so?
With Cross Site Request Forgery.

It's pretty simply, actually. The hacker could just get you to visit his website. On his website, he could have the following image tag:

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

When the users browser comes across that image tag, it'll be making a GET request to that url. And since the request comes from his browser, it'll send with it all of the cookies associated with bank.example. If the user had recently signed in to bank.example... the session_id cookie will be set, and the server will think that the user meant to transfer $1,000,000 to account 999999!

enter image description here

Well, just don't visit dangerous sites and you'll be fine.

That isn't enough. What if someone posts that image to Facebook and it appears on your wall? What if it's injected into a site you're visiting with a XSS attack?

It's not so bad. Only GET requests are vulnerable.

Not true. A form that sends a POST request can be dynamically generated. Here's the example from the 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>

Authenticity Token

When your ApplicationController has this:

protect_from_forgery with: :exception

This:

<%= form_tag do %>
  Form contents
<% end %>

Is compiled into this:

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

In particular, the following is generated:

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

To protect against CSRF attacks, if Rails doesn't see the authenticity token sent along with a request, it won't consider the request safe.

How is an attacker supposed to know what this token is? A different value is generated randomly each time the form is generated:

enter image description here

A Cross Site Scripting (XSS) attack - that's how. But that's a different vulnerability for a different day.

甜柠檬 2024-07-29 09:21:06

可以防止的最小攻击示例: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 响应,
  • 当浏览器发出 POST 请求时,Rails 会发送一个很长的随机隐藏参数,它会一起发送该参数,而服务器只会接受它如果它匹配,

那么真实浏览器上的表单将如下所示:

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

基本上:

  • 如果不是 GET 表单,

    form_tag 这样的 HTML 助手会为您在表单中添加一个隐藏字段

  • AJAX 由 jquery-ujs,它从 csrf_meta_tags 添加到标头的 meta 元素中读取令牌(存在于默认模板),并将其添加到发出的任何请求中。

    uJS 还尝试更新过时的缓存片段中表单中的令牌。

其他预防方法

Minimal attack example that would be prevented: CSRF

On my website evil.example I convince you to submit the following form:

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

If you are logged into your bank through session cookies, then the cookies would be sent and the transfer would be made without you even knowing it.

That is were the CSRF token comes into play:

  • with the GET response that that returned the form, Rails sends a very long random hidden parameter
  • when the browser makes the POST request, it will send the parameter along, and the server will only accept it if it matches

So the form on an authentic browser would look like:

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

Thus, my attack would fail, since it was not sending the authenticity_token parameter, and there is no way I could have guessed it since it is a huge random number.

This prevention technique is called Synchronizer Token Pattern.

Same Origin Policy

But what if the attacker made two requests with JavaScript, one to read the token, and the second one to make the transfer?

The synchronizer token pattern alone is not enough to prevent that!

This is where the Same Origin Policy comes to the rescue, as I have explained at: https://security.stackexchange.com/questions/8264/why-is-the-same-origin-policy-so-important/72569#72569

How Rails sends the tokens

Covered at: Rails: How Does csrf_meta_tag Work?

Basically:

  • HTML helpers like form_tag add a hidden field to the form for you if it's not a GET form

  • AJAX is dealt with automatically by jquery-ujs, which reads the token from the meta elements added to your header by csrf_meta_tags (present in the default template), and adds it to any request made.

    uJS also tries to update the token in forms in outdated cached fragments.

Other prevention approaches

身边 2024-07-29 09:21:06

Authenticity Token 是 Rails 的方法,用于防止 < a href="http://en.wikipedia.org/wiki/Cross-site_request_forgery#Example_and_characteristics" rel="noreferrer">“跨站点请求伪造(CSRF 或 XSRF)攻击”。

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

The Authenticity Token is rails' method to prevent 'cross-site request forgery (CSRF or XSRF) attacks'.

To put it simple, it makes sure that the PUT / POST / DELETE (methods that can modify content) requests to your web app are made from the client's browser and not from a third party (an attacker) that has access to a cookie created on the client side.

牵你的手,一向走下去 2024-07-29 09:21:06

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

 <%= token_tag nil %>

创建

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

在任何地方

since Authenticity Token is so important, and in Rails 3.0+ you can use

 <%= token_tag nil %>

to create

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

anywhere

蓝海似她心 2024-07-29 09:21:06

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

Beware the Authenticity Token mechanism can result in race conditions if you have multiple, concurrent requests from the same client. In this situation your server can generate multiple authenticity tokens when there should only be one, and the client receiving the earlier token in a form will fail on it's next request because the session cookie token has been overwritten.
There is a write up on this problem and a not entirely trivial solution here:
http://www.paulbutcher.com/2007/05/race-conditions-in-rails-sessions-and-how-to-fix-them/

吐个泡泡 2024-07-29 09:21:06

需要 authenticity_token 的方法

authenticity_token在post、put、delete等幂等方法的情况下是必需的,因为幂等方法会影响数据。

为什么需要它

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

Methods Where authenticity_token is required

authenticity_token is required in case of idempotent methods like post, put and delete, Because Idempotent methods are affecting to data.

Why It is Required

It is required to prevent from evil actions. authenticity_token is stored in session, whenever a form is created on web pages for creating or updating to resources then a authenticity token is stored in hidden field and it sent with form on server. Before executing action user sent authenticity_token is cross checked with authenticity_token stored in session. If authenticity_token is same then process is continue otherwise it does not perform actions.

巷子口的你 2024-07-29 09:21:06

什么是authentication_token?

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

为什么需要authentication_token?

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

如何将authentication_token添加到表单?

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

What is an authentication_token ?

This is a random string used by rails application to make sure that the user is requesting or performing an action from the app page, not from another app or site.

Why is an authentication_token is necessary ?

To protect your app or site from cross-site request forgery.

How to add an authentication_token to a form ?

If you are generating a form using form_for tag an authentication_token is automatically added else you can use <%= csrf_meta_tag %>.

靖瑶 2024-07-29 09:21:06

在 Ruby on Rails 中,真实性令牌通常称为 CSRF(跨站点请求伪造)令牌,是一种安全功能,用于保护 Web 应用程序免受某些类型的攻击,特别是 CSRF 攻击。

In Ruby on Rails, the authenticity token, often referred to as the CSRF (Cross-Site Request Forgery) token, is a security feature used to protect web applications from certain types of attacks, particularly CSRF attacks.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文