[英]AWS Cognito Identity Service Provider appears to store access token in local storage. Is this safe?
We are developing an application that uses a React front end website hosted on AWS using Amplify.我们正在开发一个使用 Amplify 托管在 AWS 上的 React 前端网站的应用程序。 This communicates with a .NET Core 3.1 Web API running on EC2 / Elastic Beanstalk.
这与运行在 EC2 / Elastic Beanstalk 上的 .NET Core 3.1 Web API 通信。 Cognito is used for user authentication with the Web API configured to use JWT tokens.
Cognito 用于使用 Web API 配置为使用 JWT 令牌的用户身份验证。
It works OK, but we have noticed that the Cognito provider stores the JWT access token in the browser local storage.它工作正常,但我们注意到 Cognito 提供程序将 JWT 访问令牌存储在浏览器本地存储中。 This is what we see using F12 in Chrome and inspecting local storage.
这是我们在 Chrome 中使用 F12 并检查本地存储时看到的。
From what we have read, storing access tokens in local storage is not advised as it makes the application susceptible to XSS attacks.根据我们的阅读,不建议将访问令牌存储在本地存储中,因为这会使应用程序容易受到 XSS 攻击。 Strange then, that the Cognito identity provider chooses to store sensitive information here.
奇怪的是,Cognito 身份提供者选择在这里存储敏感信息。
If this approach is not considered safe, can the provider be configured to store this information elsewhere, such as cookies?如果这种方法不安全,是否可以将提供程序配置为将此信息存储在其他位置,例如 cookies?
Alternatively, as we control both front and back ends, is there an alternative method that can be used to secure the API that does not involve tokens?或者,当我们同时控制前端和后端时,是否有替代方法可用于保护不涉及令牌的 API? Obviously the API needs to know which user is logged on to the web application in order to perform authorization checks.
显然,API 需要知道哪个用户登录到 web 应用程序才能执行授权检查。 [Note authorization in the application is record level and defined in database tables, so it goes beyond simple user profile attributes.]
[注意应用程序中的授权是记录级别并在数据库表中定义,因此它超越了简单的用户配置文件属性。]
Many thanks in advance for your advice.非常感谢您的建议。
Doug道格
Security is a spectrum not a feature so it really depends on your appetite for risk vs effort.安全性是一个范围而不是一项功能,因此它实际上取决于您对风险与努力的偏好。 Amplify is not a particularly nice codebase, it has 500+ issues and if you look at the code you might be fairly shocked at the quality of it.
Amplify 并不是一个特别好的代码库,它有 500 多个问题,如果您查看代码,您可能会对它的质量感到震惊。
If you are using Hosted-UI then you can write code to manage the tokens yourself rather than using amplify, although you will need to learn a bit about OAuth grants and OIDC.如果您使用的是 Hosted-UI,那么您可以自己编写代码来管理令牌,而不是使用 amplify,尽管您需要了解一些有关 OAuth 授权和 OIDC 的知识。
Be aware that the Hosted UI lacks a huge amount of features, so if you are going to use it make sure you are happy with it.请注意,托管 UI 缺少大量功能,因此如果您要使用它,请确保您对它感到满意。 Off the top of my head
在我的头顶
An alternative is also to just use the AWS SDK to get tokens directly using cognito-idp but this also has a bunch of issues:另一种方法是仅使用 AWS SDK 直接使用 cognito-idp 获取令牌,但这也有很多问题:
We were using auth0 which was leagues ahead but we had to move to Cognito because of SMS OTP cost (min $25k per year at auth0).我们使用的是领先联盟的 auth0,但由于 SMS OTP 成本(auth0 每年最低 25,000 美元),我们不得不迁移到 Cognito。
I have been using AWS for over a decade now, Cognito is by far the worst service I have used, and I have used a lot, If you can avoid it.我已经使用 AWS 十多年了,Cognito 是迄今为止我用过的最糟糕的服务,而且我用过很多,如果你能避免的话。 do so.
这样做。
To answer the original question, yeah it's insecure.要回答最初的问题,是的,它是不安全的。 The best you can probably do is keep them in memory.
您可能做的最好的事情就是将它们保存在 memory 中。 If you wanted to you could probably put the hosted UI behind a cloudfront and use an lambda@edge to transform the token into a cookie instead.
如果您愿意,您可以将托管 UI 放在云端,并使用 lambda@edge 将令牌转换为 cookie。 This has now opened you up to CSRF attacks though.
不过,这现在让您面临 CSRF 攻击。
answering the original question: no, it is not safe at all.回答最初的问题:不,这根本不安全。 Storing refreshtoken in any local storage accessable to any local app/script is not secure.
将刷新令牌存储在任何本地应用程序/脚本可访问的任何本地存储中都是不安全的。 So, the best way would be to store the refreshoten (and also the access token) in an httponly cookie or even better to store a one-time session token in httponly secure cookie could be used to get new access and refresh cookies - similarly as it is made by cognito hosted ui with XSRF-TOKEN.
因此,最好的方法是将 refreshoten(以及访问令牌)存储在 httponly cookie 中,或者甚至更好地将一次性 session 令牌存储在 httponly 安全 cookie 中,以获取新的访问权限并刷新 cookies - 类似于它是由带有 XSRF-TOKEN 的 cognito 托管 ui 制作的。 See below how I would solve (and plan to solve) this issue:
请参阅下面我将如何解决(并计划解决)这个问题:
Some background: Due to GDPR regulations I think I can not use the cognito hosted ui - I have to make sure users read and accept the general terms and conditions (giving clear and auditable consent) and can review and accept cookie policies as well before they type in any user data for sign up.一些背景:由于 GDPR 规定,我认为我不能使用 cognito 托管的用户界面 - 我必须确保用户阅读并接受一般条款和条件(给出明确且可审核的同意),并且在他们之前也可以查看和接受 cookie 政策输入任何用户数据进行注册。 Nevertheless the built in hosted ui design is quite outdated and unflexible.
然而,内置的托管 ui 设计已经过时且不灵活。 I have an SPA website where I want to manage users, secure endpoints, etc.
我有一个 SPA 网站,我想在其中管理用户、安全端点等。
So I have the following idea which is still not super secure but I think it is more secure one if you want to use js and ampify sdk and which also might answer your question: I'll use amplify javascript sdk to let users sign up, change psw and log in (get tokenid, access token and refresh token), will make my own "hosted ui".所以我有以下想法,它仍然不是超级安全,但我认为如果你想使用 js 并放大 sdk 并且它也可能回答你的问题,它会更安全:我将使用放大 javascript ZEAE18BC41E1434DD98FA2DD989531 让用户签名更改 psw 并登录(获取 tokenid、访问令牌和刷新令牌),将制作我自己的“托管 ui”。 I'll store the access token in memory only (not in local cookies and not in localstorage for sure).
我将仅将访问令牌存储在 memory 中(不在本地 cookies 中,当然也不在 localstorage 中)。 Access token will be used in header (bearer) to access apiGW endpoints.
访问令牌将在 header(承载)中用于访问 apiGW 端点。 Access tokens will have very short expire dates.
访问令牌的过期日期非常短。 (I'd also use httponly secure cookies sent back by the apigw, as well as in the body.., then compare at BE side..)
(我也会使用由apigw发回的httponly安全cookies,以及在正文中..,然后在BE端进行比较..)
And here comes the trick: I'd cut the refresh token into two.诀窍来了:我将刷新令牌一分为二。 (Don't forget it is just a string.) I'd store the first part of the string in a local cookie (javascript can read it, if browser is closed and opened again it will be still there) and will send the other half of the refresh token to an apiGW endpoint (accessable without authentication) which will store it in a dynamoDB table (with TTL) and will send back an httponly secure cookie to the browser with a randomly generated "storagetoken" in it (which will be a key in dynamodb).
(不要忘记它只是一个字符串。)我会将字符串的第一部分存储在本地 cookie 中(javascript 可以读取它,如果浏览器关闭并再次打开它仍然存在)并将发送另一个将刷新令牌的一半发送到 apiGW 端点(无需身份验证即可访问),该端点将其存储在 dynamoDB 表中(带有 TTL),并将一个 httponly 安全 cookie 发送回浏览器,其中包含随机生成的“存储令牌”(这将是dynamodb 中的一个键)。 There will be another unauthenticated apigw endpoint which will be called by the client whenever the client needs the full refresh token.
每当客户端需要完整的刷新令牌时,客户端都会调用另一个未经身份验证的 apigw 端点。 Calling this endpoint the browser will send in the httponly secure cookie as well (same domain), so the backend will get it.
调用这个端点,浏览器也将发送 httponly 安全 cookie(相同的域),所以后端会得到它。 As it is issued by the BE and available only in the given browser it can not be stolen so the backend will send back the stored half refreshtoken.
由于它是由 BE 发布的,并且仅在给定的浏览器中可用,因此它不会被窃取,因此后端将发回存储的半刷新令牌。 The other half part of refreshtoken is stored in a simple cookie in the browser.
refreshtoken 的另一半存储在浏览器中的一个简单 cookie 中。
If the browser is closed and opened again client checks if there is any valid accesstoken and if not it checks if there is a half refeshtoken stored as cookie.如果浏览器关闭并再次打开,客户端检查是否有任何有效的访问令牌,如果没有,则检查是否有一半的刷新令牌存储为 cookie。 Then ask the other part of refreshtoken assuming there is a httponlycookie also stored and it will get back the other part of the refreshtoken from the BE.
然后询问 refreshtoken 的另一部分,假设还存储了一个 httponlycookie,它将从 BE 取回 refreshtoken 的另一部分。 In case of success the client tries to use the full refreshtoken to renew/get access token from cognito, in case of failure it will pop up the login screen.
如果成功,客户端会尝试使用完整的 refreshtoken 从 cognito 更新/获取访问令牌,如果失败,它将弹出登录屏幕。 Whenever refreshtoken is not in use it is deleted from the memory.
无论何时未使用 refreshtoken,它都会从 memory 中删除。
I know this is still not supersecure but might be a better solution than storing refresh token in localstorage.我知道这仍然不是超级安全的,但可能比在本地存储中存储刷新令牌更好。
Alternatively, as we control both front and back ends, is there an alternative method that can be used to secure the API that does not involve tokens?
或者,当我们同时控制前端和后端时,是否有替代方法可用于保护不涉及令牌的 API?
I don't know anything of Amplify but in AWS Cognito what you describe is the Implicit grant
OAuth flow.我对 Amplify 一无所知,但在 AWS Cognito 中,您描述的是
Implicit grant
OAuth 流。 In AWS Cognito it is possible to use Authorization code grant
where you instead of the token get a code which you in the backend can exchange for a user pool token.在 AWS Cognito 中,可以使用
Authorization code grant
,而不是令牌,您可以在后端获取代码,您可以在后端交换用户池令牌。
https://docs.aws.amazon.com/cognito/latest/developerguide/cognito-user-pools-configuring-app-integration.html https://docs.aws.amazon.com/cognito/latest/developerguide/cognito-user-pools-configuring-app-integration.html
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.