Javascript 我是否必须将令牌存储在 cookie、localstorage 或 session 中?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/54258233/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Do I have to store tokens in cookies or localstorage or session?
提问by Faris Dewantoro
I am using React SPA, Express, Express-session, Passport, and JWT. I'm confused about some of the different client-side storage options to store tokens: Cookies, Session, and JWT / Passport.
我正在使用 React SPA、Express、Express-session、Passport 和 JWT。我对存储令牌的一些不同客户端存储选项感到困惑:Cookies、Session 和 JWT/Passport。
Do tokens have to be stored in cookies, even if I can store them in req.sessionID?
令牌是否必须存储在 cookie 中,即使我可以将它们存储在 cookie 中req.sessionID?
Many websites use cookies to store shopping cart tokens. So far I have stored shopping cart data based on the session ID without adding any cookies.
许多网站使用 cookie 来存储购物车令牌。到目前为止,我已经根据会话 ID 存储了购物车数据,而没有添加任何 cookie。
So when users visit my website, I will match it with their
req.sessionIDand then retrieve the data in the database like shopping carts and user session.
因此,当用户访问我的网站时,我会将其与他们的网站进行匹配
req.sessionID,然后检索数据库中的数据,例如购物车和用户会话。
Do I need to store cookies? I can access it via req.sessionIDto get the data needed.
我需要存储cookies吗?我可以通过访问它req.sessionID来获取所需的数据。
And the second
第二个
I have made authentication using a passport-google-oauth20.After I successfully login, the data is saved into the session. and to send it to the client I have to send it via the URL query ?token='sdsaxas'.
我已使用passport-google-oauth20.进行身份验证。成功登录后,数据将保存到会话中。并将其发送给客户端,我必须通过 URL 查询发送它?token='sdsaxas'。
in this case I get a lot of difference of opinion. someone saved it into local storage and someone saved it into cookies by converting it to a token using JWT.
在这种情况下,我有很多不同意见。有人将其保存到本地存储中,有人通过使用 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);
});
Can I indeed store everything related to the session by using sessionID (without cookies or localstorage)?
我确实可以使用 sessionID(没有 cookie 或 localstorage)存储与会话相关的所有内容吗?
Only by doing fetch once or every page refresh and retrieving the data and then saved into redux because I use React SPA.
由于我使用 React SPA,因此只能通过 fetch 一次或每次页面刷新并检索数据然后保存到 redux 中。
回答by Saptarshi Basu
This answer is based on the stateless approach and therefore it doesn't talk about the traditional session management
这个答案基于无状态方法,因此它不讨论传统的会话管理
You have asked two altogether different questions:
你问了两个完全不同的问题:
- Shopping cart - which is more related to business functionality
- OAuth 2 & JWT - which is related to security and authentication
- 购物车 - 与业务功能更相关
- OAuth 2 & JWT - 与安全和身份验证相关
As an user of an ecommerce website, I'd expect that any item I add to my shopping cart from my mobile device while commuting to my workplace, should be available in the cart when I login to the website from my PC after reaching home. Therefore, the cart data should be saved in the back-end DB and linked to my user account.
作为电子商务网站的用户,我希望我在上班途中从移动设备添加到购物车的任何商品,在我回家后从 PC 登录网站时都应该在购物车中可用。因此,购物车数据应保存在后端数据库中并链接到我的用户帐户。
When it comes to authentication using OAuth 2.0, the JWT access token and / or refresh token need to be stored somewhere in the client device, so that once the user authenticates himself by providing login credentials, he doesn't need to provide his credentials again to navigate through the website. In this context, the browser local storage, session storage and cookies are all valid options. However, note that here the cookie is not linked to any session on the server side. In other words, the cookie doesn't store any session id. The cookie is merely used as a storage for access token which is passed to the server with every http request and the server then validates the token using the digital signature to ensure that it is not tampered and it is not expired.
在使用 OAuth 2.0 进行身份验证时,JWT 访问令牌和/或刷新令牌需要存储在客户端设备中的某个位置,以便一旦用户通过提供登录凭据对自己进行身份验证,他就不需要再次提供他的凭据浏览网站。在这种情况下,浏览器本地存储、会话存储和 cookie 都是有效的选项。但是,请注意,此处的 cookie 未链接到服务器端的任何会话。换句话说,cookie 不存储任何会话 ID。cookie 仅用作访问令牌的存储,访问令牌随每个 http 请求传递给服务器,然后服务器使用数字签名验证令牌以确保它没有被篡改和过期。
Although all three storage options for access and / or refresh tokens are popular, cookie seems to be the most secured option when used in the correct way.
尽管访问和/或刷新令牌的所有三个存储选项都很流行,但如果以正确的方式使用,cookie 似乎是最安全的选项。
To understand this better, I recommend you read thisand thisalong with the OAuth 2.0 specification.
为了更好地理解这一点,我建议您阅读本文和本文以及 OAuth 2.0 规范。
Update On 16-Feb-2019
2019 年 2 月 16 日更新
I said earlier that cookie seems to be the most secured options. I'd like to further clarify the point here.
我之前说过 cookie 似乎是最安全的选项。我想在这里进一步澄清这一点。
The reason I think browser localStorageand sessionStoragedo not provide enough security for storing auth tokens are as follows:
我认为浏览器localStorage并sessionStorage没有为存储身份验证令牌提供足够的安全性的原因如下:
If XSS occurs, the malicious script can easily read the tokens from there and send them to a remote server. There on-wards the remote server or attacker would have no problem in impersonating the victim user.
localStorageandsessionStorageare not shared across sub-domains. So, if we have two SPA running on different sub-domains, we won't get the SSO functionality because the token stored by one app won't be available to the other app within the organization. There are some solutions usingiframe, but those look more like workarounds rather than a good solution. And when the response headerX-Frame-Optionsis used to avoid clickHymaning attacks withiframe, any solution withiframeis out of question.
如果发生 XSS,恶意脚本可以轻松地从那里读取令牌并将它们发送到远程服务器。在那里,远程服务器或攻击者在冒充受害用户方面没有问题。
localStorage并且sessionStorage不跨子域共享。因此,如果我们有两个 SPA 在不同的子域上运行,我们将无法获得 SSO 功能,因为组织内的另一个应用程序无法使用一个应用程序存储的令牌。有一些使用 的解决方案iframe,但这些看起来更像是解决方法而不是好的解决方案。并且当响应头X-Frame-Options被用来避免点击劫持攻击时iframe,任何解决方案iframe都是不可能的。
These risks can, however, be mitigated by using a fingerprint (as mentioned in OWASP JWT Cheat Sheet) which again in turn requires a cookie.
然而,这些风险可以通过使用指纹(如OWASP JWT 备忘单中提到的)来减轻,而指纹又需要一个 cookie。
The idea of fingerprint is, generate a cryptographically strong random string of bytes. The Base64 string of the raw string will then be stored in a HttpOnly, Secure, SameSitecookie with name prefix __Secure-. Proper values for Domain and Path attributes should be used as per business requirement. A SHA256 hash of the string will also be passed in a claim of JWT. Thus even if an XSS attack sends the JWT access token to an attacker controlled remote server, it cannot send the original string in cookie and as a result the server can reject the request based on the absence of the cookie. The cookie being HttpOnlycannot be read by XSS scripts.
指纹的想法是,生成加密强的随机字节串。原始字符串的 Base64 字符串将存储在名称为 prefix的HttpOnly, Secure, SameSitecookie 中__Secure-。应根据业务需求使用域和路径属性的正确值。字符串的 SHA256 哈希值也将在 JWT 的声明中传递。因此,即使 XSS 攻击将 JWT 访问令牌发送到攻击者控制的远程服务器,它也无法发送 cookie 中的原始字符串,因此服务器可以基于 cookie 的缺失拒绝请求。HttpOnlyXSS 脚本无法读取cookie 。
Therefore, even when we use localStorageand sessionStorage, we have to use a cookie to make it secured. On top of that, we add the sub-domain restriction as mentioned above.
因此,即使我们使用localStorageand sessionStorage,我们也必须使用 cookie 来确保它的安全。最重要的是,我们添加了如上所述的子域限制。
Now, the only concern about using a cookie to store JWT is, CSRF attack. Since we use SameSitecookie, CSRF is mitigated because cross-site requests (AJAX or just through hyperlinks) are not possible. If the site is used in any old browser or some other not so popular browsers that do not support SameSitecookie, we can still mitigate CSRF by additionally using a CSRF cookie with a cryptographically strong random value such that every AJAX request reads the cookie value and add the cookie value in a custom HTTP header (except GET and HEAD requests which are not supposed to do any state modifications). Since CSRF cannot read anything due to same origin policy and it is based on exploiting the unsafe HTTP methods like POST, PUT and DELETE, this CSRF cookie will mitigate the CSRF risk. This approach of using CSRF cookie is used by all modern SPA frameworks. The Angular approach is mentioned here.
现在,使用 cookie 存储 JWT 的唯一问题是 CSRF 攻击。由于我们使用SameSitecookie,CSRF 得到缓解,因为跨站点请求(AJAX 或仅通过超链接)是不可能的。如果该网站在任何旧浏览器或其他不支持的不太流行的浏览器中使用SameSitecookie,我们仍然可以通过额外使用具有加密强随机值的 CSRF cookie 来缓解 CSRF,这样每个 AJAX 请求都会读取 cookie 值并将 cookie 值添加到自定义 HTTP 标头中(除了不应该这样做的 GET 和 HEAD 请求)任何状态修改)。由于CSRF由于同源策略无法读取任何内容,并且它基于利用POST,PUT和DELETE等不安全的HTTP方法,因此该CSRF cookie将降低CSRF风险。所有现代 SPA 框架都使用这种使用 CSRF cookie 的方法。这里提到了Angular 方法。
Also, since the cookie is httpOnlyand Secured, XSS script cannot read it. Thus XSS is also mitigated.
此外,由于 cookie 是httpOnlyand Secured,XSS 脚本无法读取它。因此,XSS 也得到了缓解。
It may be also worth mentioning that XSS and script injection can be further mitigated by using appropriate content-security-policyresponse header.
还值得一提的是,可以通过使用适当的content-security-policy响应头进一步减轻 XSS 和脚本注入。
Other CSRF mitigation approaches
其他CSRF缓解方法
- State Variable (Auth0 uses it) - The client will generate and pass with every request a cryptographically strong random nonce which the server will echo back along with its response allowing the client to validate the nonce. It's explained in Auth0 doc.
- Always check the referer header and accept requests only when referer is a trusted domain. If referer header is absent or a non-whitelisted domain, simply reject the request. When using SSL/TLS referrer is usually present. Landing pages (that is mostly informational and not containing login form or any secured content may be little relaxed ?and allow requests with missing referer header
- TRACE HTTP method should be blocked in the server as this can be used to read the httpOnly cookie
- Also, set the header Strict-Transport-Security: max-age=; includeSubDomains? to allow only secured connections to prevent any man-in-the-middle overwrite the CSRF cookies from a sub-domain
- 状态变量(Auth0 使用它) - 客户端将生成并随每个请求传递一个加密的强随机随机数,服务器将与其响应一起回显,允许客户端验证随机数。它在Auth0 doc 中进行了解释。
- 始终检查引用标头并仅在引用域是受信任域时才接受请求。如果引用标头不存在或域未列入白名单,只需拒绝请求即可。使用 SSL/TLS 时通常会出现引用。登陆页面(主要是信息性的,不包含登录表单或任何安全内容可能有点放松?并允许缺少引用标头的请求
- 应在服务器中阻止 TRACE HTTP 方法,因为这可用于读取 httpOnly cookie
- 另外,设置标头 Strict-Transport-Security: max-age=; 包括子域?只允许安全连接以防止任何中间人覆盖来自子域的 CSRF cookie
回答by Effective Robot
HTTP is a stateless protocol.Read that answer for more detail, but essentially that means that HTTP servers, such as your web server, do not store any information about clients beyond the lifetime of one request. This is a problem for web apps because it means you can't remember which user is logged in.
HTTP 是一种无状态协议。阅读该答案以获取更多详细信息,但本质上这意味着 HTTP 服务器(例如您的 Web 服务器)不会在一个请求的生命周期之外存储有关客户端的任何信息。这是 Web 应用程序的一个问题,因为这意味着您无法记住哪个用户已登录。
Cookies were invented as the solution to this. Cookies are textual data that the client and server send back and forth on everyrequest. They allow you to effectively maintain application state data, by having the client and server agree on what they remember each time they communicate.
饼干的发明就是为了解决这个问题。Cookie 是客户端和服务器在每次请求时来回发送的文本数据。它们允许您有效地维护应用程序状态数据,让客户端和服务器就他们每次通信时记住的内容达成一致。
This means, fundamentally, you cannot have a session without a cookie. There mustbe a cookie that stores at leastthe session ID, so that you can find out which user is currently logged into your app by looking up the session. This is what express-session does: the documentationfor the main sessionmethod explicitly notes that the session ID is stored in a cookie.
这意味着,从根本上说,没有 cookie 就不能进行会话。有必须是一个cookie,存储至少会话ID,这样就可以找出哪些用户通过查找会话当前登录到您的应用程序。这就是 express-session 所做的:主要方法的文档session明确指出会话 ID 存储在 cookie 中。
so my question is do I need to store cookies?because I can access it via req.sessionID to get the data needed.
所以我的问题是我需要存储 cookie 吗?因为我可以通过 req.sessionID 访问它来获取所需的数据。
Youdon't need to store cookies. express-session will do this for you. Your application as a whole doesneed to store a cookie; without it, you wouldn't have a req.sessionIDto look up.
您不需要存储 cookie。express-session 会为你做这件事。您的应用程序作为一个整体确实需要存储一个 cookie;没有它,你就不用req.sessionID抬头了。
回答by MING WU
According to my experience, just store token in localStorage.
根据我的经验,只需将令牌存储在 localStorage 中。
localStorage:
本地存储:
it can store information up tp 5MB. You do not need to ask user's permission to store token in localStorage.
它可以存储高达 5MB 的信息。在 localStorage 中存储令牌不需要征得用户的许可。
The only concern is that whether the target device support localStorage api.
唯一需要担心的是目标设备是否支持localStorage api。
Check here: https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage
在这里查看:https: //developer.mozilla.org/en-US/docs/Web/API/Window/localStorage
It is widely supported. But according to my experience, if you have an ios app, and there is a html page in this app which ask the user to store token (also called webview), the localStorage api cannot be recognized and throw an error.
它得到了广泛的支持。但是根据我的经验,如果你有一个ios应用程序,并且这个应用程序中有一个html页面要求用户存储令牌(也称为webview),则无法识别localStorage api并抛出错误。
The solution is simply i just put token in url and transfer it every time. In webview, url is not visible.
解决方案很简单,我只是将令牌放入 url 并每次都传输它。在 webview 中,url 不可见。
Cookie:
曲奇饼:
It is a very old style to store info locally. Storage in cookie is relatively small and you need to ask user's permission in order to store token in cookie.
在本地存储信息是一种非常古老的风格。cookie 中的存储量相对较小,您需要征得用户的许可才能将令牌存储在 cookie 中。
Cookies are sent with every request, so they can worsen performance (especially for mobile data connections). Modern APIs for client storage are the Web storage API (localStorage and sessionStorage) and IndexedDB. (https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies)
每个请求都会发送 Cookie,因此它们会降低性能(尤其是对于移动数据连接)。用于客户端存储的现代 API 是 Web 存储 API(localStorage 和 sessionStorage)和 IndexedDB。( https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies)
Do not store token in sessionStorage or redux.
不要将令牌存储在 sessionStorage 或 redux 中。
Data stored in sessionStorage will be lost if the tab is closed. If a user accidentally closed a tab, the token is lost and the server will not be able to identify the current user. Token stored in redux is not different to be stored in other js files. redux store is just another js file. information stored in redux get lost for every page refresh.
如果选项卡关闭,存储在 sessionStorage 中的数据将丢失。如果用户不小心关闭了一个选项卡,令牌就会丢失,服务器将无法识别当前用户。存储在 redux 中的 token 与存储在其他 js 文件中没有区别。redux store 只是另一个 js 文件。每次页面刷新时,存储在 redux 中的信息都会丢失。
In conclusion,
综上所述,
most of the time, token is stored in localStorage if using a modern style. In certain scenarios, you can store token in cookie and may be put in url sometimes. But never store in session.
大多数情况下,如果使用现代风格,令牌存储在 localStorage 中。在某些情况下,您可以将令牌存储在 cookie 中,有时可能会放在 url 中。但永远不要存储在会话中。
Hope it helps.
希望能帮助到你。

