为什么不鼓励 PKCE 用于单页应用程序?

信息安全 oauth oauth2 打开ID 开放式连接
2021-08-19 20:26:06

在开发单页应用程序时,今天的许多服务仍然推荐 OpenID Connect/Oauth2 令牌交换的隐式流程。(见Okta - 现在推荐 PKCE w/implicit fallback, Google , Auth0

一些较新的指导指向在令牌交换步骤中使用没有 client_secret 的授权代码流,我同意这是有道理的,因为文章中引用了原因(例如,令牌不在浏览器历史记录、网络日志等中。 )。为什么这个概念没有在 PKCE 中更进一步?与其完全省略 client_secret,不如像本机应用程序推荐的那样使用动态的?SPA 和 Native Apps 都被认为是“公共客户端”,无法安全地保存秘密,但只有 Native Apps 获得 PKCE 推荐,而 SPA 似乎被保留在过去。

我了解 PKCE 试图解决的安全隐患与仅浏览器的上下文没有直接关系,但这难道不能被视为一种纵深防御机制并无论如何都可以使用吗?我从经验中知道,Google 不会让您生成 Web 应用程序凭据并尝试对 SPA 使用除隐式以外的任何内容(请参阅此问题),授权代码流程将需要 client_secret 并且 PKCE 交换仅在您选择本机时才有效应用程序凭据类型,但您不能为您的应用程序指定 https redirect_uri。

其他提供商允许 PKCE 在 Web 上下文中使用授权代码流,但他们不建议这样做。我想要这个并利用它有错吗?使用可用Web Crypto API针对较新的浏览器并根据需要进行填充)添加额外的步骤来生成和传递代码挑战似乎相当简单

3个回答

@catanman 在 SPA 中围绕 PKCE 的技术考虑方面提出了很好的观点,但就在最近,IETF Oauth 工作组发布了一份最佳当前实践文档(2018 年 12 月 28 日),指出:

注意:虽然到目前为止 PKCE 被推荐作为保护本机应用程序的机制,但该建议适用于所有类型的 OAuth 客户端,包括 Web 应用程序。

虽然所有其他答案都是正确的,但最新的OAuth 2.0 for Browser-Based Apps Best Practices Doc(2019 年 1 月 29 日)指出(强调我的):

  1. 概述

对于在基于浏览器的应用程序中授权用户,
当前的最佳做法是

o 使用带有 PKCE 扩展的 OAuth 2.0 授权代码流

...

7.1。从基于浏览器的应用程序发起授权请求

基于浏览器的公共应用程序必须为 OAuth 实现代码
交换证明密钥 (PKCE [RFC7636])
扩展,并且授权
服务器必须支持此类客户端的 PKCE。

PKCE 扩展通过为授权服务器提供一种方法来验证交换授权代码的同一客户端实例与启动流程的客户端实例相同,从而防止恶意客户端拦截授权代码并交换访问令牌的攻击.

我认为有一些小好处值得作为纵深防御,即使是基于浏览器的应用程序也是如此。

SPA 不会从 PKCE 中受益。PKCE 解决的问题与您描述的问题不同。

首先,对于SPA,当前的最佳实践仍然是使用隐式流,而不是授权码流。对于隐式流,访问令牌包含在重定向 URI的哈希片段 (#)中,而不是包含在查询组件 (?) 中。由于浏览器在发出请求时从不包含 URI 的哈希片段部分,因此令牌不会出现在浏览器历史记录、网络日志中......

谈到本机应用程序时,rfc8252 第 6节说明如下:

公共本机应用程序客户端必须实现代码交换的证明密钥 (PKCE RFC7636])


在旁注中,请注意 PKCE 要求适用于公共本地客户端。您可能想知道原生应用程序如何公开。答案在第 8.4 节

除非使用动态客户端注册 [RFC7591] 之类的机制来提供每个实例的机密,否则本机应用程序被归类为公共客户端

我不确定如何引用这些客户端,因为我从未见过任何地方提到过机密的本地客户端。也许非公共本机客户端可以工作:)


回答您的问题:那么为什么公共本机应用程序而不是 SPA 需要使用 PKCE 的授权代码流?

逻辑如下:

  1. OAuth2 的主要目的之一是防止用户凭据暴露给客户端。
  2. 要使本机应用程序满足此要求,用户不得在本机应用程序可以访问的任何地方输入凭据。因此,必须避免使用本机登录屏幕和 web 视图。
  3. 解决方案是让原生应用程序启动一个外部用户代理(参见rfc8252 附录 B),用户将在其中向授权服务器进行身份验证并授权应用程序。本机应用程序无法访问外部用户代理,因此用户的凭据是安全的。
  4. 但是,引入了 SPA 中不存在的新复杂情况:外部用户代理现在必须将授权服务器的响应传回本机应用程序?答案是应用间通信(参见第 5节和第 7 节
  5. 不幸的是,恶意的 3rd 方应用程序可以拦截多种类型的应用程序间通信!对于隐式流程,这意味着访问令牌可能会被恶意应用程序窃取,这将是一件非常糟糕的事情。这是原生应用不使用隐式流的主要原因之一。
  6. 但是即使使用授权码流,恶意的第 3 方应用程序仍然可以拦截授权码,并使用它来获取访问令牌,因为没有客户端密码。因此对于公共原生应用程序使用授权代码流而不是隐式流似乎没有用。
  7. 这就是 PKCE 的用武之地。PKCE 使得即使恶意应用程序拦截了授权代码,它也无法将其交换为访问令牌。这是通过要求请求访问令牌的实体证明它是首先请求授权代码的同一实体来实现的。

希望现在你明白为什么:

  • SPA 根本不会从 PKCE 中受益,因为有了 SPA,一切都发生在浏览器中。PKCE 仅在存在应用程序间通信时才有用。
  • 公共本机应用程序不应使用隐式流,因为应用程序间通信有时可能不安全。
  • 公共原生应用应使用授权码流 + PKCE

这是一篇很好的博客文章,可以提供更多信息: https ://medium.com/@justinsecurity/mobile-apps-and-oauths-implicit-flow-68e72c6515a1