### 漏洞概述 该漏洞涉及OAuth2提供商在处理用户邮箱验证时的严格性不足。攻击者可以利用未验证的邮箱来合并身份,从而获取对受害者账户的完全控制权。 ### 影响范围 - **受影响的服务**:Azure AD v1、Facebook、Spotify、Twitch、Twitter、Windows Live、WorkOS等。 - **受影响的功能**:OAuth适配器中的邮箱验证逻辑。 - **潜在风险**:攻击者可以通过未验证的邮箱合并身份,从而获得对受害者账户的完全控制权。 ### 修复方案 1. **引入三态邮箱验证状态**: - 引入`EmailVerificationStatus`(未知、已验证、未验证)替代之前的布尔值,以便适配器能够区分明确的提供商声明和任何信号。 2. **添加`ensureProviderLinkAllowed`守卫**: - 在OAuth登录和PKCE解析路径中添加`ensureProviderLinkAllowed`守卫,拒绝将新提供商身份链接到现有Nhost账户,除非提供商明确声明邮箱已验证。 3. **收紧OAuth适配器的“已验证”逻辑**: - 仅在提供商返回明确信号(如`email_verified`、`is_confirmed`、`confirmed_at`等)时,才将邮箱视为已验证。 4. **移除Azure AD回退**: - 移除使用`upn`或`preferred_username`作为邮箱的Azure AD回退,因为这些是内部标识符,可能被攻击者设置。 5. **更新提供商适配器**: - 更新Azure AD、Facebook、Spotify、Twitch、Twitter、Windows Live、WorkOS等适配器的逻辑,不再依赖未验证的邮箱。 - 更新Discord、Emu ID、GitLab、LinkedIn等适配器的逻辑,解析其各自的验证声明。 6. **增加测试覆盖**: - 增加per-adapter JSON解码测试和控制器测试,覆盖回调和PKCE流程,确保提供商报告未验证邮箱时不会合并身份。 ### POC代码 ```markdown - Controller: providerFlowSignIn and providerResolver now reject linking a new provider identity to an existing account unless profile.EmailVerified is true. This is a defense-in-depth guard that protects all providers, including any added in the future. - Discord: discordUserProfile now decodes the verified field, and EmailVerified is sourced from it instead of email != "". - Bitbucket: removed the fallback that accepted an unconfirmed email when no confirmed email was found. No confirmed email now always returns ErrNoConfirmedBitbucketEmail. - AzureAD: removed the fallback to preferred username and upn, which are internal directory identifiers and do not prove external email ownership. - EntraID: entraIDUser now decodes email_verified and EmailVerified is sourced from it instead of email != "". ``` ### 总结 该漏洞通过引入三态邮箱验证状态、添加`ensureProviderLinkAllowed`守卫、收紧OAuth适配器的“已验证”逻辑、移除Azure AD回退、更新提供商适配器以及增加测试覆盖,有效修复了未验证邮箱合并身份的风险。