【问题标题】:AWS Cognito Identity Service Provider appears to store access token in local storage. Is this safe?AWS Cognito 身份服务提供商似乎将访问令牌存储在本地存储中。这安全吗?
【发布时间】:2021-03-24 13:41:20
【问题描述】:

我们正在开发一个使用 Amplify 托管在 AWS 上的 React 前端网站的应用程序。这与运行在 EC2/Elastic Beanstalk 上的 .NET Core 3.1 Web API 进行通信。 Cognito 用于通过配置为使用 JWT 令牌的 Web API 进行用户身份验证。

它工作正常,但我们注意到 Cognito 提供程序将 JWT 访问令牌存储在浏览器本地存储中。这是我们在 Chrome 中使用 F12 并检查本地存储时看到的。

根据我们的阅读,不建议将访问令牌存储在本地存储中,因为这会使应用程序容易受到 XSS 攻击。奇怪的是,Cognito 身份提供者选择在这里存储敏感信息。

如果认为这种方法不安全,是否可以将提供程序配置为将此信息存储在其他位置,例如 cookie?

另外,当我们同时控制前端和后端时,是否有替代方法可用于保护不涉及令牌的 API?显然,API 需要知道哪个用户登录到 Web 应用程序才能执行授权检查。 [注意应用程序中的授权是记录级别的,并在数据库表中定义,因此它超越了简单的用户配置文件属性。]

非常感谢您的建议。

道格

【问题讨论】:

    标签: reactjs amazon-web-services amazon-cognito access-token


    【解决方案1】:

    安全性是一个范围而不是一项功能,因此它实际上取决于您对风险与努力的偏好。 Amplify 并不是一个特别好的代码库,它有 500 多个问题,如果您查看代码,您可能会对它的质量感到震惊。

    如果您使用的是 Hosted-UI,那么您可以自己编写代码来管理令牌,而不是使用 amplify,尽管您需要了解一些有关 OAuth 授权和 OIDC 的知识。

    请注意,托管 UI 缺少​​大量功能,因此如果您要使用它,请确保您对它感到满意。在我的头顶上

    • 托管 UI 中没有静默刷新功能,因此没有安全的方式来存储刷新令牌。
    • 不支持托管 UI 中的自定义身份验证流程
    • 托管 UI 中不支持无密码
    • 无法在托管 UI 中预填充字段(例如用户名)
    • 无法在自定义 UI 中自定义过多的晦涩错误消息
    • 现在已修复,但多年来电子邮件地址都区分大小写!

    另一种方法是使用 AWS SDK 直接使用 cognito-idp 获取令牌,但这也有很多问题:

    • 没有代码/PKCE/nonce 功能在移动身份验证会话中如此不安全
    • 无法设置 oauth 范围,因此无法使用它们
    • 因此无法用于 OIDC
    • SRP 的实现是香蕉,到目前为止还没有规范
    • 如果您强制设备注册,它会在设备注册之前交付一个工作访问令牌! (允许不可见的设备进行恶意登录)

    我们使用的是领先联盟的 auth0,但由于 SMS OTP 成本(auth0 每年最低 25,000 美元),我们不得不迁移到 Cognito。

    我已经使用 AWS 十多年了,Cognito 是到目前为止我用过的最差的服务,而且我用过很多!如果可以避免,请这样做。

    要回答最初的问题,是的,它不安全。您可能做的最好的事情就是将它们保存在内存中。如果您愿意,您可以将托管 UI 放在云端,并使用 lambda@edge 将令牌转换为 cookie。不过,这现在让您面临 CSRF 攻击。

    【讨论】:

    • 非常感谢您的广泛回答。我们的专业水平显然没有你们高!最后,我们决定为代币使用自定义存储对象。这仍然使用本地存储,但会加密存储在那里的数据。
    【解决方案2】:

    回答最初的问题:不,这根本不安全。 将刷新令牌存储在任何本地应用程序/脚本可访问的任何本地存储中都是不安全的。因此,最好的方法是将 refreshoten(以及访问令牌)存储在 httponly cookie 中,或者甚至更好地将一次性会话令牌存储在 httponly 安全 cookie 中,可用于获取新的访问权限和刷新 cookie - 类似于它是由带有 XSRF-TOKEN 的 cognito 托管 ui 制作的。 请参阅下面我将如何解决(并计划解决)这个问题:

    一些背景: 由于 GDPR 规定,我认为我不能使用 cognito 托管的用户界面 - 我必须确保用户阅读并接受一般条款和条件(给出明确且可审核的同意),并且在他们输入任何内容之前也可以查看和接受 cookie 政策用于注册的用户数据。然而,内置的托管 ui 设计已经过时且不灵活。我有一个 SPA 网站,我想在其中管理用户、安全端点等。

    所以我有以下想法,它仍然不是超级安全,但我认为如果你想使用 js 和 ampify sdk 并且它也可能回答你的问题,它会更安全: 我将使用放大 javascript sdk 让用户注册、更改 psw 和登录(获取 tokenid、访问令牌和刷新令牌),将制作我自己的“托管 ui”。我将仅将访问令牌存储在内存中(当然不在本地 cookie 中,也不在本地存储中)。访问令牌将在标头(承载)中用于访问 apiGW 端点。访问令牌的过期日期非常短。 (我还会使用apigw发回的httponly安全cookie,以及body中的......,然后在BE端进行比较......)

    诀窍来了:我将刷新令牌一分为二。 (不要忘记它只是一个字符串。)我会将字符串的第一部分存储在本地 cookie 中(javascript 可以读取它,如果浏览器关闭并再次打开它仍然存在)并将发送另一个将刷新令牌的一半发送到 apiGW 端点(无需身份验证即可访问),该端点将其存储在 dynamoDB 表中(带有 TTL),并将一个 httponly 安全 cookie 发送回浏览器,其中包含随机生成的“存储令牌”(这将是dynamodb 中的一个键)。每当客户端需要完整的刷新令牌时,客户端都会调用另一个未经身份验证的 apigw 端点。调用这个端点,浏览器也将发送 httponly 安全 cookie(相同的域),所以后端会得到它。由于它是由 BE 发布的并且仅在给定的浏览器中可用,因此它不会被窃取,因此后端将发回存储的半刷新令牌。 refreshtoken 的另一半存储在浏览器中的一个简单 cookie 中。

    如果浏览器关闭并再次打开,客户端检查是否有任何有效的访问令牌,如果没有,则检查是否有一半的刷新令牌存储为 cookie。然后询问 refreshtoken 的另一部分,假设还存储了一个 httponlycookie,它将从 BE 取回 refreshtoken 的另一部分。如果成功,客户端会尝试使用完整的 refreshtoken 从 cognito 更新/获取访问令牌,如果失败,它将弹出登录屏幕。 每当 refreshtoken 没有被使用时,它就会从内存中删除。

    我知道这仍然不是超级安全的,但可能是比将刷新令牌存储在本地存储中更好的解决方案。

    【讨论】:

      【解决方案3】:

      另外,由于我们同时控制前端和后端,是否有替代方法可用于保护不涉及令牌的 API?

      我对 Amplify 一无所知,但在 AWS Cognito 中,您描述的是 Implicit grant OAuth 流程。在 AWS Cognito 中,您可以使用 Authorization code grant 代替令牌获取代码,您可以在后端交换用户池令牌。

      https://docs.aws.amazon.com/cognito/latest/developerguide/cognito-user-pools-configuring-app-integration.html

      【讨论】:

        猜你喜欢
        • 2012-09-11
        • 2018-09-22
        • 1970-01-01
        • 2016-07-23
        • 2017-11-15
        • 1970-01-01
        • 2020-09-03
        • 2018-06-10
        • 1970-01-01
        相关资源
        最近更新 更多