实施 CSRF 免费 JWT 身份验证的安全方法

Posted

技术标签:

【中文标题】实施 CSRF 免费 JWT 身份验证的安全方法【英文标题】:Secure ways to implement CSRF free JWT authentification 【发布时间】:2017-01-03 02:50:09 【问题描述】:

我刚刚了解了 JWT 进行身份验证。将 JWT 令牌存储在 localStorage/sessionStorage 中会暴露给 XSS。将其存储在 cookie 中容易受到 CSRF 的攻击。我一直在研究这个并且我想到了这个解决方案,但我不确定它有多安全以及我是否应该在生产中使用它。

解决方案是从服务器获取 JWT 令牌,将其存储在 cookie 中。生成一个 CSRF 令牌(将存储在 JWT 中),该令牌将与每个 html 页面一起发送,或者在隐藏的 HTML 字段中,或者作为全局 JS 变量 ()。该 CSRF 令牌将与使用 JS/AJAX 的每个请求一起发送。这样我们就可以先排除 CSRF,再验证 JWT 令牌。

我不确定是否应该在每个加载的页面上发送一个新令牌,或者每个会话应该持有一个令牌。第一种情况意味着只有最后加载的页面/表单才能提交(如果用户打开了其他页面的多个选项卡,这可能会出现问题)。

这是一个可用于生产的安全解决方案吗?

另外,还有哪些其他可行的解决方案可以达到相同的目标?

【问题讨论】:

JWT 与 API 身份验证有关,我认为不需要 CSRF,因为您已经使用 JWT 对您的请求进行了身份验证,或者您可以阻止跨源请求并将其限制在某些域中 我认为您没有阅读我的帖子,但无论如何,请参阅此处为什么在 cookie 中存储身份验证令牌时需要 CSRF 保护stormpath.com/blog/… 【参考方案1】:

这是我对这个问题的理解。

JWT 令牌 - 存储在 httponly/secure cookie 中 - 这确保任何 javascript 都无法访问它 CSRF 令牌 - 由 JS 存储 - 登录时,将声明附加为标题(包括您的 CSRF 值)

当 JS 获取到 header 时,它可以将声明存储在 cookie、localstorage 或 session 存储中。因为没有 JS 可以访问 cookie 中的 JWT 令牌,所以通过将 CSRF 附加到每个请求并确保它与 JWT 中的内容匹配,您可以确保是您的 JS 发送请求并且是您的后端发出了令牌。

隐藏字段不是一个很好的解决方案,因为您必须将它添加到每个请求中。根据您使用的框架,您应该能够将 CSRF 令牌加载到负责在刷新页面时将令牌作为每个请求的标头发送的任何内容中。将所有声明保存在本地存储或 cookie 中的好处是您可以保留用户的前端状态。拥有 exp 声明意味着您可以判断用户何时在安全 jwt 令牌 cookie 中不再具有有效令牌,并且您可以重定向到登录页面。

sessionstorage - 仅特定于选项卡 localstorage - 跨选项卡特定于域 cookie - 特定于跨标签的域

向 AJAX 请求添加 CSFR:

    $.ajax(
        type:"POST",
        beforeSend: function (request)
        
            request.setRequestHeader("CSRF-TOKEN", csrfToken);
        ,
        url: "entities",
        data: "json=" + escape(JSON.stringify(createRequestObject)),
        processData: false,
        success: function(msg) 
            $("#results").append("The result =" + StringifyPretty(msg));
        
);

即使您没有使用 Angular,这仍然适用。您应该将令牌作为标头附加到 JS 中,并且可以从 cookie 中获取该值。

来自Angular JS Documentation

在执行 XHR 请求时,$http 服务从 cookie 中读取一个令牌(默认为 XSRF-TOKEN)并将其设置为 HTTP 标头(X-XSRF-TOKEN)。由于只有在您的域上运行的 JavaScript 才能读取 cookie,因此您的服务器可以确信 XHR 来自在您的域上运行的 JavaScript。

【讨论】:

感谢您的回复。您误解了几点:“CSRF Token - 由 JS 存储”,它不会由 JS 存储。它将由服务器在每个 HTML 页面中发送,作为隐藏字段或作为全局 JS 变量(echo '<script>var xsrf = \'' . $token . '\';</script>'; 另外,除了 jQuery/JS 客户端和 php 服务器端之外,我没有使用任何其他东西。我的理解是使用 AJAX 在常规 POST 变量中发回 csrf 令牌。我怎样才能在标头中发送它? 如果您将 CSRF 放在 cookie 中,您不必将其添加到每个请求中以使其对 PHP 可用,它将自动包含在内。如果您不想使用 cookie,可以在 AJAX 请求中添加标头 - 请参阅编辑后的帖子。 感谢标题解决方案。但是将 CSRF 令牌存储在 cookie 中实际上不违背 CSRF 令牌的目的吗? 你是对的,你不应该只相信 cookie。 Javascript 可以从 cookie 中获取值,但需要将其作为标头附加。查看我的编辑。

以上是关于实施 CSRF 免费 JWT 身份验证的安全方法的主要内容,如果未能解决你的问题,请参考以下文章

JWT 在未经身份验证的端点上进行 CSRF 保护?

安全的单站点 JWT 实施

JWT 访问令牌:矛盾?

带有 jwt 身份验证的 django rest api 要求 csrf 令牌

这个 JWT 实现是不是可以防止 XSS 和 CSRF 攻击,同时仍然允许我访问有效负载?

实施 Identity 2.1 + OWIN OAuth JWT 不记名令牌时如何从 Web API 控制器端点进行身份验证