Samlify SSO 签名绕过 (CVE-2025-47949):从XML签名本质到纵深防御
Samlify SSO 签名绕过 (CVE-2025-47949):从XML签名本质到纵深防御
原创 Hankzheng 技术修道场 2025-05-22 09:25
近期,Node.js 生态中流行的 SAML SSO 库 Samlify
曝出的关键级别签名绕过漏洞 (CVE-2025-47949
, CVSS v4.0: 9.9) 引发了广泛关注。此漏洞允许攻击者通过精心构造的SAML响应,绕过身份验证并可能获取管理员权限。本文将对该漏洞进行终极剖析,从XML签名机制、攻击手法、Samlify的缺陷,到修复原理及纵深防御策略进行全面解读。
XML 签名核心: 与 URI 的指向
理解此漏洞,首先要明确XML数字签名的核心机制。SAML响应中的
元素内部通常包含一个或多个
元素。每个
通过其 URI
属性精确指定了文档中被签名的部分。
– URI=””
表示签名整个XML文档(通常是包含签名的父元素,但不包括
自身)。
- URI=”#
“
表示签名文档中ID为
的特定元素。例如,URI=”#AssertionID123″
指向ID为 AssertionID123
的那个
。
SAML规范通常建议对核心的
元素进行签名,或者对整个
元素签名。关键在于,SP必须严格校验签名是否覆盖了其用于认证决策的那个特定Assertion。
CVE-2025-47949:签名包装攻击的精妙之处
Samlify的CVE-2025-47949
漏洞属于典型的XML签名包装 (XML Signature Wrapping, XSW)
攻击。其攻击流程及Samlify的缺陷如下:
1. 获取合法SAML响应与恶意Assertion构造
攻击者获取一个由IdP合法签名的SAML响应,并另外构造一个包含目标用户(如管理员)身份的恶意、未签名
的
。
- 关键的注入与签名“迷惑”
攻击者将恶意的Assertion注入到合法的SAML响应XML中。例如,原始响应可能如下:
<samlp:Response ID="ResponseID1" ...> <ds:Signature> <ds:SignedInfo> <ds:Reference URI="#AssertionID_Original"> <!-- 签名指向原始Assertion --> ... </ds:Reference> </ds:SignedInfo> <ds:SignatureValue>...</ds:SignatureValue> </ds:Signature> <saml:Assertion ID="AssertionID_Original"> <!-- 合法Assertion --> <saml:Subject><saml:NameID>[email protected]</saml:NameID></saml:Subject> ... </saml:Assertion> <!-- 恶意Assertion被注入为兄弟节点 --> <saml:Assertion ID="AssertionID_Malicious"> <!-- 恶意Assertion (未被签名覆盖) --> <saml:Subject><saml:NameID>[email protected]</saml:NameID></saml:Subject> ... </saml:Assertion></samlp:Response>
在此例中,原始签名明确指向 AssertionID_Original
。注入的 AssertionID_Malicious
并不在原始签名的覆盖范围内。
- Samlify的解析与信任缺陷
结果是,一个在密码学层面具有部分有效签名的SAML响应,成功地使其包含的恶意、未经签名验证的数据被用于认证决策,从而实现了SSO绕过和权限提升。
-
未能严格绑定被签名元素
即使签名通过 URI=”#AssertionID_Original”
明确指向了合法的Assertion,后续代码在提取Assertion数据时,可能未使用这个经过验证的、由 URI
精确指定的Assertion,而是采用了较为宽松的策略,例如: -
对多个Assertion的处理不当
当存在多个Assertion时,未能确保仅使用由签名直接担保的那个。 -
选取文档中的第一个或最后一个
元素。 -
使用如 //saml:Assertion
这样不够精确的XPath查询,获取了所有Assertion节点列表,然后错误地选择了列表中的恶意条目。 -
签名验证“通过”
Samlify 检查
。由于该签名及其引用的 AssertionID_Original
均未被篡改,签名在密码学上是有效的。Samlify确认“存在一个有效签名”。 -
数据提取逻辑的致命缺陷
问题出在Samlify如何将“签名有效”这一事实与“具体使用哪个Assertion进行身份验证”联系起来。在受影响版本中,Samlify在验证签名后,可能由于以下原因错误地处理了恶意的Assertion:
Samlify 2.10.0 的修复推测
Samlify 2.10.0 版本据信已修复此问题。其修复方式很可能涉及:
1. 强化签名与数据主体的绑定
确保在验证签名后,应用程序逻辑唯一且仅唯一
地使用由该签名中
的 URI
明确指定的、经过完整性校验的XML元素(即那个被签名的Assertion)来进行后续的身份解析和认证授权。
-
改进XML节点选择逻辑
废弃任何可能导致歧义的Assertion选择策略(如“第一个/最后一个”),严格依赖签名信息来定位和信任Assertion。 -
对多个Assertion的严格处理
如果SAML响应中包含多个Assertion,必须有明确的策略来处理,理想情况下应拒绝包含未经签名覆盖的额外Assertion的响应,或至少确保只处理那个被有效签名直接覆盖的Assertion。
XML安全的普遍教训与纵深防御
XSW攻击是XML安全领域的一个常见模式,不仅限于SAML。任何依赖XML签名进行安全决策的协议(如WS-Security)都可能面临此类威胁。其核心教训是:验证签名存在性是必要的,但确保被验证签名的客体与实际业务逻辑处理的客体完全一致,才是充分的。
纵深防御策略:
- SP侧(Samlify用户侧)
- 首要:升级Samlify至2.10.0+
-
严格配置
确保SAML SP配置为严格模式,例如,要求Assertion必须被签名(不仅仅是Response元素)。 -
校验Issuer和Audience
除了签名,务必校验Assertion中的Issuer是否为预期的IdP,Audience是否包含自身的EntityID。 -
监控与日志
审计SAML认证日志,关注异常的SAML响应结构或认证失败尝试。 -
IdP侧
-
强制Assertion签名
配置IdP以确保总是对
元素本身进行签名,并使用强摘要和签名算法。 -
最小权限原则
确保通过SAML断言授予的属性和角色遵循最小权限原则。 -
通用安全实践
-
HTTPS强制
始终通过HTTPS传输SAML消息,防止中间人截获和篡改。 -
定期审查安全配置
对IdP和SP的SAML配置进行定期安全审查。 -
WAF/RASP
虽然WAF规则对于XSW可能不够鲁棒,但结合RASP(运行时应用自我保护)技术,可以在应用内部检测和阻止可疑的XML解析行为。
总结
CVE-2025-47949
再次警示我们,在复杂系统中集成标准协议时,对安全细节的忽视可能导致严重后果。开发者不仅要依赖库的功能,更要理解其安全边界和潜在风险。对于Samlify用户,立即升级是当务之急。对于更广泛的技术社区,则应从中汲取教训,加强对XML及相关签名机制安全性的理解与实践。