【问题标题】:Is "Resource Owner Password Credentials" safe in OAuth2?OAuth2 中的“资源所有者密码凭据”是否安全?
【发布时间】:2020-02-29 11:17:39
【问题描述】:

所以,我正在开发一个API,使用slim/slimleague/oauth2-server 来管理OAuth2 连接。 OAuth2 会很有用,因为我需要在服务之间使用 Client Credentials grant

然后,我也在用 React Native 开发一个混合应用程序。此应用程序将要求用户使用电子邮件和密码登录或连接其他服务(如 Facebook、Google、Twitter 等)。

我对在这种情况下使用什么 OAuth2 流程感到困惑。网上有很多文章说 Resource Owner Password Credentials 不再安全,我们应该改用 Authentication Code with PKCE

但我无法发现或理解如何在第一方应用程序中使用 PKCE 应用身份验证代码,因为所有有关您的文档都需要使用浏览器来获取 redirect_uri 中的身份验证代码。

我想象的流程是这样的:

  1. 用户打开应用,然后插入您的凭据usernamepassword
  2. 此屏幕将连接到 API /request_token URI 发送 { 'grant_type': 'password', 'username': username, 'password': password, 'client_id': CLIENT_ID },考虑到它是一个公共应用程序,我们无法发送 client_secret
  3. API 验证凭据并返回一些数据,例如{ "access_token": access_token, "token_type": "JWT", "expires_in": LIFE_SPAN },这里我们将使用 JWT 来生成基于public/private keyaccess_token
  4. 身份验证完成,应用程序将在 access_token 处于活动状态时存储它,当它过期时将流向 refresh_token

我的问题:它安全吗? Scott Brady did some "aggressive" article talking it's NEVER safe.

应用程序是如何做到这一点的?例如,当我使用 Instagram 应用程序时,他们拥有应用程序和 API,我在用户体验流程中不需要浏览器。现代应用程序是否使用“资源所有者密码凭据”或“使用 PKCE 的身份验证代码”?使用“带有 PKCE 的身份验证代码”时,是否可以避免在流程中插入浏览器?

[编辑] 可能的解决方案

正如 Gary Archer 所说,“建议使用 PKCE 进行身份验证代码流程 - 以及通过系统浏览器登录”,但我们并不是在谈论授予访问用户数据或第三方应用程序的权限。

作为设计师,我不同意在同一 API 所有者拥有的第一方应用程序中登录需要浏览器,这不是我们正在寻找的用户体验。以及我们看到的所有应用,例如 Instagram、Facebook、Uber……我们只需输入您的用户名和密码,就可以访问您的帐户。

我要做的是创建一个自定义版本的身份验证代码,PKCE 删除 required_uri

[EDIT:2] 新流程

经过大量搜索,我找到了一些我认为很有趣的答案。如上所述,我从流中删除了redirect_url。看:

  1. 当用户提供您的凭据时,流程从登录屏幕开始;
  2. 客户端生成code_verifier,然后将code_verifier 散列为code_challenge,并使用以下参数将其发送到授权服务器:

    • response_type=code :表示您的服务器希望收到授权码。
    • client_id=xxxx :客户端 ID。
    • client_integrity=xxxx :第一方应用的应用完整性检查。
    • code_challenge=xxxx :如前所述生成的代码质询。
    • code_challenge_method=S256 :plain 或 S256,取决于质询是普通验证字符串还是字符串的 SHA256 哈希。如果省略此参数,则服务器将假定为plain。
    • username=xxxx : 用于认证的用户名。
    • password=xxxx :密码的哈希版本。
    • state=xxxx :由您的应用程序生成的随机字符串(CSRF 保护)。
  3. 授权服务器将验证用户身份验证,存储 code_challenge 并返回带有 client_tokenauthorization_code

  4. 客户端收到aauthorization_codeclient_token后,保存client_token并立即将authorization_code发送回授权服务器,参数如下:

    • grant_type=authorization_code :表示此令牌请求的授权类型。
    • code=xxxx : 客户端将获取到的授权码发送出去。
    • client_id=xxxx :客户端 ID。
    • code_verifier=xxxx : PKCE 请求的代码验证器,客户端在授权请求之前最初生成的。
  5. 授权服务器将验证所有数据,如果一切正常,将返回access_token

  6. 客户端将使用access_token 设置Authorization 标头,并始终向每个请求发送client_token,只有两个值都正确时才会被接受;
  7. 如果access_token过期,客户端会请求刷新access_token并获取一个新的。

现在,我将把这个逻辑重现到 PHP 语言中。如果一切顺利,我希望它是正确的,我会带着明确的答案回来。

[编辑] 澄清

我正在使用 OAuth2 来连接您的第三方帐户(Google、Facebook 等)。但用户也可以登录到我数据库中的本地帐户。对于这种情况,用户根本不需要授予任何东西。因此,将用户发送到浏览器让您登录是没有意义的。

我想知道,对于这种情况,本地帐户是否可以使用 资源所有者密码凭据 或更安全的 PKCE 身份验证代码(我们已经得出结论它更好接近)。但是使用 PKCE 的身份验证代码需要 redirect_uri,我是否需要使用此重定向将用户登录到不需要授予访问权限的本地帐户?

【问题讨论】:

  • 这是一个典型的 IAM 陷阱,开发人员想出自己的自定义 OAuth 实现。不要那样做。拳头问为什么您的解决方案中需要 OAuth。然后看看你如何使用令牌。我相信您可以使用定义的授权类型来适应您的场景。您能否设计独立于 ROPC 的架构?
  • 我只是不想使用浏览器登录应用程序。我拥有数据库、api 和应用程序。用户在我的数据库中有用户名/密码,他将使用官方应用程序进行连接。我想使用“带有 PKCE 的身份验证代码”,但它需要浏览器,在我的场景中它对我没有意义。
  • 我需要 OAuth2,因为我们将使用客户端凭据授权类型与 client_id/client_secret 进行一些连接。我只是想知道我是否真的需要在第一方应用程序中使用 OAuth2 来登录他们的帐户。 “资源所有者密码凭据”是解决此问题的方法。但是大家都说我们需要使用“Authentication Code with PKCE”,但是这个approuch更适合第三方应用...

标签: oauth-2.0


【解决方案1】:

那我们走吧。经过大量研究,我发现了一些我将应用并且可能正常工作的方法。因此,首先,挑战如下:

  • 您绝不能信任在客户端运行的客户端。有很多担忧,您的应用程序可能会被反编译、修改,用户设备可能带有恶意软件,或者连接可能会受到中间人攻击 (MITM) 的影响...
  • 即使使用 OAuth2,API 服务器也只能识别 正在访问资源,而不能识别 WHAT 正在访问。因此,任何敏感信息都是危险的,任何东西都可以窃取和使用它。
  • 资源所有者密码凭据是 OAuth2 协议的一部分,用于授权资源所有者访问您的资源。所以,它不会成为身份验证过程的一部分,如果你这样对待它,你会毁掉它;
  • 通过使用 ROPC 授权类型,无法知道资源所有者是否真的发出了该请求,是什么让网络钓鱼攻击变得“容易”。提醒“你知道 WHO 而不是 WHAT”。最后,这种授权使任何假设用户身份的事情变得容易;
  • 这种授权类型也违背了 OAuth2 的提议,因为 OAuth 试图避免使用密码来访问资源。这就是为什么很多人说不要使用它的原因;
  • 为了加强,重要的是要强调 ROPC 不是对用户进行身份验证,而只是授权他访问资源服务器。
  • 是的,ROPC 允许刷新令牌,但有两个问题:首先,客户端每次需要重新提供凭据才能获取新令牌;其次,如果使用长期访问代码,那么事情会变得更加危险。

为了防止恶意事物任意使用用户凭据,有访问令牌。它们替换密码并且需要在短时间内刷新。这就是为什么它们比 HTTP 基本身份验证要好得多。

这就是为什么建议在现代应用程序中使用带有 PKCE 的身份验证代码,它提供了使用 OAuth2 协议的所有功能和优势。但是,这里出现了一个冗长的讨论,甚至是开发者社区的问题:

为了获得验证码,一些用户需要在浏览器中登录,授予访问权限,重定向回客户端,很快,客户端将收到一个代码来交换访问令牌。

此方案效果很好,需要用于第三方应用。但是,如果它是第一方应用程序呢?当您拥有包含用户数据的数据库并拥有“受信任”应用程序时,重定向用户没有任何意义。对吧?

此时,我的问题是:如何在不重定向用户的情况下使用 AuthCode (PKCE) 流程?而且,再次强调,谈论 OAuth2 协议始终与“授予客户端访问资源服务器”相同(授权,而不是身份验证)。

所以真正的问题是:为什么授权码需要重定向?然后,我得到了以下答案:

此流程需要知道客户端凭据和用户共识才能返回授权码。

这就是我在编辑中出错的原因。 OAuth2 协议不需要更改(对不起,我有不同的想法)。出于这个原因,OAuth2 需要的是在您的层之上的授权中介。因此,授权码不会返回给客户端,而是返回给授权中介,最终将其返回给客户端。有意义吗?

它将如何工作?那么,将需要 4 个不同的“核心”:

  1. 身份验证服务器:将负责对用户凭据和客户端身份进行身份验证。主要目标是证明“WHO 是用户,WHAT 正在连接以获取身份验证”;
  2. Authorization Mediator(OAuth2 之上的一层):将验证客户端的唯一身份,以确保客户端/用户“知道”并可以获得访问令牌;
  3. 授权服务器:作为 OAuth2 实现的一部分,没有任何改变。将授权客户端获取您的授权码、访问令牌和刷新令牌;
  4. 资源服务器:将允许通过访问令牌访问资源。

然后,我们可以考虑的安全技术:

  1. API 密钥:每个应用程序(客户端)都将拥有您自己的 API 密钥,以及与这些密钥关联的权限范围。通过使用它,您可以收集有关 API 使用情况的基本统计信息。大多数 API 服务使用统计信息来强制每个应用程序的速率限制,以提供不同的服务层或拒绝可疑的高频调用模式;
  2. Mutual SSL Authentication:通过使用这种技术客户端和服务器交换和验证彼此的公钥。验证密钥后,客户端和服务器会协商共享密钥、消息验证码 (MAC) 和加密算法;
  3. HMAC:API 密钥将分为 ID 和共享密钥。然后,和以前一样,ID 与每个 HTTP 请求一起传递,但共享密钥用于对传输中的信息进行签名、验证和/或加密。客户端和服务器将使用 HMAC SHA-256 等算法交换共享密钥;
  4. 保护代码应用程序:使用代码混淆器会使从应用程序中定位和提取敏感数据变得更加困难,例如秘密共享、api 密钥、公钥......
  5. 处理用户凭据:提供一种简单的方法让用户登录并证明您的身份。插入有效凭据后,服务器可以返回用户令牌 (JWT) 并以此模拟用户会话。

让我们看看流程:

  • 第一部分:验证用户和客户端;

    1. 在客户端将用户凭据(例如{ email, mobile_number, hash ( password ), verification_method })发送到身份验证服务器路由/login后,用户将输入您的凭据并被要求使用您的电子邮件或手机号码证明您的身份;
    2. 身份验证服务器将验证用户凭据并向用户发送一次性密码以确认您的身份(电子邮件或手机号码由用户选择);
    3. 然后,用户将收到的OTP插入,客户端将发送回Authentication Server路由/login-otp,包括验证方法(如{ otp, verification_method });
    4. 最后,Authentication Server 将返回一个{ hash ( shared_secret ) } 以供尽快使用。
  • 第二部分:授权 API 访问;

    1. 当收到shared_secret时,客户端将安全地存储在移动应用程序中,然后它将使用PKCE调用/auth{ response_type, client_id, scope, state, code_challenge, code_challenge_method }请求授权码,授权服务器将验证凭据并返回授权码,没有重定向;
    2. 稍后,客户端会将收到的代码交换为访问/token 的访问令牌,但需要发送一些额外的数据:{ payload: { grant_type, code, client_id, code_verifier }, timestamp, hash ( some_user_data + timestamp + shared_secret ) }
    3. Authorization Mediator 将接收此请求并验证尝试生成用户生成的相同哈希。并将所有数据重定向到授权服务器,该服务器将验证 client_idcodecode_verifier 并使用访问令牌进行响应;
    4. 这个新的access_token 将返回给授权中介,然后返回给授予对 API 资源访问权限的客户端。
  • 第三部分:访问资源服务器;

    1. 客户端每次都需要向 API /api 发送一个调用,其中包含 Authorization 标头和一些带有 { timestamp, hash ( some_user_data + timestamp + shared_secret ) } 的额外数据;
    2. Authorization Mediator 将验证 shared_secret 哈希,调用资源服务器验证 access_token 并返回数据。
  • 第四部分:刷新访问令牌;

    1. 访问令牌过期后,客户端将向/refresh-token 发送一个调用,其中包含Authorization 标头和一些带有{ payload: { grant_type, refresh_token, client_id, scope }, timestamp, hash ( some_user_data + timestamp + shared_secret ) } 的额外数据;
    2. Authorization Mediator 将验证 shared_secret 哈希,调用 Authorization Server 并返回新的令牌访问权限。

此流程的视觉图像:

我不认为这是一个完美的策略,但它用 PKCE 将资源所有者密码凭据替换为身份验证代码,并提供了一些额外的安全技术。它比单一且简单的身份验证方法要好得多,它保留了 OAuth2 协议,并且更难以破坏用户数据。

一些参考和支持:

How do popular apps authenticate user requests from their mobile app to their server?

Why does your mobile app need an API key?

Mobile API Security Techniques

Secure Yet Simple Authentication System for Mobile Applications: Shared Secret Based Hash Authentication

【讨论】:

    【解决方案2】:

    推荐使用 PKCE 验证代码流程 - 以及通过系统浏览器登录。还推荐使用 AppAuth 模式。 https://curity.io/resources/develop/sso/sso-for-mobile-apps-with-openid-connect/

    但实施起来既棘手又耗时 - 因此您需要考虑一下 - 有时使用更便宜的选项就足够了。取决于暴露数据的敏感性。

    如果有帮助,这里有一些关于我的 Android 演示应用程序的注释,它也关注可用性 - 以及指向您可以运行的代码示例的链接: https://authguidance.com/2019/09/13/android-code-sample-overview/

    【讨论】:

    • 我想这个流程适用于第三方应用程序,毕竟用户需要授予对您帐户的访问权限。但是,即使使用受信任的第一方应用程序,我们也需要使用浏览器进行连接?例如,Instagram 使用什么样的 API 连接来登录应用程序?
    • 我想我明白你的意思了!也许我可以通过使用公钥/私钥和用户凭据来连接这个网络服务,我不需要用 OAuth2 来做这个,我理解 OAuth2 是第三方应用程序的更好方法。
    【解决方案3】:

    首先,不要仅仅因为您需要在您的应用程序中采用它而发明 OAuth 授权。维护起来会很复杂。

    在您的场景中,您需要提供社交登录(例如:- 通过 Google、Facebook 登录)。这当然是必须支持的所需功能。但这并不限制您通过自定义注册过程获取最终用户凭据。这有很多原因,例如不是每个人都使用社交媒体或谷歌帐户。有时人们更喜欢注册而不是分享其他服务的用户标识符(是的,这是社交登录的另一端)。

    所以继续,提供社交登录。首次通过外部身份服务器(例如:谷歌)登录时存储用户标识符。而且,使用密码和电子邮件进行良好的旧注册步骤。

    【讨论】:

    • 我想知道如何使用 PKCE 的身份验证代码来将我自己的用户登录到我自己的应用程序。而已。我的应用程序连接到我的 API 服务器。我不是在谈论社交登录,那么在这种情况下,是的,我的应用程序将使用 OAuth2,连接到 Facebook 用户帐户,将他重定向到浏览器并授予权限。但是对于用户在我的数据库中登录它的凭据,我不需要redirect_uri,因为用户不需要授予访问权限。带有 PKCE 的 AuthCode 需要 redirect_uri
    • @caiquearaujo 这正是我的观点。阅读您的问题后,我认为您不需要复杂的拨款。您可以简单地使用没有重定向的登录页面。或多或少这个 ROPC 拨款。是的,有文章说这很糟糕,但如果你真的想避免,我看不出有任何理由避免它。
    猜你喜欢
    • 2014-08-05
    • 2018-05-25
    • 2013-11-23
    • 2014-09-02
    • 2018-02-15
    • 2014-05-11
    • 2015-05-30
    • 1970-01-01
    • 2013-09-09
    相关资源
    最近更新 更多