简体   繁体   English

AWS Cognito 身份服务提供商似乎将访问令牌存储在本地存储中。 这安全吗?

[英]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在我的头顶

  • no silent refresh capability in the hosted UI, so no safe way to store the refresh token.托管 UI 中没有静默刷新功能,因此没有安全的方式来存储刷新令牌。
  • no support for custom auth flow in the hosted UI不支持托管 UI 中的自定义身份验证流程
  • no passwordless support in the hosted UI托管 UI 中没有无密码支持
  • no ability to pre-populate a field in the hosted UI (eg username)无法在托管 UI 中预填充字段(例如用户名)
  • no ability to customise the plethora of obscure error messages in the custom UI无法在自定义 UI 中自定义过多的晦涩错误消息
  • fixed now, but for years the email addresses were case sensitive!现在已修复,但多年来 email 地址是区分大小写的!

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 获取令牌,但这也有很多问题:

  • no code/PKCE/nonce capability so insecure in a mobile authsession没有代码/PKCE/nonce 功能在移动身份验证中如此不安全
  • no ability to set oauth scopes, so can't use them无法设置 oauth 范围,所以不能使用它们
  • consequently not possible to use for OIDC因此无法用于 OIDC
  • the SRP implementation is bananas and so far off spec SRP 实施是香蕉,到目前为止还没有规范
  • if you make device registration mandatory, it will deliver a working access tokens before the device is registered!如果您强制设备注册,它将在设备注册之前交付一个工作访问令牌! (allows invisible devices for malicious logins) (允许不可见的设备进行恶意登录)

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.

相关问题 对为什么不将访问令牌存储在本地存储感到困惑 - Confused on why not store access token in local storage 在 AsyncStorage 中存储访问令牌安全吗? - Safe to store access token inside AsyncStorage? 将令牌存储在 cookie 中或 reactjs 中的本地存储中 - store token in cookie or local storage in reactjs 将访问令牌存储在 next-auth session 中是否安全? - Is it safe to store access token in next-auth session? 我正在尝试将照片存储在数据库(firebase)和存储中。 照片已存储在 Storage 中,但未添加到 firestore - i am trying to store the photo in the database (firebase) and in the storage. the photo got store in the Storage, but not added in the firestore 如何在react redux中从本地存储传递访问令牌 - how to pass access token from local storage in react redux 将令牌添加到本地存储? - Adding Token to Local Storage? 一旦 AWS Cognito 身份验证,我们如何获得 facebook 访问令牌? - How we can get the facebook access token once the AWS Cognito authentication? 如何在 redux 存储中重新加载页面时访问本地存储? - How can I access the local storage on page reload in redux store? 根据 id 更改 url 并从本地存储中获取值。 ReactJS - Changing url based on id and getting values from local storage. ReactJS
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM