我目前正在使用ReactJS构建一个单页应用程序。
我读到不使用localStorage的原因之一是因为XSS漏洞。
既然React会转义所有用户输入,那么现在使用localStorage是否安全呢?
我目前正在使用ReactJS构建一个单页应用程序。
我读到不使用localStorage的原因之一是因为XSS漏洞。
既然React会转义所有用户输入,那么现在使用localStorage是否安全呢?
当前回答
只要加密,将令牌存储在localStorage中是安全的。下面是一个压缩的代码片段,展示了许多方法中的一种。
import SimpleCrypto from 'simple-crypto-js';
const saveToken = (token = '') => {
const encryptInit = new SimpleCrypto('PRIVATE_KEY_STORED_IN_ENV_FILE');
const encryptedToken = encryptInit.encrypt(token);
localStorage.setItem('token', encryptedToken);
}
然后,在使用令牌之前,使用PRIVATE_KEY_STORED_IN_ENV_FILE对其进行解密
其他回答
我加入讨论的时间比较晚,但是由于OpenID Connect等更成熟和现代的认证协议的优势。
TL;DR:首选的方法是将JWT Token存储在内存中:而不是在cookie中,也不是在本地存储中。
细节
您希望将用户身份验证的责任与应用程序的其他工作分离开来。Auth很难得到正确的答案,少数几个花了所有时间思考这个问题的团队可能会担心你我永远都不会得到正确答案的细节。
为你的应用建立一个专用的身份提供者,并使用OpenID Connect协议进行身份验证。这可能是像谷歌、Microsoft或Okta这样的提供商,也可能是与一个或多个其他服务联合的轻量级身份服务器。
使用授权码流让用户验证并获得应用程序的访问令牌。使用一个受尊重的客户端库来处理OpenID连接细节,这样你就可以让库在应用程序拥有有效令牌、通过刷新获得新的有效令牌或当令牌无法刷新(因此用户需要再次验证)时通知应用程序。应该对库进行配置(可能是默认情况),以避免存储令牌。
FAQ
当有人刷新页面时会发生什么?我不想让他们再次登录。
When the app first loads, it should always redirect the user to your Identity Provider. Based on how that identity provider handles things, there's a good chance the user won't have to log in. For example, if you're federating to an identity provider like Google or Microsoft, the user may have selected an option indicating that they are on a trusted device and they want to be remembered. If so, they won't need to log in again for a very long time, long after your auth token would have expired. This is much more convenient for your users.
同样,如果用户表示他们在共享设备上,将来不应该自动登录,那么您希望强制再次登录:您无法区分刷新浏览器窗口的用户和重新打开关闭的浏览器并导航到存储在浏览器历史记录中的页面的用户。
身份提供者不是使用cookie来保持用户登录吗?那么CSRF或XSS攻击呢?
这些实现细节是特定于身份提供程序的。如果他们使用cookie,他们的工作就是实施反csrf措施。他们比你更不可能使用有问题的第三方库,或者导入有问题的外部组件,因为他们的应用程序只有一个任务。
难道我不应该花时间解决XSS攻击吗?如果有人向我的应用程序中注入代码,这难道不是“游戏结束”吗?
如果这是一个非此即彼的命题,并且你有理由相信你的应用程序存在XSS或代码注入漏洞,那么这些肯定应该优先考虑。但是,对于一种分层的安全性来说,良好的安全性包括在多个级别上遵循最佳实践。
此外,使用受信任的第三方库连接到受信任的第三方安全提供者应该有望节省您处理各种其他安全相关问题的时间。
Philippe De Ryck博士写了一篇有用的文章,深入分析了漏洞(尤其是XSS)的真正影响。
这篇文章让人大开眼界!
简而言之,开发人员最关心的应该是保护web应用程序不受XSS的影响,而不应该太担心使用哪种类型的存储区域。
菲利普医生建议采取以下3个步骤:
Don't worry too much about the storage area. Saving an access token in localStorage area will save the developer a massive amount of time for development of next phases of the application. Review your app for XSS vulnerabilities. Perform a through code review and learn how to avoid XSS within the scope of your templating framework. Build a defense-in-depth mechanism against XSS. Learn how you could further lock down your application. E.g. utilising Content Security Policy (CSP) and HTML5 sandboxing.
记住,一旦你被XSS攻击了,游戏就结束了!
我知道这是一个老问题,但根据@mikejones1477所说,现代前端库和框架转义文本,为您提供对XSS的保护。cookie不是使用凭证的安全方法的原因是,当localStorage阻止CSRF时,cookie不能阻止CSRF(还要记住,cookie也可以通过JavaScript访问,所以XSS不是这里的大问题),这就是为什么的答案。
在本地存储中存储身份验证令牌并手动将其添加到每个请求中以防止CSRF的原因是关键字:手动。由于浏览器不会自动发送认证令牌,如果我访问邪恶。例如,它设法发送一个POST http://example.com/delete-my-account,它将无法发送我的authn令牌,因此该请求被忽略。
当然httpOnly是圣杯,但你不能从reactjs或任何js框架访问,你仍然有CSRF漏洞。我的建议是localstorage,或者如果你想使用cookie,确保像Django一样实现一些解决CSRF问题的解决方案。
关于CDN,确保你没有使用一些奇怪的CDN,例如谷歌或bootstrap提供的CDN,由社区维护,不包含恶意代码,如果你不确定,你可以自由审查。
Localstorage被设计成可以通过javascript访问,所以它不提供任何XSS保护。正如在其他回答中提到的,有很多可能的方式来进行XSS攻击,默认情况下,本地存储不受保护。
However, cookies have security flags which protect from XSS and CSRF attacks. HttpOnly flag prevents client side javascript from accessing the cookie, Secure flag only allows the browser to transfer the cookie through ssl, and SameSite flag ensures that the cookie is sent only to the origin. Although I just checked and SameSite is currently supported only in Opera and Chrome, so to protect from CSRF it's better to use other strategies. For example, sending an encrypted token in another cookie with some public user data.
因此,cookie是存储身份验证数据的更安全的选择。
只要加密,将令牌存储在localStorage中是安全的。下面是一个压缩的代码片段,展示了许多方法中的一种。
import SimpleCrypto from 'simple-crypto-js';
const saveToken = (token = '') => {
const encryptInit = new SimpleCrypto('PRIVATE_KEY_STORED_IN_ENV_FILE');
const encryptedToken = encryptInit.encrypt(token);
localStorage.setItem('token', encryptedToken);
}
然后,在使用令牌之前,使用PRIVATE_KEY_STORED_IN_ENV_FILE对其进行解密