【问题标题】:Why not use session ID as XSRF token?为什么不使用会话 ID 作为 XSRF 令牌?
【发布时间】: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 (?)。

但是:

  1. 在给定 SID 的情况下,攻击者无法构造有效的 XSRF 令牌,因为他/她没有在签署 SID 以获取 XSRF 令牌时使用的密钥。 -- 但是攻击者怎么可能只获得 SID 而不是 XSRF 令牌呢?这有点牵强吗?

  2. 如果 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


    【解决方案1】:

    首先要说的是,您可以重用会话 ID 作为 CSRF 令牌,只要它可以保护您免受 CSRF 的影响,并且不会自动产生任何严重的安全漏洞。但是,出于某种合理的原因,OWASP used to explicitly recommend against it。 (他们现在根本不解决这个问题。)

    反对重用会话 ID 作为 CSRF 令牌的论点可以总结如下(粗体表示关键点,下方有理由):

    1. 攻击者获取的会话 ID 通常比攻击者获取的 CSRF 令牌更严重。

      攻击者从拥有 CSRF 令牌(假设其他一些安全信息,如会话 ID,没有被用作 CSRF 令牌)中获得的所有好处就是执行 CSRF 攻击的能力。这给了他们两个巨大的限制,如果他们真的获得了会话 ID,他们就不会遇到:

      • 他们仍然需要使用相应的会话令牌将用户引诱到攻击页面(或让他们阅读攻击电子邮件,或在 iframe 中查看攻击广告等)以任何方式利用 CSRF 令牌.使用会话 ID,他们只需将其放入浏览器中,然后像使用该用户一样使用网站。
      • 虽然他们可以使用用户的凭据发送请求,但Same Origin Policy 仍会阻止他们查看对这些请求的响应。这可能(也可能不会,取决于您要保护的 API 的结构和攻击者的聪明才智)实际上意味着虽然攻击者可以代表用户执行操作,但他们不能获取用户有权查看的敏感信息。 (您更关心哪一个取决于上下文 - 假设攻击者倾向于获取您的银行帐户的内容,而不是仅仅知道有多少,但他们也宁愿知道您的病史而不是破坏它。)

    2. CSRF 令牌可能比会话 ID 更容易被攻击者获取

      • XSS 攻击很可能允许攻击者获取 CSRF 令牌,因为通常的做法是将其烘焙到 DOM 中(例如,作为 <form> 中的 <input> 元素的值。会话 cookie,另一方面手,可以是kept secret even in the face of a successful XSS attack using the HttpOnly flag,要求攻击者进行更多前期工作以有效利用 XSS 漏洞。
      • 如果 CSRF 令牌作为请求参数而不是自定义 HTTP 标头被发送回服务器(保证在普通 HTML <form> 提交中包含它时就是这种情况),那么 Web 服务器访问日志通常会记录GET 请求上的 CSRF 令牌(因为它是 URL 的一部分)。因此,设法查看访问日志的攻击者将能够获得许多 CSRF 令牌。
      • 嵌入 CSRF 令牌的页面或脚本可能会缓存在用户的浏览器中,从而允许攻击者从缓存中检索它们(例如,在用户使用图书馆或 Internet 中的公共机器之后,这可能是相关的咖啡馆,然后要么清除他们的 cookie 而不是他们的缓存,要么使用“注销”按钮从浏览器中删除他们的会话 cookie 而不会在服务器端使其无效)。

    3. 但是,如果您将会话 ID 用作 CSRF 令牌,那么任何允许他们获取 CSRF 令牌的攻击也会自动为他们提供会话 ID。

    4. 因此,您不应重复使用 CSRF 令牌作为会话 ID,因为这会使会话 ID 更容易受到攻击。

    老实说,我将以上所有内容更多地视为理论问题,而不是实际问题。论点的弱点是第 2 点;我能想到的唯一可以用于获取 CSRF 令牌但不能用于获取会话 cookie 的实际漏洞仍然是非常严重的漏洞。如果您的网站上有一个 XSS 漏洞,或者攻击者可以访问您该死的服务器日志,那么您很可能已经完蛋了。在我去过的大多数图书馆和网吧中,工作人员并不精通安全,安装键盘记录器而不被发现并收集密码很容易——攻击者不需要去努力等待人们使用机器,然后翻录浏览器缓存的内容。

    但是,除非您的情况以某种方式难以在随机会话 ID 旁边为 CSRF 存储一个额外的随机令牌,否则为什么不为它给您带来的任何适度的安全利益而这样做呢?

    【讨论】:

    • 也许只使用 session_id() 的提取,这样你就不会泄露完整的 ID:$CSRFtoken = substr(session_id(), 4, 6);
    【解决方案2】:

    纯粹的 CSRF 攻击无法访问浏览器的 cookie,因此当您说“窃听者”时,只有在他们嗅探数据包(即没有 SSL、公共 wifi)时才能实现。

    根据 Play Framework 的配置(我不熟悉它,因此将其作为一般 Web 应用程序建议),会话和身份验证 cookie 几乎肯定会被标记为 HttpOnly,因此他们无法通过 XSS 从客户端读取。

    最终,使用同步器令牌模式来防止 XSRF 的想法是使用唯一值(最好是加密强),只有服务器和客户端知道并且该会话唯一。基于这个目标,Play Framework 似乎做得很好。

    【讨论】:

    • 在我看来,Play 的会话 ID 符合您在上一段中提到的标准。那么他们为什么不直接将其用作 XSRF 令牌。
    • (( 是的,数据包嗅探,这就是我所说的窃听。HttpOnly:它似乎默认情况下实际上是禁用的(在 play/framework/src/play/mvc/Scope.java 的顶部) .我认为Play在会话cookie中存储了其他东西(除了会话ID),浏览器可能需要访问其他东西(取决于应用程序)。但是cookie是签名的;它不能被篡改,只有阅读。))
    • 啊,是的,我明白你在说什么,区别在于签名。显然,签名使 Play 能够验证令牌的完整性,因为它没有被流氓进程重建。关于您的更新,不使用会话 ID 意味着令牌中有更多的随机性,因此如果会话 ID 被暴露,它就不能用于伪造令牌。当然,如果它已签名,那么无论如何它不可能是,但与所有安全性一样,它是关于在防御之上分层防御。
    【解决方案3】:

    也许 Play Framework 不希望 HTML 中的 SID。最终用户 Bob 可能会下载一个网页,如果该网页中有一个<form>,则 SID 将包含在下载的 HTML 中(如果 SID 本身用作 XSRF 令牌)。如果 Bob 然后将他下载的页面通过电子邮件发送给 Mallory,那么 Mallory 会找到 SID 并可以冒充 Bob!?

    (另一个不使用 SID 的次要原因:正如我在更新中提到的,SID 可能根本不可用。也许它是尽可能晚地生成的,以节省 CPU 资源。)

    【讨论】:

    • OWASP csrf 备忘单 (owasp.org/index.php/…) 很好地解释了这一点:“虽然这种方法可以有效降低跨站点请求伪造的风险,但在 HTTP 参数中包含经过身份验证的会话标识符可能会增加会话劫持的总体风险。”
    猜你喜欢
    • 1970-01-01
    • 2019-04-14
    • 2017-10-22
    • 2022-12-18
    • 1970-01-01
    • 2021-12-25
    • 1970-01-01
    • 2013-07-18
    • 1970-01-01
    相关资源
    最近更新 更多