我正在用Reactjs构建一个应用程序。在验证了access_token之后,我必须进行fetch调用。注册时,从后端服务器获取access_token。但是,在哪里存储这些access_token。有没有办法使这些access_token成为全局的,以便所有组件都可以访问它。我使用了本地存储、缓存和会话存储,但这些都不可取。在过去的几天里,这个问题被搁置了,有什么解决方案吗?提前做好准备。
发布于 2020-04-09 01:14:16
可用选项和限制:
有两种类型的选项可用于存储令牌:
sessionStorage和localStorage。这里存储的数据将始终可用于您的Javascript代码,并且不能从后端访问。因此,例如,您必须手动将其添加到请求的头部中。此存储仅适用于您的应用程序的域,而不适用于子域。这两种机制之间的主要区别在于,data expiry:sessionStorage:数据仅可用于一个会话(直到浏览器或选项卡显示为“closed).localStorage:”或“Data在设计身份验证机制时,您必须考虑两个方面:
出于安全考虑,OWASP不建议将敏感数据存储在网络存储中。你可以查看他们的CheatSheetSeries页面。您还可以阅读this detailed article以了解更多详细信息。
原因主要与XSS漏洞有关。如果你的前端没有100%防御XSS攻击,那么恶意代码就可以在你的网页上执行,它就可以访问令牌。这是非常困难的,因为它可能是由您使用的某个Javascript库引起的。
另一方面,如果Cookie设置为HttpOnly,则Javascript无法访问Cookie。现在cookies的问题是,它们很容易使您的网站容易受到CSRF的攻击。SameSite cookies可以减轻这种类型的攻击。但是,较旧版本的浏览器不支持这种类型的cookie,因此可以使用其他方法,例如使用状态变量。此Auth0 documentation article中对此进行了详细介绍。
建议的解决方案:
为了安全地存储令牌,我建议您使用2个cookies的组合,如下所述:
JWT令牌具有以下结构:header.payload.signature
通常,有用的信息存在于有效负载中,例如用户角色(可用于调整/隐藏UI的某些部分)。所以保持该部分对Javascript代码可用是很重要的。
完成身份验证流程并在后端创建JWT令牌后,我们的想法是:
header.payload部件存储在SameSite Secure Cookie中(因此只能通过https使用,并且仍然可用于JS代码)signature部件存储在JWT中<代码>D48<代码>D49在后端CookieAuthorization: Bearer your_token您可以设置cookie的过期时间,以满足您的应用程序的要求。
这个想法是由Peter Locke在this article中提出并很好地描述的。
发布于 2020-10-27 08:59:40
虽然来晚了,但我想分享一下我对这个话题的看法。Anouar给出了一个很好的答案,包括被认为是针对XSS的仅http-only cookie,指出了CSRF的漏洞,并链接了Peter Locke的文章。
然而,在我的例子中,我需要应用程序是100%无状态的,这意味着我不能使用cookie。
从安全角度来看,将访问令牌存储在永久位置(如localStorage、window等)是一种糟糕的做法。因此,您可以使用redux (或react.js内置的状态/上下文)将JWT存储在变量中。这将保护令牌免受上述攻击,但一旦页面刷新,则将其置为空。
为了解决这个问题,我使用了一个刷新令牌,它存储在localStorage中(如果您愿意,可以使用会话存储或类似的存储)。该刷新令牌的单一目的是获得新的访问令牌,并且后端确保刷新令牌不会被窃取(例如,实现一个被检查的计数器)。我将访问令牌保存在缓存中(我的应用程序中的一个变量),一旦过期或由于重新加载而丢失,我将使用刷新令牌来获取新的访问令牌。
显然,只有在构建后端时(或者至少在后端实现刷新令牌的情况下),这才能起作用。如果您处理的现有应用程序接口没有实现刷新令牌等,并且您不能选择将访问令牌保存在变量中(由于重新加载时为null ),则在将令牌保存到localStorage (或会话存储,or...yea )之前,您也可以使用应用程序密钥对令牌进行加密。请注意,解密令牌需要一些时间,并且可能会减慢您的应用程序。因此,您可以将加密令牌保存到localStorage (或...)在刷新后只解密一次,然后将其保持在状态/redux变量中,直到您再次刷新/再次从localStorage解密它,依此类推。
关于这个话题的最后一句话:身份验证是应用程序的关键基础设施,尽管有趣的游戏和在线银行之间有明显的区别(你可能想要对那家银行“偏执”,而只“关心”游戏),但像"localStorage是完全好的“或”在最坏的情况下会发生什么?1小时后到期“这样的答案是危险的,而且是完全错误的。机器可以在几秒钟内造成很大的破坏,你不想让这个缺口敞开。如果您懒得保护您的应用程序,也许您想使用现有的解决方案,而不是构建自己的解决方案。
也就是说,JWT / token auth对于这个游戏来说是相当新的(几年,但不像dev中的其他主题那样成熟)。找到一个有效的解决方案需要一些时间和精力,但让我们继续构建安全的软件,而不是用快速的黑客手段淹没网络。
最好的,快乐的编码。
发布于 2018-11-30 00:15:15
Michael沃什伯恩有一篇非常好的文章,关于如何用redux保持你的状态,here on his webpage
在这篇文章中,他有一个由Dan Abramov创建的very descriptive video tutorial的链接,他是Redux的合著者之一,我跟随他将其添加到我的项目中。下面是我用来让它工作的代码:
store.js
import { createStore, combineReducers } from "redux";
import { UserReducer, CopyReducer } from "../reducers";
import { loadState, saveState } from "../utils/localStorage";
export const giveMeStore = () => {
const reducers = combineReducers({
copy: CopyReducer,
user: UserReducer
});
const persistedState = loadState();
const store = createStore(reducers, persistedState);
//user contains the TOKEN
store.subscribe(() => {
saveState({
user: store.getState().user
});
});
return store;
};localStorage.js
export const loadState = () => {
try {
const serializedState = localStorage.getItem("state");
if (serializedState === null) {
return undefined;
}
return JSON.parse(serializedState);
} catch (err) {
return undefined;
}
};
export const saveState = state => {
try {
const serializedState = JSON.stringify(state);
localStorage.setItem("state", serializedState);
} catch (err) {
//ignoring write erros
}
};并将商店添加到提供程序中:
import React from "react";
import ReactDOM from "react-dom";
import { Provider } from "react-redux";
import { giveMeStore } from "./store.js";
const Root = () => {
return (
<Provider store={giveMeStore()}>
//... your components
//...
</Provider>
);
};
ReactDOM.render(<Root />, document.querySelector("#root"));https://stackoverflow.com/questions/48983708
复制相似问题