OpenID Connect部署中公私钥混淆的安全风险
在开发名为badkeys的加密公钥漏洞检测工具时,我在德国OWASP日的问答环节被问及是否曾用该工具检查OpenID Connect设置中的加密密钥。此前我并未进行过此类检测。
OpenID Connect是一种单点登录协议,允许网站通过其他服务提供登录功能。当您看到网站支持通过Google或Facebook账户登录时,背后采用的技术通常就是OpenID Connect。
像Google这样的OpenID提供商可以在预定义URL(格式如:https://example.com/.well-known/openid-configuration)发布JSON格式的配置文件。这些配置文件包含"jwks_uri"字段,指向包含用于验证认证令牌的加密公钥的JSON Web密钥集(JWKS)。JSON Web密钥是一种以JSON格式编码加密密钥的方法,而JSON Web密钥集则是包含多个此类密钥的JSON结构。
公私钥混淆问题
JSON Web密钥有一个非常特殊的属性:加密公钥和私钥本质上都是大数字。对于大多数算法,公钥的所有数字都包含在私钥中。在JSON Web密钥中,这些数字采用URL安全的Base64编码。
以下是一个ECDSA公钥的JSON Web密钥格式示例:
|
|
对应的私钥为:
|
|
两者非常相似,唯一区别是私钥包含一个额外的值"d"(在ECDSA中为私钥值)。RSA私钥包含多个额外值,但基本思想相同:私钥就是公钥加上一些额外值。
不同寻常的是,公钥和私钥使用相同的序列化格式。区分它们的唯一方法是检查是否存在私钥值。由于JSON通常以可扩展方式解释,应用程序通常会忽略JSON文件中无意义的额外字段。
这两个事实结合导致了一个有趣而危险的特性:使用私钥代替公钥通常有效,因为JSON Web密钥格式中的每个私钥也是有效的公钥。
通过扫描Tranco Top 100万列表并扩展SSO-Monitor的主机名,我发现了约13,000个具有有效OpenID Connect配置的主机。检查发现9个主机实际上配置了私钥而非公钥,包括stackoverflowteams.com、stack.uberinternal.com和ask.fisglobal.com等知名公司。
短RSA密钥问题
7个主机配置了512位RSA密钥,这种密钥早已被证明可破解。45个主机使用1024位RSA密钥,虽然尚未公开演示攻击,但被认为可能被强大攻击者破解。
1999年首次成功公开攻击512位RSA需要超级计算机运行数月,如今使用CADO-NFS等开源软件即可在几小时内以约70美元云服务成本完成破解。
生产环境使用示例密钥
badkeys扫描还发现18个主机使用"公共私钥"——即对应私钥存在于公开可用软件包中的示例密钥。虽然已向相关方报告512位RSA密钥和示例密钥问题,但大多数仍未修复。
影响分析
在13,000个检测到的OpenID配置中,共发现33个易受攻击主机(0.25%)。漏洞严重程度取决于认证令牌交换方式:通过浏览器交换时最严重,攻击者可签署任意登录令牌;直接服务器间交换时攻击难度较大,需要中间人攻击和TLS连接攻击。
改进建议
这些问题本可通过更好的规范完全预防:
-
密钥格式区分:JSON Web密钥对公钥和私钥使用相同序列化格式的设计决策容易导致混淆。在TLS或SSH等公钥私钥完全不同的生态系统中,错误配置会立即被发现。
-
实施合规检查:OpenID Connect发现规范要求"JWK集不得包含私钥或对称密钥值"。实现应检查是否存在"d"值来执行此规定。
-
算法标识改进:未来后量子签名算法可为公钥和私钥指定不同标识符(如"ML-DSA-65-Public"和"ML-DSA-65-Private")。
-
密钥长度限制:OpenID Connect等相对较新的协议本不应支持512位等短RSA密钥。理想情况下应只定义几种标准密钥大小,避免不必要的复杂性。
-
测试密钥标准化:采用RFC 9500中的标准化测试密钥集可减少生产环境使用示例密钥的问题。
最新版badkeys添加了–jwk参数,可直接扫描JSON Web密钥或密钥集文件,帮助检测已知漏洞和已泄露密钥。
感谢Daniel Fett提出扫描OpenID Connect密钥的建议和反馈,以及Sebastian Pipping对本博文的宝贵意见。