我正在使用React SPA、Express、Express-session、Passport和JWT。我对存储令牌的一些不同的客户端存储选项感到困惑:Cookie、会话和JWT / Passport。
令牌是否必须存储在cookie中,即使我可以将其存储在req.sessionID
中?
许多网站使用cookies来存储购物车令牌,到目前为止,我已经根据会话ID存储了购物车数据,没有添加任何cookies。
因此,当用户访问我的网站时,我会将其与他们的req.sessionID
进行匹配,然后检索数据库中的数据,如购物车和用户会话。
我需要存储cookies吗?我可以通过req.sessionID
访问它来获取所需的数据。
第二个
我使用passport-google-oauth20
进行了身份验证。成功登录后,数据保存到会话中。要将其发送到客户端,我必须通过URL查询?token='sdsaxas'
发送它。
在这种情况下,我得到了很多不同意见。2有人将其保存到本地存储器,有人通过使用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条答案
按热度按时间62o28rlo1#
此答案基于无状态方法,因此不涉及传统会话管理
你问了两个完全不同的问题:
1.购物车-与业务功能关系更密切
作为一个电子商务网站的用户,我希望我在上班途中从移动的设备添加到购物车中的任何物品,在我到家后从PC登录网站时都能在购物车中找到。因此,购物车数据应保存在后端数据库中,并链接到我的用户帐户。
当使用OAuth 2.0进行身份验证时,JWT访问令牌和/或刷新令牌需要存储在客户端设备的某个地方,这样,一旦用户通过提供登录凭据进行身份验证,就不需要再次提供凭据来浏览网站。在这种情况下,浏览器本地存储、会话存储和cookie都是有效的选项。但是,请注意,这里的cookie没有链接到服务器端的任何会话。换句话说,cookie没有存储任何会话ID。cookie仅用作存储访问令牌,该令牌随每个HTTP请求传递到服务器,然后服务器使用数字签名验证令牌,以确保它没有被篡改,也没有过期。
虽然访问和/或刷新令牌的所有三种存储选项都很受欢迎,但如果使用正确,cookie似乎是最安全的选项。
为了更好地理解这一点,我建议您阅读this和this沿着OAuth 2.0规范。
2019年2月16日更新
我前面说过cookie似乎是最安全的选择,我想在这里进一步澄清这一点。
我认为浏览器
localStorage
和sessionStorage
没有为存储auth令牌提供足够的安全性的原因如下:1.如果发生XSS,恶意脚本可以轻松地从那里读取令牌并将其发送到远程服务器。这样,远程服务器或攻击者就可以轻松地模拟受害用户。
localStorage
和sessionStorage
不在子域之间共享。因此,如果我们有两个SPA在不同的子域上运行,我们将无法获得SSO功能,因为一个应用程序存储的令牌对组织内的另一个应用程序不可用。有一些解决方案使用iframe
、但那些看起来更像是变通方法,而不是好的解决方案。而且当响应报头X-Frame-Options
被用来避免iframe
的点击劫持攻击时,任何使用iframe
的解决方案都是毫无疑问的。然而,这些风险可以通过使用指纹来降低(如OWASP JWT Cheat Sheet中所述),而指纹又需要Cookie。
指纹的思想是生成一个加密性很强的随机字节串,原始字符串的Base64字符串将被存储在
HttpOnly
、Secure
名称前缀为__Secure-
的SameSite
Cookie。应根据业务要求使用正确的Domain和Path属性值。字符串的SHA 256散列也将在JWT的声明中传递。因此,即使XSS攻击将JWT访问令牌发送到攻击者控制的远程服务器,它不能发送cookie中的原始字符串,结果服务器可以基于cookie的不存在而拒绝请求。XSS脚本无法读取HttpOnly
的cookie。因此,即使我们使用
localStorage
和sessionStorage
,我们也必须使用cookie来保护它。在此基础上,我们添加了上述的子域限制。现在,使用cookie存储JWT的唯一问题是CSRF攻击。由于我们使用
SameSite
cookie,CSRF被缓解,因为跨站点请求( AJAX 或只是通过超链接)是不可能的。如果网站是在任何旧的浏览器或其他一些不那么流行的浏览器,不支持SameSite
cookie使用,我们仍然可以通过额外使用带有加密的强随机值的CSRF cookie来减轻CSRF,这样每个 AJAX 请求都会读取cookie值并将cookie值添加到自定义HTTP头中(除了GET和HEAD请求,它们不应该做任何状态修改)。由于CSRF由于同源策略而不能读取任何东西,并且它是基于利用不安全的HTTP方法,如POST,PUT和DELETE,这个CSRF cookie将降低CSRF风险。2使用CSRF cookie的方法被所有现代SPA框架所使用。3Angular 方法在这里被提及。此外,由于cookie是
httpOnly
和Secured
,XSS脚本无法读取它。因此,XSS也得到缓解。还值得一提的是,可以通过使用适当的
content-security-policy
响应头进一步减轻XSS和脚本注入。其他CSRF缓解方法
1.状态变量(Auth 0使用它)-客户端将生成并通过每个请求传递一个加密的强随机数,服务器将与其响应沿着回显,允许客户端验证该随机数。
1.始终检查引用标头,并且仅当引用是受信任域时才接受请求。如果引用标头不存在或不是白名单域,则只需拒绝请求。使用SSL/TLS时,通常存在引用。登录页(主要是信息性的,不包含登录表单或任何安全内容)可能会稍微放松,并允许缺少引用标头的请求。
1.应在服务器中阻止TRACE HTTP方法,因为该方法可用于读取
httpOnly
cookie。1.此外,设置标头Strict-Transport-Security:max-age=; includeSubDomains仅允许安全连接,以防止任何中间人覆盖来自子域的CSRF Cookie。
gupuwyp22#
LocalStorage/SessionStorage容易受到XXS攻击。JavaScript可以读取访问令牌。
带有httpOnly、secure和SameSite=strict标志的Cookie更安全。JavaScript无法访问访问令牌及其有效负载。
但是,如果存在XSS漏洞,攻击者无论如何都能够作为经过身份验证的用户发送请求,因为恶意脚本不需要读取cookie值,cookie可以由浏览器自动发送。
这种说法是正确的,但风险不同。
有了Cookie,访问令牌仍然是隐藏的,攻击者只能进行“现场”攻击。注入到Web应用程序中的恶意脚本可能会受到限制,或者可能不太容易更改/注入更多脚本。用户或Web应用程序可能需要首先成为攻击者的目标。这些条件限制了攻击的规模。
使用localStorage,攻击者可以读取访问令牌并进行远程攻击。他们甚至可以与其他攻击者共享令牌,从而造成更严重的损害。如果攻击者设法在CDN中注入恶意脚本,比如说Google Fonts API,攻击者将能够从使用所包含CDN的所有网站虹吸访问令牌和URL。并且很容易找到新的目标,使用localStorage的网站更容易成为目标。
为了便于讨论
笔测试可能会将敏感数据的localStorage使用标记为风险。
如果JavaScript可以从XSS攻击的localStorage中读取访问令牌,那么为什么每个人仍然推荐使用httpOnly标志呢?
OWASP的建议
不要将会话标识符存储在本地存储中,因为JavaScript始终可以访问这些数据。Cookie可以使用httpOnly标记降低此风险。
https://medium.com/@coolgk/localstorage-vs-cookie-for-jwt-access-token-war-in-short-943fb23239ca
i1icjdpr3#
HTTP is a stateless protocol.阅读该答案以了解更多细节,但本质上这意味着HTTP服务器(如您的Web服务器)不会在一个请求的生命周期之外存储任何客户端信息。这是Web应用的一个问题,因为这意味着您无法记住哪个用户登录。
Cookie就是为了解决这个问题而发明的。Cookie是客户端和服务器在 * 每次 * 请求时来回发送的文本数据。它们允许您通过让客户端和服务器在每次通信时就它们所记住的内容达成一致来有效地维护应用程序状态数据。
这意味着,从根本上说,没有cookie就无法拥有会话。* 必须 * 有一个cookie存储 * 至少 * 会话ID,以便您可以通过查找会话来找出当前登录应用的用户。这就是express-session的作用:主
session
方法的文档明确指出会话ID存储在cookie中。所以我的问题是我需要存储cookie吗?因为我可以通过req.sessionID访问它来获得所需的数据。
req.sessionID
可供查找。mdfafbf14#
根据我的经验,只需将令牌存储在localStorage中。
它可以存储高达5MB的信息。您不需要请求用户的权限就可以在localStorage中存储令牌。
唯一需要关注的是目标设备是否支持localStorage api。
在此检查:https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage
它得到了广泛的支持。但根据我的经验,如果你有一个iOS应用程序,并且这个应用程序中有一个HTML页面要求用户存储令牌(也称为WebView),则LocalStorage API无法识别并抛出错误。
解决方法很简单,我只是把token放在url中,每次都传输它。在webview中,url是不可见的。
在本地存储信息是一种非常古老的方式,cookie中的存储空间相对较小,并且您需要请求用户的许可才能在cookie中存储令牌。
Cookie随每个请求发送,因此会降低性能(尤其是移动数据连接)。用于客户端存储的现代API是Web存储API(localStorage和sessionStorage)和IndexedDB。(https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies)
如果关闭选项卡,存储在sessionStorage中的数据将丢失。如果用户意外关闭选项卡,令牌将丢失,服务器将无法识别当前用户。存储在redux中的令牌与存储在其他js文件中的令牌没有什么不同。redux存储只是另一个js文件。每次页面刷新时,存储在redux中的信息都会丢失。
大多数情况下,如果使用现代风格,令牌会存储在localStorage中。在某些情况下,您可以将令牌存储在cookie中,有时也可以将其放入url中。但决不能存储在会话中。
希望有帮助。