我是否必须将令牌存储在cookie或本地存储或会话中?

时间:2019-01-18 16:46:53

标签: javascript jwt

对不起,也许这是许多其他人提出的问题,但是我真的很困惑,因为某些参考文献有许多不同的方式。

Iam使用React SPA,Express,Express-session,Passport,JWT

所以我对Cookie,Session和JWT / Passport感到困惑。

许多网站使用cookie来存储购物车令牌。到目前为止,我已经基于会话ID存储了购物车数据,而未添加任何Cookie。

  

因此,当用户访问我的网站时,我会将其与他们的网站进行匹配   req.sessionID,然后在数据库中检索购物车和用户会话之类的数据。

所以我的问题是我是否需要存储cookie?因为我可以通过req.sessionID访问它以获得所需的数据。

第二个

我已经使用passport-google-oauth20进行了身份验证。成功登录后,数据将保存到会话中。并将其发送给客户端,我必须通过URL查询?token='sdsaxas'发送。

  

在这种情况下,我有很多意见分歧。有人保存了它   到本地存储中,然后有人通过使用JWT将其转换为令牌将其保存到Cookie中。

 jwt.sign(
        payload,
        keys.jwt.secretOrPrivateKey, 
        {
            expiresIn:keys.jwt.expiresIn // < i dont know what is this expired for cookies or localstorage ?
        }, (err, token) => {

            res.redirect(keys.origin.url + "?token=" + token);
        });

结论是,所有内容都与会话相关,因此我可以使用sessionID来完成所有操作?没有Cookie或本地存储

因为我使用React SPA,所以只能执行一次或每页刷新并检索数据,然后将其保存到redux中

4 个答案:

答案 0 :(得分:8)

LocalStorage / SessionStorage容易受到XXS攻击。可以通过JavaScript读取访问令牌。

带有httpOnly,secure和SameSite = strict标志的Cookie更加安全。 JavaScript无法访问访问令牌及其有效载荷。

但是,如果存在XSS漏洞,则攻击者无论如何都可以以经过身份验证的用户身份发送请求,因为恶意脚本不需要读取cookie值,cookie可以由浏览器自动发送。

这句话是正确的,但风险有所不同。

使用cookie时,访问令牌仍处于隐藏状态,攻击者只能进行“现场”攻击。注入到Web应用程序中的恶意脚本可能会受到限制,或者更改/注入更多脚本可能不是很容易。攻击者可能首先需要将用户或Web应用程序作为攻击目标。这些条件限制了攻击的规模。

使用localStorage,攻击者可以读取访问令牌并远程进行攻击。他们甚至可以与其他攻击者共享令牌,并造成更严重的破坏。如果攻击者设法将恶意脚本注入CDN(例如Google字体API),则攻击者将能够从所有使用包含CDN的网站上窃取访问令牌和URL,并轻松找到新的目标。使用localStorage的网站更容易成为目标。

为了争辩

笔测试可能会将您对敏感数据的localStorage标记为危险。

如果JavaScript可以通过XSS攻击从localStorage读取访问令牌,为什么您仍然认为每个人仍然推荐httpOnly标志。

OWASP的推荐

请勿将会话标识符存储在本地存储中,因为JavaScript始终可以访问数据。 Cookies可以使用httpOnly标志减轻这种风险。

https://medium.com/@coolgk/localstorage-vs-cookie-for-jwt-access-token-war-in-short-943fb23239ca

答案 1 :(得分:7)

此答案基于无状态方法,因此没有讨论传统的会话管理

您问了两个完全不同的问题:

  1. 购物车-与业务功能更相关
  2. OAuth 2和JWT-与安全性和身份验证有关

作为电子商务网站的用户,我希望在上下班途中从移动设备添加到购物车中的任何物品在我从PC登录到网站后都可以在购物车中找到。到达家。因此,购物车数据应保存在后端数据库中并链接到我的用户帐户。

在使用OAuth 2.0进行身份验证时,JWT访问令牌和/或刷新令牌需要存储在客户端设备中的某个位置,这样,一旦用户通过提供登录凭据对自己进行身份验证,则无需提供他的凭据再次浏览网站。在这种情况下,浏览器本地存储,会话存储和cookie都是有效选项。但是,请注意,此处cookie没有链接到服务器端的任何会话。换句话说,该cookie不存储任何会话ID。 Cookie只是用作访问令牌的存储,该访问令牌随每个http请求传递到服务器,然后服务器使用数字签名验证令牌,以确保它不会被篡改且不会过期。

尽管访问和/或刷新令牌的所有三种存储选项都很流行,但以正确的方式使用cookie似乎是最安全的选项。

为了更好地理解这一点,建议您阅读thisthis以及OAuth 2.0规范。

于2019年2月16日更新

我之前说过,cookie似乎是最安全的选择。我想在这里进一步阐明这一点。

我认为浏览器localStoragesessionStorage不能提供足够的安全性来存储身份验证令牌的原因如下:

  1. 如果发生XSS,则恶意脚本可以轻松地从那里读取令牌并将其发送到远程服务器。从此以后,远程服务器或攻击者将不会冒充受害用户。

  2. localStoragesessionStorage不在子域之间共享。因此,如果我们有两个运行在不同子域上的SPA,则我们将无法获得SSO功能,因为一个应用程序存储的令牌对于组织内的另一个应用程序将不可用。有一些使用iframe的解决方案,但它们看起来更像是变通方法,而不是一个好的解决方案。而且,当使用响应标头X-Frame-Options来避免使用iframe进行点击劫持攻击时,使用iframe的任何解决方案都是毫无疑问的。

但是,可以通过使用指纹(如OWASP JWT Cheat Sheet中所述)来减轻这些风险,而指纹又需要使用cookie。

指纹的想法是,生成一个加密强度高的随机字节串。然后,原始字符串的Base64字符串将存储在名称前缀为HttpOnly的{​​{1}},SecureSameSite cookie中。应根据业务需求使用“域”和“路径”属性的正确值。字符串的SHA256哈希也将在JWT声明中传递。因此,即使XSS攻击将JWT访问令牌发送给攻击者控制的远程服务器,它也无法发送cookie中的原始字符串,结果服务器可以基于cookie的缺失拒绝请求。 XSS脚本无法读取Cookie __Secure-

因此,即使我们使用HttpOnlylocalStorage,我们也必须使用cookie来确保其安全。最重要的是,我们添加了如上所述的子域限制。

现在,有关使用cookie存储JWT的唯一问题是CSRF攻击。由于我们使用sessionStorage cookie,因此CSRF得以缓解,因为不可能进行跨站点请求(AJAX或仅通过超链接)。如果该网站在任何旧的浏览器或不支持SameSite cookie的其他不太流行的浏览器中使用,我们仍然可以通过使用具有加密强随机值的CSRF cookie来缓解CSRF,以便每个AJAX请求都读取cookie值,然后将cookie值添加到自定义HTTP标头中(不应该进行任何状态修改的GET和HEAD请求除外)。由于CSRF由于相同的原始策略而无法读取任何内容,并且基于利用不安全的HTTP方法(例如POST,PUT和DELETE),因此该CSRF cookie将减轻CSRF的风险。所有现代SPA框架都使用这种使用CSRF cookie的方法。提到了Angular方法here

此外,由于cookie为SameSitehttpOnly,因此XSS脚本无法读取它。因此,XSS也得到缓解。

值得一提的是,通过使用适当的Secured响应标头可以进一步减轻XSS和脚本注入的压力。

答案 2 :(得分:0)

HTTP is a stateless protocol.请阅读该答案以获取更多详细信息,但从本质上讲,这意味着HTTP服务器(例如Web服务器)在一个请求的生存期内不会存储有关客户端的任何信息。对于Web应用程序来说这是一个问题,因为这意味着您不记得哪个用户登录了。

发明了Cookie作为解决方案。 Cookies是客户端和服务器根据每个请求来回发送的文本数据。它们使客户端和服务器在每次通信时记住他们所记得的内容,从而使您能够有效地维护应用程序状态数据。

从根本上讲,这意味着没有Cookie的会话将无法进行必须是一个cookie,它至少存储 个会话ID,以便您可以通过查找会话来确定当前哪个用户登录了您的应用程序。这是express-session的作用:主要的session方法的the documentation明确指出,会话ID存储在cookie中。

  

所以我的问题是我是否需要存储cookie?因为我可以通过req.sessionID访问它以获得所需的数据。

不需要存储cookie。快速会话将为您做到这一点。整个应用程序确实需要存储cookie;没有它,您将没有req.sessionID来查找。

答案 3 :(得分:-1)

根据我的经验,只将令牌存储在localStorage中。

localStorage

它最多可以存储5 MB的信息。您无需征得用户许可即可将令牌存储在localStorage中。

唯一要担心的是目标设备是否支持localStorage api。

在这里检查:https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage

它得到广泛支持。但是根据我的经验,如果您有一个ios应用程序,并且此应用程序中有一个html页面,要求用户存储令牌(也称为webview),则无法识别localStorage api并抛出错误。

解决方案很简单,我只是将令牌放在url中,然后每次都将其传输。在webview中,URL不可见。

Cookie:

在本地存储信息是一种非常古老的风格。 Cookie中的存储空间相对较小,您需要征得用户的许可才能将令牌存储在Cookie中。

Cookie随每个请求一起发送,因此它们可能会降低性能(尤其是对于移动数据连接而言)。用于客户端存储的现代API是Web存储API(localStorage和sessionStorage)和IndexedDB。  (https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies

请勿将令牌存储在sessionStorage或redux中。

如果关闭选项卡,则存储在sessionStorage中的数据将丢失。如果用户不小心关闭了选项卡,则令牌将丢失并且服务器将无法识别当前用户。 存储在redux中的令牌与存储在其他js文件中的令牌没有区别。 redux存储只是另一个js文件。每次刷新页面时,存储在redux中的信息都会丢失。

总结

在大多数情况下,如果使用现代风格,令牌将存储在localStorage中。在某些情况下,您可以将令牌存储在cookie中,有时可能放在url中。但是永远不要存储在会话中。

希望有帮助。