【发布时间】:2011-10-21 12:53:05
【问题描述】:
为什么Play Framework 使用[会话 ID 的签名版本] 作为Cross Site Request Forgery (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? -- 这有点牵强吗?
代码 sn-ps:
在这里 Play 构造它的 XSRF 令牌(getId 返回会话 ID):
(play/framework/src/play/mvc/Scope.java)
public String getAuthenticityToken() {
return Crypto.sign(getId());
}
Here Play 检查 <form> 是否具有有效的 XSRF 令牌:
(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");
}
}
更新:
Play 更改了生成 XSRF 令牌的方式,现在不再使用 SID,而是对随机值进行签名和使用! (我刚刚将我的 Play Framework Git repo 克隆从旧 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 和 Errors
d6e5dc50ea11fa7ef626cbdf01631595cbdda54c
来自问题#669:
仅在绝对必要时创建会话
每次请求资源时都会创建会话 cookie。只有当会话中确实有数据要存储时,play 才应该创建会话 cookie。
所以他们使用的是随机值,而不是 SID,因为 SID 可能尚未创建。这就是不使用 SID 的派生词作为 XSRF 令牌的原因。但没有说明为什么他们在过去使用 SID 时对 SID 进行签名/散列。
【问题讨论】:
标签: security cookies playframework csrf