无秘密的 OAuth 2.0 授权码授予

Posted

技术标签:

【中文标题】无秘密的 OAuth 2.0 授权码授予【英文标题】:OAuth 2.0 Authorization Code Grant without secret 【发布时间】:2017-01-10 17:08:37 【问题描述】:

因此,我一直在考虑针对使用 Azure AD 的自定义 API 使用 Microsofts cordova-plugin-ms-adal plugin(使用本机库)为 Cordova 移动应用程序设置 OAuth 2.0。这一切都很好,但我对秘密的使用(或更具体地说是它的缺失)有点困惑。

在网络上的许多文章中,他们声明在使用授权码授予并请求令牌时,您包含秘密。并且当您可以安全地存储秘密时,这种授权类型非常适合使用,例如在服务器上。

然而插件不需要在应用程序中指定秘密(这是正确的),但它仍然使用授权码授予来进行身份验证。我也可以手动调用

https://login.windows.net/common/oauth2/authorize?resource=http://***.onmicrosoft.com/***API&client_id=***&response_type=code&redirect_uri=http://***.onmicrosoft.com/***App

在我的浏览器中,登录,获取代码,然后使用 POST 到https://login.windows.net/common/oauth2/token

grant_type: authorization_code
client_id: ***
code: ***
redirect_uri: http://***.onmicrosoft.com/***App
resource: http://***.onmicrosoft.com/***API

它可以工作,所以我得到了一个有效的 JWT,无需发送秘密

为什么!?这不安全吗? (我还注意到OAuth 2.0 spec section 4.1.3 并没有说明授予类型授权代码需要秘密!?)

使用没有秘密/基本身份验证标头的授权类型授权码有什么影响?

【问题讨论】:

你能告诉我你是如何在浏览器登录中获取代码的,你是否使用 javascript 或任何服务器端语言进行了 POST。我正面临这个问题,需要你的帮助!!!! @SouravDas 为了更好地理解我手动调用端点。一旦您在浏览器中调用第一个 URL 并登录,它就会重定向到您使用 ?code= 参数配置的 redirect_uri。当您 POST 到令牌端点时使用此值 - 我使用 getpostman.com 进行测试。在我的实际代码中,我有 cordova-plugin-ms-adal 插件设置来为我处理这个! 感谢您的回复。我们使用 ADFS 而不是 AZAD,所以我想我必须写完整个东西! 【参考方案1】:

对所谓的机密客户端(具有客户端机密的客户端)使用授权码授权确实比使用公共客户端更安全。

这是因为授权代码本身的交换是作为前端通道中的 URL 参数发生的,即通过浏览器,因此相对容易受到跨脚本、点击劫持、网络/DNS 操纵等攻击。意味着在某些情况下(粗心的用户、攻击者网络控制、服务器实现中粗心的重定向 URI 匹配等),专门的攻击者有可能从用户那里窃取授权码。

要交换访问令牌的授权代码,机密客户端必须在授权代码旁边的受 HTTPs 保护的调用上提供客户端机密,而公共客户端没有任何方法来确保它是真的是指定的客户。

这意味着攻击者相对很容易模仿公共客户端,因为这只需要非机密信息(他可以从自己的浏览器中获取client_idredirect_uri ) 和授权code,他可以通过上述攻击获取。

虽然获取机密客户端的授权码以相同的方式工作,但攻击者无法使用它并将其交换为访问令牌,因为为此他需要一个客户端机密,而该客户端机密通常更难获得攻击者。秘密通常存储在后端存储中的服务器上,并且仅通过安全的 HTTPs 通道进行通信,因此不会泄漏。

【讨论】:

【参考方案2】:

grant_type=authorization_code(或任何其他流程)与公共客户端(没有秘密或以任何其他方式进行身份验证的客户端)一起使用的含义是授予的访问令牌不代表client直接访问资源,代表客户端代表用户访问资源的授权。

这就是为什么您会在 Azure AD 中注意到,当您注册本机客户端应用程序(公共客户端)时,您只能将其配置为具有对资源的委派权限,而不是仅限应用程序的权限。

【讨论】:

【参考方案3】:

无需发送秘密。

不正确。在link you posted 中,请求还包括:

Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW

也许你没有注意到。这表明client_secret 使用 HTTP 基本身份验证以编码形式传输。

由服务器决定客户端如何进行身份验证(如有必要)。来自规范:

如果客户类型是保密的或客户是发给客户的 凭据(或分配的其他身份验证要求), 客户端必须按照所述向授权服务器进行身份验证 在第 3.2.1 节中。

第 3.2.1 节指向第 2.3 节(严重),其中说:

拥有客户端密码的客户端可以使用 HTTP Basic [RFC2617] 中定义的身份验证方案,用于进行身份验证 授权服务器。

客户端密码也就是客户端密码。

所以秘密正在传输。

【讨论】:

谢谢托马斯。我承认我在规范示例中没有注意到这一点!我想我的问题是:在没有秘密/基本身份验证标头的情况下使用授权类型的授权代码有什么影响? (我已经更新了我的问题以反映这一点。)

以上是关于无秘密的 OAuth 2.0 授权码授予的主要内容,如果未能解决你的问题,请参考以下文章

如何从单页应用程序实现 OAuth 2.0 授权码授予?

在 Angular 中实现 OAuth 2.0 授权代码授予

如何使用 Angular 2 发送 OAuth2 的客户端 ID 和秘密 ID?

Spring Security OAuth 2.0 - 授权代码授予始终需要客户端密码

OAuth中授权码的用途是啥

“错误:” “Unsupported_grant_type” 使用 OAuth 2.0,Owin。密码授予和授权授予