常见OAuth漏洞
2025年1月30日 - 作者:Jose Catalan, Szymon Drosdzol
OAuth2的流行使其成为攻击者的主要目标。虽然它简化了用户登录,但其复杂性可能导致配置错误,从而产生安全漏洞。一些更复杂的漏洞不断重现,因为协议的内部工作机制并不总是被很好理解。为了改变这种状况,我们决定编写一份针对OAuth实现的已知攻击的综合指南。此外,我们还创建了一份全面的检查清单,对测试人员和开发人员快速评估其实现是否安全都将非常有用。
立即下载OAuth安全备忘单!Doyensec_OAuth_CheatSheet.pdf
OAuth简介
OAuth术语
OAuth是一个具有多个参与者和移动部件的复杂协议。在我们深入探讨其内部工作机制之前,让我们回顾一下其术语:
资源所有者:可以授予受保护资源访问权限的实体。通常是最终用户。 客户端:代表资源所有者请求访问受保护资源的应用程序。 资源服务器:托管受保护资源的服务器。这是您想要访问的API。 授权服务器:对资源所有者进行身份验证并在获得适当授权后颁发访问令牌的服务器。例如,Auth0。 用户代理:资源所有者用于与客户端交互的代理(例如,浏览器或本机应用程序)。
参考资料
- OAuth 2.0 RFC:https://datatracker.ietf.org/doc/html/rfc6749
- OAuth术语:#oauth-2-0-terminology
OAuth常见流程
针对OAuth的攻击依赖于挑战授权流程所建立的各种假设。因此,理解这些流程对于有效攻击和防御OAuth实现至关重要。以下是最流行流程的高级描述。
隐式流程
隐式流程最初是为无法安全存储客户端凭据的本机或单页应用程序设计的。然而,现在不鼓励使用它,并且它未包含在OAuth 2.1规范中。尽管如此,它在Open ID Connect (OIDC)中仍然是检索id_tokens的可行身份验证解决方案。
在此流程中,用户代理被重定向到授权服务器。在执行身份验证和同意后,授权服务器直接返回访问令牌,使其可供资源所有者访问。这种方法将访问令牌暴露给用户代理,可能通过XSS或有缺陷的redirect_uri验证等漏洞受到威胁。如果response_mode未设置为form_post,隐式流程会将访问令牌作为URL的一部分传输。
参考资料
- https://datatracker.ietf.org/doc/html/rfc6749#section-1.3.2
- https://datatracker.ietf.org/doc/html/rfc6749#section-4.2
- https://auth0.com/docs/get-started/authentication-and-authorization-flow/implicit-flow-with-form-post
授权码流程
授权码流程是Web应用程序中使用最广泛的OAuth流程之一。与直接向授权服务器请求访问令牌的隐式流程不同,授权码流程引入了一个中间步骤。在此过程中,用户代理首先检索授权码,然后应用程序将该授权码与客户端凭据一起交换为访问令牌。这个额外的步骤确保只有客户端应用程序能够访问访问令牌,防止用户代理看到它。
此流程仅适用于机密应用程序,例如常规Web应用程序,因为应用程序客户端凭据包含在代码交换请求中,并且必须由客户端应用程序安全存储。
参考资料
- https://datatracker.ietf.org/doc/html/rfc6749#section-1.3.1
- https://datatracker.ietf.org/doc/html/rfc6749#section-4.1
- https://cloudentity.com/developers/basics/oauth-grant-types/authorization-code-flow/
带PKCE的授权码流程
OAuth 2.0提供了一个使用代码交换证明密钥(PKCE)的授权码流程版本。此OAuth流程最初是为无法存储客户端密钥的应用程序(例如本机或单页应用程序)设计的,但已成为OAuth 2.1规范中的主要推荐。
在默认授权码流程中添加了两个新参数:一个随机生成的值称为code_verifier及其转换版本code_challenge。
首先,客户端创建并记录一个秘密的code_verifier,并推导出一个转换版本t(code_verifier),称为code_challenge,它与所使用的转换方法t_m一起在授权请求中发送。 然后,客户端在访问令牌请求中发送授权码和code_verifier秘密。 最后,授权服务器转换code_verifier并将其与t(code_verifier)进行比较。
可用的转换方法(t_m)如下:
- plain:code_challenge = code_verifier
- S256:code_challenge = BASE64URL-ENCODE(SHA256(ASCII(code_verifier)))
请注意,使用带有自定义redirect_uri方案(如example.app://)的默认授权码流程可能允许恶意应用程序在合法OAuth 2.0应用程序旁边将自身注册为此自定义方案的处理程序。如果发生这种情况,恶意应用程序可以拦截授权码并将其交换为访问令牌。有关更多详细信息,请参阅OAuth重定向方案劫持。
使用PKCE,拦截授权响应将不允许先前的攻击场景,因为攻击者只能访问authorization_code,但他们无法获取访问令牌请求中所需的code_verifier值。
下图说明了带PKCE的授权码流程:
[流程图示意图]
参考资料
客户端凭据流程
客户端凭据流程专为机器对机器(M2M)应用程序设计,例如守护进程或后端服务。当客户端也是资源所有者时,它非常有用,无需用户代理身份验证。此流程允许客户端通过提供客户端凭据直接检索访问令牌。
下图说明了客户端凭据流程:
[流程图示意图]
参考资料
- https://datatracker.ietf.org/doc/html/rfc6749#section-1.3.4
- https://datatracker.ietf.org/doc/html/rfc6749#section-4.4
设备授权流程
设备授权流程专为缺乏基于用户代理授权的浏览器或输入过于受限而无法在授权流程中进行基于文本的身份验证的互联网连接设备设计。
此流程允许设备(如智能电视、媒体控制台、数码相框或打印机)上的OAuth客户端使用单独设备上的用户代理获取用户授权以访问受保护资源。
在此流程中,首先客户端应用程序从授权服务器检索用户代码和验证URL。然后,它指示用户代理使用提供的用户代码和验证URL在不同设备上进行身份验证和同意。
下图说明了设备授权码流程:
[流程图示意图]
参考资料
- https://datatracker.ietf.org/doc/html/rfc8628
- https://auth0.com/docs/get-started/authentication-and-authorization-flow/device-authorization-flow
资源所有者密码凭据流程
此流程要求资源所有者完全信任客户端及其对授权服务器的凭据。它设计用于无法使用基于重定向的流程的用例,尽管它在最近的OAuth 2.1 RFC规范中已被删除,并且不推荐使用。
不是将资源所有者重定向到授权服务器,而是将用户凭据发送到客户端应用程序,然后客户端应用程序将其转发到授权服务器。
下图说明了资源所有者密码凭据流程:
[流程图示意图]
参考资料
- https://datatracker.ietf.org/doc/html/rfc6749#section-4.3
- https://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-1-01#name-differences-from-oauth-20
攻击
在本节中,我们将介绍针对OAuth的常见攻击以及基本的修复策略。
CSRF
OAuth CSRF是一种针对OAuth流程的攻击,其中使用授权码的浏览器与启动流程的浏览器不同。攻击者可以利用它强迫受害者使用其授权码,导致受害者连接到攻击者的授权上下文。
考虑下图:
[攻击流程图]
根据应用程序的上下文,影响可能从低到高不等。在任何情况下,确保用户控制其操作所在的授权上下文并且不能被强迫进入另一个上下文至关重要。
缓解措施 OAuth规范建议利用state参数来防止CSRF攻击。 [state是]客户端用于在请求和回调之间保持状态的不透明值。授权服务器在将用户代理重定向回客户端时包含此值。该参数应用于防止跨站请求伪造(CSRF)。
下图说明了state参数如何防止攻击:
[防护流程图]
参考资料
- Justin Richer, Antonio Sanso, (2017), OAuth 2 In Action
重定向攻击
良好实现的授权服务器在将用户代理重定向回客户端之前验证redirect_uri参数。redirect_uri值的允许列表应按客户端配置。这种设计确保用户代理只能重定向到客户端,并且授权码仅披露给给定的客户端。相反,如果授权服务器忽略或错误实现此验证,恶意行为者可以操纵受害者完成一个流程,将其授权码披露给不受信任的方。
在最简单的形式中,当完全缺少redirect_uri验证时,可以利用以下流程进行利用:
[攻击流程图]
当验证实施不当时,此漏洞也可能出现。唯一正确的方法是通过比较确切的redirect_uri(包括来源(方案、主机名、端口)和路径)进行验证。
常见错误包括:
- 仅验证来源/域
- 允许子域
- 允许子路径
- 允许通配符
如果给定来源包含具有开放重定向漏洞的URL或具有用户控制内容的页面,则可以通过Referer头或开放重定向滥用它们来窃取代码。
另一方面,以下疏忽:
- 部分路径匹配
- 误用正则表达式匹配URI
可能导致通过制作恶意URL的各种绕过,这些URL将导致不受信任的来源。
参考资料
- Justin Richer, Antonio Sanso, (2017), OAuth 2 In Action
可变声明攻击
根据OAuth规范,用户由sub字段唯一标识。但是,此字段没有标准格式。因此,根据授权服务器的不同,使用许多不同的格式。一些客户端应用程序为了在多个授权服务器之间构建统一的用户识别方式,回退到用户句柄或电子邮件。但是,根据所使用的授权服务器,这种方法可能是危险的。一些授权服务器不保证此类用户属性的不可变性。更糟糕的是,在某些情况下,这些属性可以由用户自己任意更改。在这种情况下,可能会发生账户接管。
当实现"使用Microsoft登录"功能以使用email字段识别用户时,会出现这种情况。在这种情况下,攻击者可能在自己的Azure上创建AD组织(本例中为doyensectestorg),然后可用于执行"使用Microsoft登录"。虽然对于给定用户而言,放置在sub中的对象ID字段是不可变的且无法伪造,但email字段纯粹是用户控制的,不需要任何验证。
[截图示例]
在上面的屏幕截图中,有一个创建的用户示例,可用于接管客户端中victim@gmail.com的账户,该客户端使用email字段进行用户识别。
参考资料
- https://learn.microsoft.com/en-us/entra/identity-platform/claims-validation#validate-the-subject
- https://www.descope.com/blog/post/noauth
客户端混淆攻击
当应用程序实现OAuth隐式流程进行身份验证时,它们应验证最终提供的令牌是否是为该特定客户端ID生成的。如果未执行此检查,攻击者可能使用为不同客户端ID生成的访问令牌。
想象攻击者创建一个公共网站,允许用户使用Google的OAuth隐式流程登录。假设有数千人连接到托管的网站,攻击者将可以访问他们为攻击者网站生成的Google OAuth访问令牌。
如果这些用户中的任何一个在未验证访问令牌的易受攻击网站上已有账户,攻击者将能够提供为不同客户端ID生成的受害者访问令牌,并能够接管受害者的账户。
安全实现的用于身份验证的OAuth隐式流程如下:
[安全流程图]
如果未执行步骤8到10且未验证令牌的客户端ID,则可能执行以下攻击:
[攻击流程图]
修复措施 值得注意的是,即使客户端使用更安全的流程(例如,显式流程),它也可能接受访问令牌 - 有效地允许降级到隐式流程。此外,如果应用程序使用访问令牌作为会话cookie或授权头,则可能易受攻击。实际上,确保永远不接受来自用户控制参数的访问令牌可以尽早打破利用链。除此之外,我们建议执行上述步骤8到10中描述的令牌验证。
参考资料
范围升级攻击
使用授权码授予类型,用户数据通过安全的服务器到服务器通信进行请求和发送。
如果授权服务器接受并隐式信任访问令牌请求中发送的scope参数(注意,此参数未在RFC中为授权码流程中的访问令牌请求指定),恶意应用程序可能尝试通过