← 文章 / 云原生与基础设施
Hacker News 2小时前 · 2026-09-23 07:04:04 · 1 阅读

SAML:一个充斥着糟糕设计的复杂认证协议

SAML(Security Assertion Markup Language,安全断言标记语言)这一认证协议诞生于学术界,成长于企业 IT 部门,至今仍是这些组织的主力技术。然而,它是时候退休了。2000 年代末期,随着 SaaS(软件即服务)公司的兴起,IT 部门需要一种让用户登录众多新 Web 服务的方式,SAML 和随之兴起 SSO(单点登录)行业正好满足了这一需求。但如今,SAML 已经被自身的复杂性压得喘不过气。是时候将其废弃,转向 OpenID Connect(OIDC)等更现代的替代方案了。在本文中,我将探讨 SAML 由委员会拼凑而成的出身、它在学术和企业环境中的地位攀升、它在安全研究社区的审视下如何逐渐崩解,以及它(有望)被更新的协议取代的前景。

SAML 入门

SAML 的阴险之处在于,它大体上其实不难理解,但它的地基是由沙子、骨粉和灰烬堆成的;它能工作……前提是你假设 XML 签名校验是可靠的。但 XML 签名校验深受诅咒,复杂到大多数实际部署的 SAML 实现都只是在包装 libxmlsec——一个没人愿意读的粗糙 C 代码库。
—— Thomas Ptacek,2023

SAML 与 SSO 行业的诞生

维基百科说,“SAML 是一种基于 XML 的、用于安全断言的标记语言。”它由 OASIS(结构化信息标准促进组织)下属的安全服务技术委员会(SSTC)于 2002 年创建。以现代标准来看,这个开局就不太妙。XML 虽有可取之处,但比起 JSON 这类较新的方案要复杂得多,这一点后面细说。而且,让一个委员会下面再设小组委员会、再开会,注定会造出“什么都要塞进去”的大杂烩协议(想想瀑布式开发大规模预先设计之类的做法)。果不其然,最终我们把四种(!)基于 XML 的安全协议一股脑塞进了 SAML:

……以下知识产权被贡献给了 SSTC:

  • 来自 Netegrity 的 Security Services Markup Language(S2ML)
  • 来自 Securant 的 AuthXML
  • 来自 VeriSign 的 XML 信任断言服务规范(X-TASS)
  • 来自 Jamcracker 的信息技术标记语言(ITML)

SAML:历史

不过,对这类协议的需求是显而易见的。随着互联网在 90 年代从 Web 1.0 向 00 年代初的 Web 2.0 过渡,用户和机构迫切需要一种简单的方式来验证众多新兴 Web 服务的身份。学术界是这场运动的最大推动力,但并非唯一力量:2002 年耶鲁大学的中央认证服务(CAS)、2003 年由研究型大学联盟 Internet2(包括我的母校)发布的 Shibboleth IdP、2003 年微软推出的 ADFS,以及由挪威国有公司 Uninett 开发、大约在 2007 年出现的 simpleSAMLphp,该公司与学术界关系密切。所有这些认证项目最终以不同方式支持了 SAML。犹如 ARPA 网先驱先例,大学一直站在互联网发展的最前沿,也是 Web 服务的最早用户。一旦建立起协议可用性的基础层和学术界的初步试验场,商业产业便接棒冲刺,将其打造成了一个价值数十亿美元的产业。

单点登录(SSO)、身份认证和认证服务商行业也起源于世纪初,但真正蓬勃发展是在几年之后:Ping Identity(2002 年)、OneLogin(2009 年)、Okta(2009 年)和 Duo Security(2010 年)。这些公司基本上都建立在 SAML 协议之上,唯一的例外是 Duo——直到 2015 年推出其首款 SSO 产品,我才会因此参与到这个故事中。我曾参与开发 Duo 的首个本地部署访问网关产品(DAG),它基于 simpleSAMLphp 和 SAML 协议构建。正是在这个项目里,我深度接触了 SAML 协议,并花了好几年时间消化其冗长的规范文档。当时,Kelby Ludwig 发现了 XML 注释绕过漏洞,尽管我们稍后会详细讨论各种攻击手段和 SAML 的缺陷,但归根结底,当时的 SSO 和认证服务商行业正处在爆发期,且大量基础设施都搭建在 SAML 协议之上。

盔甲上的裂痕

XML 签名包装(XSW)攻击堪称 SAML 的阿喀琉斯之踵。虽然早在 2005 年(链接)、2008 年(链接)和 2009 年(链接)就有了关于签名包装的研究,2008 年(链接)也有针对 SAML 的研究,但我认为这一领域的奠基之作是 2012 年的《On Breaking SAML: Be Whoever You Want to Be》。该研究将理论应用于实践,并开发出了自动化检测 XSW 攻击的方法(链接)。这正是我们在实现 DAG 时的北极星指引,也是选择 simpleSAMLphp 作为构建基石的原因。彼时的 PHP 并非以安全著称(链接),但 simpleSAMLphp 的表现却足以说明问题。在连大多数人都还搞不懂 XSW 是什么的年代,simpleSAMLphp 已经具备了抵御此类攻击的能力:

simpleSAMLphp 的安全记录(来源:On Breaking SAML)

尽管在这篇 2012 年的论文中已被重点讨论,XSW 至今依然存在。既然我们已经清楚这类漏洞的存在,为什么还是修不掉?不过在深入剖析 SAML 的缺陷之前,得先看看它脚下的地基有多不牢靠:XML。

论安全方面的黑历史,XML 也不遑多让。这些漏洞类型对 90 年代的开发者来说可能更熟悉,但它们至今仍存在于 XML 中:XXE、实体扩展("billion laughs")、DTD 检索(SSRF)、XPath/XQuery/XInclude/XSLT/CDATA 注入,等等。一个 SAML 库在实现真正的 SAML 功能之前,得先把这些漏洞类型全部应对好。

除了安全漏洞之外,XML 相比 JSON 之类的格式,复杂度也高出一大截。XML 里有标签、元素、属性、注释、命名空间、标记与内容的区分、schema、CDATA、DOCTYPE,等等。而 JSON 基本上就是键、值、对象和列表。复杂度通常与安全性背道而驰,这也是我认为 SAML 是一个"糟糕设计的分形"的原因之一。

糟糕设计的分形

SAML 提供了大量学习协议设计的机会。在这一节中,我会讲五个缺陷——我认为它们对 SAML 作为认证协议的长期前景是致命的。设计新的认证协议时同样可以参考这些缺陷:要么避开它们,要么反其道而行之,把正确的做法直接内置到协议里。

构建在 XML 之上

如前所述,SAML 构建在 XML 之上,而 XML 相当复杂,但这不能怪标准委员会。XML 是当时的标配,大家都在用它。JSON 在 2001 年才被"发现",而那正是 SAML 委员会开会的时间点,他们不太可能围绕一个实验性的新格式来设计认证协议——尤其当这种格式是为 JavaScript 而生、而他们写的是大把 Java 的时候。

我们完全可以设计一套定量指标来衡量 XML 与 JSON、或 SAML 与 JWT/OIDC 的复杂度(比如规范/RFC 的总字数、规范性关键词字数等),但这足以写一篇博客或论文了。为了紧扣主题,此处不展开赘述,只需指出 XML 的复杂度远高于 JSON 这类格式。

规范化

规范化(C14N)旨在解决 XML 杂乱无章的问题:对 XML 数据计算哈希值时,确保结果一致。换言之,如果服务提供商(SP)和身份提供方(IdP)无法就 XML 数据的一致表示达成共识,字节序列就对不上,签名也就无法匹配,最终导致身份验证失败。但说起来容易,做起来难。

规范化漏洞是 2018 年 Kelby 发起的 XML 注释绕过攻击的温床:

XML 规范化(图源:Identity Theft)

规范化问题往往是解析器差异(parser differential)和/或“往返”(round-trip)漏洞的前兆,而大多数现代 SAML 攻击正是利用了这些缺陷:

信封签名

包裹式签名与规范化有着某种近亲关系。简而言之,如果你试图将签名插入到正在签名的数据负载中,那将会非常麻烦。让我们从这一角度对比 JWT 和 SAML:

JWT 与 SAML 签名对比(图片来源:jwt.io 和 samltool.io)

在上述 JWT 示例中,蓝色的签名与 JSON 负载是分离的,并在 JWT 中用句号(".")进行分隔。而在 SAML 示例中,Signature 元素被插入(即“包裹”)到了 Assertion 元素内部。这里的问题在于,当你同时还在修改数据本身时,要获得一份字节级完全一致、规范等价的数据表示极其困难。特别是当面对 XML 这种复杂格式及其复杂的规范化规则时,难度更是雪上加霜。

“大杂烩”式设计

这种设计缺陷基本上等同于违背了“你不需要它”(YAGNI)原则。虽然这么说并不完全公平,因为 SAML 规范中实际被用到的部分在过去 20 年里确实发生了变化(抱歉,SOAP 和 artifact 绑定)。但事实是,99% 的现代 SAML 实现都使用非常相似的数据结构和规范子集。你在当今野外遇到的任何 SAML 认证流程,很可能避开了规范中 90% 的内容。这为大量闲置的功能增加了显著的复杂度。

如果我要在新系统中添加 SAML 支持,除了所有标准的 SAML 检查,我还会考虑拒绝任何数据结构不像 Okta、Onelogin、Google 或 Shib 生成的那种消息。
Thomas Ptacek, 2021

僵化

SAML 设计于不同的时代,服务于不同的需求,并且未能获得必要的更新。这些问题通常具有实际影响,而不仅仅是理论上的。我所指的僵化大致可以列举如下:

  1. OIDC 假定运行在 HTTP 上,而 SAML 与传输方式无关。当然,SAML 也有 HTTP binding,而且是最常用的方式,但这并非强制。这给了 SAML 一定的灵活性,但也意味着这些灵活性必须有明确的定义、有人去实现,而且可能藏有 bug。SAML 诞生之时,HTTP + TLS 还不是 Web 服务通信的主流方式,它也从未真正适应过后来的现代格局。而 HTTPS 让 OIDC 可以把加密和可信通信直接交给传输层处理。
  2. OIDC 通常假定网络是连通的,SAML 则没有这个假设。OIDC 最常见的流程(授权码模式)假定 OpenID Provider(OP)和 Relying Party(RP)可以直接通信(OIDC 的 OP/RP 相当于 SAML 的 IdP/SP)。当然,OIDC 也有基于 form post 的 implicit flow,但如今已很少见。类似地,SAML 有支持直接通信的 artifact binding,同样也很罕见。关键在于:如果 OP 和 RP 能直接通信,认证响应本身就不必携带做出认证和授权决策所需的全部信息,从而减小了载荷的体积和复杂度——这些信息可以通过后端通道交换。
  3. OIDC 是多年自然演进的产物,而 SAML 在很大程度上是一次性设计出来的。OIDC 包含几十份规范和 RFC,都是多年间逐步发展起来的。这些文档通常是为了解决某个具体需求而编写,而不是试图预先设想所有未来需求、一次性设计到位。这有点像敏捷开发与瀑布模型的对比,正如前文所述。下面是一个不完全的时间线:
    1. OpenID Connect 1.0 规范发布(2014)
    2. JOSE 技术栈通过 RFC 7515-7519 定稿,覆盖 JW{S,E,K,A,T}(2015)
    3. PKCE 通过 RFC 7636 发布(2015)
    4. 面向移动/原生应用的 PKCE 通过 RFC 8252 发布(2017)
    5. 面向 IoT 设备的 Device authorization grant 通过 RFC 8628 发布(2019)
    6. 面向 MFA 流程的 Demonstrating proof of possession(DPoP)通过 RFC 9449 发布(2023)
    7. 面向 SPA 的 PKCE 通过 RFC 10017 发布(2026)

这段历史背景也颇值得玩味。SAML 诞生于 VPN 和网络隔离盛行的年代,这正是上文第 2 点提到的情形。当时,如果身份提供商(IdP)或服务提供商(SP)部署在企业防火墙之后,无法与对端直接通信,整个部署流程就会卡壳,厂商也就丢掉了这笔交易。SAML 必须妥善处理这种场景。2014 年,谷歌提出的 BeyondCorp 模型和零信任架构彻底颠覆了这一观念。此外,SAML 未能预见移动端、单页应用(SPA)和物联网的革命浪潮,当这些技术在 2000 年代末兴起时,它束手无策。尽管 SAML 目前在企业环境中仍被广泛采用,但上述宏观趋势已促成了该协议漫长而缓慢的衰落周期。在瞬息万变的 IT 环境中,灵活性和松耦合才是实现快速适应的关键。

殊途同归 OIDC

没有完美的协议,但就解决方案而言,最终都指向 OIDC。据我观察,SAML 唯一具备优势的场景,仍是 IdP 和 SP 无法直接通信的网络环境。OIDC 的隐式流程配合表单提交,同样能提供所需的所有组件。这其实是业内我能找到的最简洁的迁移方案之一。

那么,如果我是一个服务提供商(即 SP),希望接入单点登录(SSO)生态系统但又不想用 SAML,该怎么办?直接支持 OIDC 即可,抛弃 SAML。显然,Fly.io 和 Tailscale 已经这么做了:

到目前为止,我们坚持使用 OIDC,Tailscale 也是如此。考虑到 Tailscale 的客户端构成,既然他们能守住底线,我相信大多数组织也能做到。总之,尽量避免使用 SAML。别忘了,作为厂商,你经常要与那些根本不做真实 SSO 集成的公司竞争。
Thomas Ptacek, 2024

如果我是一个身份或认证提供商(即 IdP),想要摆脱 SAML,该怎么办呢?取决于你的客户数量,这将是一段漫长的旅程。但你都知道怎么吃大象吗?一口一口地吃。这在业内是一条成熟的路径:制定废弃计划、向客户公示、停止向新客户开放 SAML 集成、为现有 SAML 客户提供等效的 OIDC 配置、设定截止日期,然后开始行动。

SAML 曾经辉煌了大约 25 年。它孕育了 SSO 行业,保障了大量身份认证的安全性,提升了数十种 Web 服务的认证体验,并带来了数百亿美元的经济影响。我们应该感谢 SAML 协议的创建者。它为我们提供了一个绝佳的案例,让我们得以观察一个高度动态的科技时期内协议设计与演化的全貌。

如果你想进一步了解关于安全话题的历史分析,不妨看看这篇《Marshal 的迷思:Ruby 反序列化利用简史》。

如果你需要协议设计审计,或者想要对认证系统进行评审,请联系我们

原始来源: Hacker News

评论 (0)