Answers:
怎么了
当用户查看表单以创建,更新或销毁资源时,Rails应用程序将创建一个random authenticity_token
,将该令牌存储在会话中,并将其放置在表单的隐藏字段中。当用户提交表单时,Rails查找authenticity_token
,将其与会话中存储的表单进行比较,如果匹配,则允许继续请求。
为什么会发生
由于真实性令牌存储在会话中,因此客户端无法知道其值。这样可以防止人们将表单提交到Rails应用程序而无需在该应用程序内部查看表单。假设您正在使用服务A,并且已登录该服务,那么一切正常。现在,假设您去使用服务B,并且看到了自己喜欢的图片,然后按一下该图片以查看更大的尺寸。现在,如果服务B上存在一些恶意代码,它可能会向服务A(您已登录)发送请求,并通过向发送请求,要求删除您的帐户http://serviceA.com/close_account
。这就是所谓的CSRF(跨站请求伪造)。
如果服务A使用的是真实性令牌,则此攻击向量将不再适用,因为来自服务B的请求将不包含正确的真实性令牌,并且将不允许继续。
API文档介绍了有关元标记的详细信息:
通过
protect_from_forgery
方法打开CSRF保护,该方法检查令牌并在会话与预期不符时重置会话。默认情况下,将为新的Rails应用程序生成对此方法的调用。authenticity_token
默认情况下,token参数被命名。必须通过包含csrf_meta_tags
在HTML头中,将此标记的名称和值添加到呈现表单的每个布局中。
笔记
请记住,Rails仅验证而不是幂等方法(POST,PUT / PATCH和DELETE)。不检查GET请求的真实性令牌。为什么?因为GET请求的HTTP规范状态是幂等,应该不是创建,修改,或在服务器破坏资源,并且请求应该是幂等(如果您运行相同的命令多次,你应该每次都得到相同的结果)。
而且,如开始定义的那样,实际的实现要复杂一些,以确保更好的安全性。Rails不会为每种表单发布相同的存储令牌。每次都不会生成并存储不同的令牌。它在会话中生成并存储加密哈希,并在每次呈现页面时发出新的加密令牌,这些令牌可以与存储的令牌进行匹配。请参阅request_forgery_protection.rb。
经验教训
使用authenticity_token
来保护您不幂等的方法(POST,PUT / PATCH和DELETE)。另外,请确保不允许任何可能修改服务器资源的GET请求。
编辑:通过@erturne检查有关GET请求是幂等的评论。他用比我在这里做的更好的方式解释了这一点。
真实性令牌是对跨站请求伪造(CSRF)的对策。您问什么是CSRF?
这是一种攻击者甚至不知道会话令牌就可以劫持会话的方式。
场景:
CSRF解决方案:
可以防止的最少攻击示例: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如何发送令牌
基本上:
HTML帮助程序(例如form_tag
,如果不是GET表单)会为表单添加一个隐藏字段
jquery-ujs自动处理AJAX,jquery-ujs从meta
添加到标头中的元素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)。要了解真实性令牌,您必须首先了解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
cookie。
因此,如果黑客可以诱使您提出将钱转入他的帐户的GET请求,那么他将成功。他怎么会骗你呢?与跨站点请求伪造。
实际上,这很简单。黑客可能只是让您访问他的网站。在他的网站上,他可以具有以下图像标签:
<img src="http://bank.com/transfer?amount=$1000000&account-to=999999">
当用户浏览器遇到该图片标签时,它将向该网址发出GET请求。由于该请求来自他的浏览器,因此它将与一起发送与之关联的所有cookie 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或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">
任何地方
XSS
在登录页面上进行此操作,不是出于恶意目的,而是使用预先填写的用户名创建一个新会话。现在我知道我可以使用了value="token_value"
。
请注意,如果您有来自同一客户端的多个并发请求,那么身份令牌机制可能会导致争用情况。在这种情况下,您的服务器可以在只应有一个真实性令牌的情况下生成多个真实性令牌,并且由于会话cookie令牌已被覆盖,因此以下一个请求形式接收较早令牌的客户端将失败。有关此问题的文章,这里有一个并非完全简单的解决方案:http : //www.paulbutcher.com/2007/05/race-conditions-in-rails-sessions-and-how-to-fix-them/
方法authenticity_token
需要的地方
authenticity_token
对于幂等方法(例如post,put和delete)是必需的,因为幂等方法会影响数据。
为什么需要
必须防止邪恶的行为。authenticity_token存储在会话中,每当在网页上创建用于创建或更新资源的表单时,就将真实性令牌存储在隐藏字段中,并将其与服务器上的表单一起发送。在执行操作之前,将用户发送的authenticity_token与
authenticity_token
存储在会话中的内容进行交叉检查。如果authenticity_token
相同,则过程继续,否则不执行操作。
什么是authentication_token?
这是Rails应用程序使用的随机字符串,用于确保用户正在从应用程序页面而不是从另一个应用程序或站点请求或执行操作。
为什么需要authentication_token?
为了保护您的应用程序或站点免受跨站点伪造。
如何在表格中添加一个authentication_token?
如果使用form_for标记生成表单,则会自动添加authentication_token,否则可以使用<%= csrf_meta_tag %>
。