了解Rails真实性令牌

我遇到了一些关于Rails真品令牌的问题,就像我现在已经很多次了。

但我真的不想解决这个问题,继续下去。 我真的很想了解真实性标记。 那么,我的问题是,你有关于这个问题的一些完整的信息来源,或者你会花时间在这里详细解释?

    怎么了

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

    为什么发生

    由于真实性标记存储在会话中,客户端无法知道其值。 这可以防止人们在没有查看该应用程序本身的表单的情况下将表单提交到Rails应用程序。 想象一下,你正在使用服务A,你login到服务,一切都很好。 现在想象一下,你去使用服务B,你看到了你喜欢的图片,并按下图片查看更大的尺寸。 现在,如果服务B中存在一些恶意代码,它可能会向服务A(您login的)发送一个请求,并通过向http://serviceA.com/close_account发送请求来要求删除您的帐户。 这就是所谓的CSRF(跨站点请求伪造) 。

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

    API文档介绍了有关元标记的详细信息:

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

    笔记

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

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

    教训

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


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

    真实性标记旨在让您知道您的表单正在从您的网站提交。 它是从运行的机器上生成的,只有你的机器可以知道的唯一标识符,从而有助于防止跨站请求伪造攻击。

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

     <%= form_authenticity_token %> 

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

    您可以在文档中阅读更多关于它的信息 。

    什么是CSRF?

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

    即使不知道会话令牌,攻击者也可能劫持会话。

    场景

    • 访问您银行的网站,login。
    • 然后访问攻击者的网站(例如,来自不受信任的组织的赞助广告)。
    • 攻击者的页面包括与银行“转账资金”表格相同的表格。
    • 攻击者知道你的账户信息,并且已经预先填写了表单域,把你账户中的钱转移到攻击者的账户上。
    • 攻击者的页面包括向您的银行提交表单的Javascript。
    • 当表单被提交时,浏览器会包含您的银行网站的cookies,包括会话令牌。
    • 银行将资金转移到攻击者的账户。
    • 表单可以在不可见的iframe中,所以你永远不知道发生了什么。
    • 这被称为跨站请求伪造(CSRF)。

    CSRF解决scheme

    • 服务器可以标记来自服务器本身的表单
    • 每个表单必须包含一个额外的身份validation令牌作为隐藏字段。
    • 令牌必须是不可预知的(攻击者无法猜测它)。
    • 服务器在其页面中的表单中提供有效的令牌。
    • 服务器在表单发布时检查标记,拒绝没有正确标记的表单。
    • 示例令牌:使用服务器密钥encryption的会话标识符。
    • Rails自动生成这样的标记:请参阅每种forms的authenticity_tokeninput字段。

    Authenticity Token是防止 “跨站请求伪造(CSRF或XSRF)攻击”的方法 。

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

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

      <%= token_tag nil %> 

    创造

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

    随地

    最小的攻击例子,将被阻止

    在我的网站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> 

    如果您通过会话cookielogin到您的银行,那么cookies将被发送,并且在您不知情的情况下进行转账。

    这就是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

    我强烈build议您阅读OWASP指南 ,了解这个以及其他安全问题。

    Rails如何发送令牌

    覆盖: Rails:csrf_meta_tag如何工作?

    基本上:

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

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

      uJS也尝试更新过时caching片段中的表单。

    其他预防方法

    • 检查是否存在某些标题,例如X-Requested-With
    • 检查Origin标头的值: https : //security.stackexchange.com/questions/91165/why-is-the-synchronizer-token-pattern-preferred-over-the-origin-header-check-to
    • 重新authentication:再次询问用户密码。 这应该为每个关键操作(银行login和汇款,大多数网站的密码更改),以防万一您的网站得到XSSed。 缺点是用户必须多次input密码,这是令人厌烦的,并增加了键盘logging/肩上冲浪的机会。

    如果您有来自同一客户端的多个并发请求,请注意真实性令牌机制可能会导致争用条件。 在这种情况下,当服务器应该只有一个时,服务器可以生成多个真实性令牌,而接收表单中较早的令牌的客户端因为会话Cookie令牌已被覆盖而在下一个请求上失败。 有一个写在这个问题上,并不是一个完全微不足道的解决scheme: http : //www.paulbutcher.com/2007/05/race-conditions-in-rails-sessions-and-how-to-fix-them/

    方法需要authenticity_token地方

    在幂等方法(如post,put和delete)中需要authenticity_token ,因为Idempotent方法正在影响数据。

    为什么这是必需的

    要防止邪恶的行为。 authenticity_token存储在会话中,无论何时在创build或更新资源的网页上创build表单时,真实性标记都将存储在隐藏字段中,并在服务器上与表单一起发送。 在执行操作之前,用户发送的authenticity_token与会话中存储的authenticity_token交叉检查。 如果authenticity_token是相同的,则进程继续,否则不执行动作。

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

    CSRF

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

    在这里输入图像描述

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

    在这里输入图像描述

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

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

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

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

    哦,那是John Doe。 他在2.5分钟前成功login。 他很好走。

    黑客可能会认为:

    嗯。 一个正常的HTTP请求将无法正常工作,但是如果我能够接触到session_id cookie,那我就是金手镯了。

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

    所以如果一个黑客可以让的GET请求转移到他的帐户钱,他会成功的。 他怎么能欺骗你呢? 与跨站点请求伪造。

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

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

    当用户浏览器遇到该图像标记时,它将对该url发出GET请求。 而且由于请求来自他的浏览器,所以它会发送所有与bank.com相关的cookies。 如果用户最近login了bank.comsession_id cookie将被设置,并且服务器会认为用户要将$ 1,000,000转帐到999999!

    在这里输入图像描述

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

    这还不够。 如果有人把这个图像发布到Facebook,它会出现在你的墙上? 如果它被注入到您访问XSS攻击的网站会怎么样?

    这不是很糟糕。 只有GET请求易受攻击。

    不对。 可以dynamic生成发送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="&#x2713;" /> <input name="authenticity_token" type="hidden" value="J7CBxfHalt49OSHp27hblqK20c9PgwJ108nDHX/8Cts=" /> Form contents </form> 

    特别是,产生以下内容:

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

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

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

    在这里输入图像描述

    跨站点脚本(XSS)攻击 – 这就是如何。 但是,这是另一个不同的日子的一个不同的漏洞。

    什么是authentication_token?

    这是一个由Rails应用程序使用的随机string,用于确保用户从应用程序页面请求或执行操作,而不是从另一个应用程序或站点执行操作。

    为什么authentication_token是必要的?

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

    如何将一个authentication_token添加到表单?

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