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响应,并另外构造一个包含目标用户(如管理员)身份的恶意、未签名
的 

  1. 关键的注入与签名“迷惑”

攻击者将恶意的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
 并不在原始签名的覆盖范围内。

  1. Samlify的解析与信任缺陷

结果是,一个在密码学层面具有部分有效签名的SAML响应,成功地使其包含的恶意、未经签名验证的数据被用于认证决策,从而实现了SSO绕过和权限提升。

  1. 未能严格绑定被签名元素
    即使签名通过 URI=”#AssertionID_Original”
     明确指向了合法的Assertion,后续代码在提取Assertion数据时,可能未使用这个经过验证的、由 URI
     精确指定的Assertion,而是采用了较为宽松的策略,例如:

  2. 对多个Assertion的处理不当
    当存在多个Assertion时,未能确保仅使用由签名直接担保的那个。

  3. 选取文档中的第一个或最后一个 
     元素。

  4. 使用如 //saml:Assertion
     这样不够精确的XPath查询,获取了所有Assertion节点列表,然后错误地选择了列表中的恶意条目。

  5. 签名验证“通过”
    Samlify 检查 
    。由于该签名及其引用的 AssertionID_Original
     均未被篡改,签名在密码学上是有效的。Samlify确认“存在一个有效签名”。

  6. 数据提取逻辑的致命缺陷
    问题出在Samlify如何将“签名有效”这一事实与“具体使用哪个Assertion进行身份验证”联系起来。在受影响版本中,Samlify在验证签名后,可能由于以下原因错误地处理了恶意的Assertion:

Samlify 2.10.0 的修复推测

Samlify 2.10.0 版本据信已修复此问题。其修复方式很可能涉及:
1. 强化签名与数据主体的绑定
确保在验证签名后,应用程序逻辑唯一且仅唯一
地使用由该签名中 
 的 URI
 明确指定的、经过完整性校验的XML元素(即那个被签名的Assertion)来进行后续的身份解析和认证授权。

  1. 改进XML节点选择逻辑
    废弃任何可能导致歧义的Assertion选择策略(如“第一个/最后一个”),严格依赖签名信息来定位和信任Assertion。

  2. 对多个Assertion的严格处理
    如果SAML响应中包含多个Assertion,必须有明确的策略来处理,理想情况下应拒绝包含未经签名覆盖的额外Assertion的响应,或至少确保只处理那个被有效签名直接覆盖的Assertion。

XML安全的普遍教训与纵深防御

XSW攻击是XML安全领域的一个常见模式,不仅限于SAML。任何依赖XML签名进行安全决策的协议(如WS-Security)都可能面临此类威胁。其核心教训是:验证签名存在性是必要的,但确保被验证签名的客体与实际业务逻辑处理的客体完全一致,才是充分的。

纵深防御策略:

  1. SP侧(Samlify用户侧)
  2. 首要:升级Samlify至2.10.0+
  3. 严格配置
    确保SAML SP配置为严格模式,例如,要求Assertion必须被签名(不仅仅是Response元素)。

  4. 校验Issuer和Audience
    除了签名,务必校验Assertion中的Issuer是否为预期的IdP,Audience是否包含自身的EntityID。

  5. 监控与日志
    审计SAML认证日志,关注异常的SAML响应结构或认证失败尝试。

  6. IdP侧

  7. 强制Assertion签名
    配置IdP以确保总是对 
     元素本身进行签名,并使用强摘要和签名算法。

  8. 最小权限原则
    确保通过SAML断言授予的属性和角色遵循最小权限原则。

  9. 通用安全实践

  10. HTTPS强制
    始终通过HTTPS传输SAML消息,防止中间人截获和篡改。

  11. 定期审查安全配置
    对IdP和SP的SAML配置进行定期安全审查。

  12. WAF/RASP
    虽然WAF规则对于XSW可能不够鲁棒,但结合RASP(运行时应用自我保护)技术,可以在应用内部检测和阻止可疑的XML解析行为。

总结

CVE-2025-47949
 再次警示我们,在复杂系统中集成标准协议时,对安全细节的忽视可能导致严重后果。开发者不仅要依赖库的功能,更要理解其安全边界和潜在风险。对于Samlify用户,立即升级是当务之急。对于更广泛的技术社区,则应从中汲取教训,加强对XML及相关签名机制安全性的理解与实践。