为什么不使用会话 ID 作为 XSRF 令牌?
为什么 Play Framework 使用[会话 ID 的签名版本] 作为 跨站请求伪造 (XSRF/CSRF) 预防令牌,而不是会话 ID 本身?
(对于 XSRF 预防令牌,我的意思是必须包含在表单提交中的神奇值,以便 Web 应用程序接受表单。)
如果有窃听者,他/她无论如何都会找到 XSRF 令牌和 SID cookie(? )。
如果存在 XSS 漏洞,那么恶意 JavaScript 代码就可以读取 XSRF 令牌和 SID cookie(?)。
但是:
给定 SID,攻击者无法构建有效的 XSRF 令牌,因为他/她没有在签署 SID 以获得 XSRF 令牌时使用的密钥。 -- 但攻击者怎么可能只掌握 SID,而不掌握 XSRF 令牌呢?是不是太牵强了?
如果 SID 在 HTTP Only cookie 中发送,那么即使攻击者找到了 XSRF 令牌,攻击者也不会拥有 SID,也许攻击者真的需要 SID? ——这是牵强吗?
代码片段:
这里 Play 构造了它的 XSRF 令牌(getId
返回会话 ID): (play/framework/src/play/mvc/Scope.java)
public String getAuthenticityToken() {
return Crypto.sign(getId());
}
这里 Play 检查
protected static void checkAuthenticity() {
if(Scope.Params.current().get("authenticityToken") == null ||
!Scope.Params.current().get("authenticityToken").equals(
Scope.Session.current().getAuthenticityToken())) {
forbidden("Bad authenticity token");
}
}
更新:
Play 更改了生成 XSRF 令牌的方式,现在不再使用 SID,而是签名并使用随机值! (我刚刚将我的 Play Framework Git 存储库克隆从旧的 Play 版本 1.1 更新到新的 1.2。也许我应该在昨天完成此操作,嗯。)
public String getAuthenticityToken() {
if (!data.containsKey(AT_KEY)) {
data.put(AT_KEY, Crypto.sign(UUID.randomUUID().toString()));
}
return data.get(AT_KEY);
}
那么,他们为什么要进行此更改呢?
我找到了提交:
[#669] 再次修复并应用 Flash 和错误
d6e5dc50ea11fa7ef626cbdf01631595cbdda54c
来自问题 #669 :
仅在绝对必要时才创建会话
会话 cookie 是在每次资源请求时创建的。如果会话中确实有数据要存储,则 play 应该只创建会话 cookie。
因此,他们使用随机值,而不是 SID,因为 SID 可能尚未创建。这就是不使用 SID 的衍生品作为 XSRF 令牌的原因。但没有澄清他们过去在使用 SID 时为何对其进行签名/哈希处理。
Why does Play Framework use [a signed version of the session id] as Cross Site Request Forgery (XSRF/CSRF) prevention token, rather than the session ID itself?
(With XSRF prevention token, I mean a magic value that must be included in a form submission, for the webapp to accept the form.)
If there's an eavesdropper s/he'll find both the XSRF token and the SID cookie anyway (?).
If there's an XSS exploit, then the malicious JavaScript code can read both the XSRF token and the SID cookie (?).
However:
An attacker cannot construct a valid XSRF token, given a SID, since s/he doesn't have the secret key used when signing the SID to obtain the XSRF token. -- But how could it happen that an attacker gets hold of only the SID, not the XSRF token? Is that far-fetched?
If the SID is sent in a HTTP Only cookie, then an attacker wouldn't have the SID even if s/he found the XSRF token, and perhaps the attacker really needs the SID? -- Is this far-fetched?
Code snippets:
Here Play constructs it's XSRF token (getId
returns the session ID):
(play/framework/src/play/mvc/Scope.java)
public String getAuthenticityToken() {
return Crypto.sign(getId());
}
Here Play checks that a <form>
has a valid XSRF token:
(play/framework/src/play/mvc/Controller.java)
protected static void checkAuthenticity() {
if(Scope.Params.current().get("authenticityToken") == null ||
!Scope.Params.current().get("authenticityToken").equals(
Scope.Session.current().getAuthenticityToken())) {
forbidden("Bad authenticity token");
}
}
Update:
Play has changed the way it generates XSRF tokens, now the SID is no longer used, instead a random value is signed and used! (I just updated my Play Framework Git repo clone from old Play version 1.1 to new 1.2. Perhaps I should have done this ... yesterday, hmm.)
public String getAuthenticityToken() {
if (!data.containsKey(AT_KEY)) {
data.put(AT_KEY, Crypto.sign(UUID.randomUUID().toString()));
}
return data.get(AT_KEY);
}
Well, then why did they do this change?
I found the commit:
[#669] Fix again and apply for Flash and Errors as well
d6e5dc50ea11fa7ef626cbdf01631595cbdda54c
From issue #669:
create session only when absolute necessary
A session cookie is created on every request of a resource. play should only create a session cookie if there is really data to be stored in the session.
So they're using a random value, not the SID, because the SID might not yet have been created. Well that's a reason not to use a derivative of the SID as XSRF token. But doesn't clarify why they signed/hashed the SID, in the past, when they were using it.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
首先要说的是,您可以重复使用会话 ID 作为 CSRF 令牌,因为它可以很好地保护您免受 CSRF 的侵害,并且不会自动创建任何严重的安全漏洞。然而,出于某种合理的原因,OWASP 曾经明确建议反对它。 (他们现在根本没有解决这个问题。)
反对重新使用会话 ID 作为 CSRF 令牌的论点可以总结如下(要点以粗体显示,下面有理由):
会话 ID 是攻击者获取的安全漏洞通常比攻击者获取的 CSRF 令牌更严重。
攻击者从拥有 CSRF 令牌(假设其他一些安全信息,如会话 ID,尚未被重新用作 CSRF 令牌)中获得的就是执行 CSRF 攻击的能力。这给他们带来了两个巨大的限制,而如果他们真正获得了会话 ID,则不会有这些限制:
攻击者可能比会话 ID 更容易获取 CSRF 令牌
元素的值) >
提交中时的情况),那么 Web 服务器访问日志通常会记录 GET 请求上的 CSRF 令牌(因为它是 URL 的一部分)。因此,设法查看访问日志的攻击者将能够获取许多 CSRF 令牌。
但是,如果您将会话 ID 重新用作 CSRF 令牌,则任何允许他们获取 CSRF 令牌的攻击也会自动为他们提供会话 ID。
因此你不应该重复使用CSRF令牌作为会话ID,因为它使会话 ID 更容易受到攻击。
老实说,我认为以上所有内容更多地是一种理论问题,而不是实际问题。论证的弱点是第 2 点;我能想到的唯一可用于获取 CSRF 令牌但不能用于获取会话 cookie 的实际漏洞仍然是非常严重的漏洞。如果您的网站上存在 XSS 漏洞,或者攻击者可以访问您该死的服务器日志,那么您很可能就彻底完蛋了。在我去过的大多数图书馆和网吧中,工作人员都不太懂安全,很容易在不被发现的情况下安装键盘记录器并获取密码 - 攻击者无需前往图书馆和网吧。等待人们使用机器,然后抓取浏览器缓存的内容。
然而,除非您的环境在某种程度上使得在随机会话 ID 旁边存储额外的 CSRF 随机令牌变得困难,否则为什么不这样做呢?
The first thing to say is that you can reuse the session ID as the CSRF token, insofar as it will protect you fine against CSRF and does not automatically create any serious security holes. However, for somewhat sound reasons, OWASP used to explicitly recommend against it. (They now don't address the question at all.)
The argument against reusing the session ID as the CSRF token can be summarized as follows (key points in bold, with justification beneath):
The session ID being acquired by an attacker is generally a more serious security breach than the CSRF token being acquired by an attacker.
All that an attacker gains from having the CSRF token (assuming that some other secure piece of information, like the session ID, hasn't been reused as the CSRF token) is the ability to perform CSRF attacks. This gives them two huge limitations that they wouldn't have if they actually acquired a session ID:
The CSRF token is potentially easier for an attacker to acquire than the session ID
<input>
element in a<form>
. Session cookies, on the other hand, can be kept secret even in the face of a successful XSS attack using the HttpOnly flag, demanding more up-front work from an attacker to usefully exploit an XSS vulnerability.<form>
submits), then web server access logs will generally log the CSRF token on GET requests (as it's part of the URL). Thus an attacker who manages to view the access log would be able to acquire many CSRF tokens.But if you're reusing the session ID as the CSRF token, then any attack that permits them to acquire the CSRF token automatically gives them the session ID as well.
Therefore you should not reuse the CSRF token as the session ID, since it makes the session ID more vulnerable.
To be honest, I kind of regard everything above as more of a theoretical concern than a practical one. The weak point in the argument is point 2; the only realistic vulnerabilities I can think of that could be used for acquiring CSRF tokens but not for acquiring session cookies are still really serious vulnerabilities. If you have an XSS hole on your site, or an attacker has access to your freaking server logs, chances are you're totally fucked anyway. And in most libraries and internet cafes I've been to, the staff were not security-savvy and it'd be pretty easy to install a keylogger undetected and just harvest passwords - there'd be no need for an attacker to go to the effort of waiting for people to use the machine and then ripping the contents of their browser cache.
However, unless your circumstances somehow make it difficult to store an additional random token for CSRF alongside the random session ID, why not just do it anyway for whatever modest security benefit it gives you?
纯粹的 CSRF 攻击无法访问浏览器的 cookie,因此当您说“窃听者”时,只有他们嗅探数据包(即没有 SSL、公共 wifi)时才能实现这一点。
根据 Play 框架的配置(我不熟悉它,因此将此作为一般 Web 应用程序建议),会话和身份验证 cookie 几乎肯定会被标记为 HttpOnly 因此无法通过 XSS 从客户端读取它们。
最终,使用同步器令牌模式来防范 XSRF 的想法是使用唯一的值(最好是加密强度高的值),该值只有服务器和客户端知道并且对该会话是唯一的。基于这个目标,Play Framework 似乎做得很好。
A pure CSRF attack doesn't have access to the browser's cookies so when you say "eavesdropper", that's only going to be achievable if they're sniffing packets (i.e. no SSL, public wifi).
Depending on the configuration of the Play Framework (I'm not familiar with it so take this as general web app advice), the session and authentication cookies will almost certainly be flagged as HttpOnly so they they're unable to be read from the client via XSS.
Ultimately, the idea of using the synchroniser token pattern to protect against XSRF is to use a unique value (preferably cryptographically strong), known only to the server and the client and unique to that session. Based on this goal, Play Framework seems to do just fine.
也许 Play Framework 不希望 HTML 中包含 SID。最终用户 Bob 可能会下载一个网页,如果该网页中有
(不使用 SID 的另一个次要原因:正如我在更新中提到的,SID 可能根本不可用。也许它是尽可能晚生成的,以节省 CPU 资源。)
Perhaps Play Framework doesn't want the SID in the HTML. An end user, Bob, might download a Web page, and if there's a
<form>
in that Web page, the SID would be included in the downloaded HTML (if the SID itself is used as XSRF token). If Bob then emails his downloaded page to Mallory, then Mallory would find the SID and could impersonate Bob!?(Another minor reason not to use the SID: As I mentioned in my update, the SID might simply not be available. Perhaps it's generated as late as possible, to save CPU resources.)