SSO单点登录安全:CAS与SAML攻防

作者:Yolo 发布时间: 2026-06-03 阅读量:7

SSO单点登录安全:CAS与SAML攻防

网络安全系列 · 认证与授权篇

单点登录(Single Sign-On,SSO)是现代企业系统的标配。用户只需一次认证,就能访问多个应用——体验确实好,但安全边界也因此被拉长了。一旦 SSO 体系被攻破,攻击者拿到的不是某个应用的权限,而是整片系统的钥匙。

本文聚焦两种主流 SSO 协议——CASSAML,从协议原理讲到真实攻击手法,帮你建立完整的防御视角。


一、SSO 的核心逻辑

SSO 的本质是把认证从应用剥离,交给一个独立的身份提供者(IdP)。常见角色:

  • IdP(Identity Provider):负责验证用户身份,签发令牌
  • SP(Service Provider):业务应用,信任 IdP 的认证结果
  • 用户:一次登录,多处通行

流程简化:

用户访问 SP → 未登录 → 重定向到 IdP
用户在 IdP 认证成功 → IdP 签发凭证 → 返回 SP
SP 验证凭证 → 建立本地会话

问题就出在这个"凭证"和"验证"环节上。


二、CAS 协议详解

2.1 CAS 工作流程

CAS(Central Authentication Service)是耶鲁大学的开源协议,目前最常用的是 CAS 2.0/3.0。

标准登录流程:

1. 用户访问 https://app.example.com(SP)
2. SP 发现无会话,重定向到 https://cas.example.com/login?service=https://app.example.com/callback
3. 用户在 CAS 服务器输入用户名密码
4. CAS 验证成功,生成 TGT(Ticket Granting Ticket),写入 Cookie
5. CAS 重定向回 SP,附带 ST(Service Ticket):.../callback?ticket=ST-12345
6. SP 向 CAS 发送 /serviceValidate?ticket=ST-12345&service=... 验证 ST
7. CAS 返回用户身份信息(XML 格式)
8. SP 建立本地会话

关键票据:

票据作用有效期
TGT用户与 CAS 的会话凭证通常 2-8 小时
ST一次性票据,用于 SP 验证通常 10 秒-5 分钟
PGT代理票据,用于服务间代理与 TGT 相同
PT代理票据的一次性使用版本与 ST 相同

2.2 CAS 的常见攻击

攻击一:Service 参数伪造(未校验回调地址)

漏洞原理: CAS 服务端未严格校验 service 参数,攻击者可构造恶意回调地址。

攻击步骤:

1. 攻击者构造链接:https://cas.example.com/login?service=https://attacker.com/callback
2. 诱导用户点击,用户在 CAS 正常登录
3. CAS 重定向到 https://attacker.com/callback?ticket=ST-12345
4. 攻击者拿到有效 ST,立即向 CAS 验证
5. 攻击者获得合法用户身份

防御: CAS 服务端必须维护服务注册表,只允许白名单内的 service 地址。

// 错误示例:只检查前缀
if (service.startsWith("https://app.example.com")) {
    // 可被 https://app.example.com.attacker.com 绕过
}

// 正确做法:精确匹配或维护注册表
Service registeredService = serviceRegistry.findServiceBy(service);
if (registeredService == null || !registeredService.matches(service)) {
    throw new UnauthorizedServiceException();
}

攻击二:ST 票据劫持与重放

ST 理论上是一次性票据,但如果:

  • ST 有效期过长(>30 秒)
  • CAS 服务端未正确标记 ST 为已使用
  • 网络传输未使用 HTTPS

攻击者可通过中间人、浏览器历史、Referer 日志等方式获取 ST。

防御:

  1. ST 有效期控制在 10-30 秒
  2. CAS 服务端收到 /serviceValidate 后立即销毁 ST
  3. 全链路强制 HTTPS
  4. 敏感操作要求重新认证(step-up authentication)

攻击三:XML 响应篡改(CAS 2.0 协议漏洞)

CAS 2.0 的 /serviceValidate 返回 XML:

<cas:serviceResponse xmlns:cas="http://www.yale.edu/tp/cas">
    <cas:authenticationSuccess>
        <cas:user>admin</cas:user>
        <cas:attributes>
            <cas:email>[email protected]</cas:email>
        </cas:attributes>
    </cas:authenticationSuccess>
</cas:serviceResponse>

攻击场景: 如果 SP 使用不安全的 XML 解析器,可能遭受:

  • XXE 攻击:通过 <!DOCTYPE> 读取服务器文件
  • XML 签名绕过:篡改响应中的用户名

防御:

# Python 安全解析示例
import xml.etree.ElementTree as ET
from defusedxml import ElementTree

# 禁用外部实体
parser = ET.XMLParser(resolve_entities=False)
tree = ElementTree.parse(response, parser=parser)

攻击四:TGT 窃取与会话固定

TGT 存储在 CAS 域的 Cookie 中(TGC Cookie)。如果:

  • CAS 未设置 HttpOnlySecureSameSite 属性
  • 子域存在 XSS 漏洞
  • 使用了不安全的传输

攻击者可窃取 TGC,直接获得用户的 CAS 会话。

防御配置:

# CAS 服务端 Cookie 配置
cas.tgc.secure=true
cas.tgc.httpOnly=true
cas.tgc.sameSite=Strict
cas.tgc.crypto.encryption.key=...
cas.tgc.crypto.signing.key=...

三、SAML 协议详解

3.1 SAML 基础

SAML(Security Assertion Markup Language)是 OASIS 标准,企业级应用广泛(如 Office 365、Salesforce、AWS)。

核心概念:

  • Assertion:IdP 对用户身份的声明(包含 NameID、属性、认证上下文)
  • Protocol:请求/响应的交互规则(AuthnRequest、Response、LogoutRequest)
  • Binding:传输方式(HTTP Redirect、HTTP POST、Artifact)

典型流程(SP-initiated):

1. 用户访问 SP,点击登录
2. SP 生成 SAML AuthnRequest,通过浏览器重定向到 IdP
3. 用户在 IdP 认证
4. IdP 生成 SAML Response(包含 Assertion),通过浏览器 POST 到 SP
5. SP 验证 Response 的签名,提取用户信息,建立会话

3.2 SAML 消息结构

<saml2:Response xmlns:saml2="urn:oasis:names:tc:SAML:2.0:protocol">
    <saml2:Issuer>https://idp.example.com</saml2:Issuer>
    <saml2:Assertion xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion">
        <saml2:Issuer>https://idp.example.com</saml2:Issuer>
        <ds:Signature>...</ds:Signature>  <!-- 签名 -->
        <saml2:Subject>
            <saml2:NameID>[email protected]</saml2:NameID>
            <saml2:SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer">
                <saml2:SubjectConfirmationData 
                    InResponseTo="id-123" 
                    NotOnOrAfter="2026-06-03T10:00:00Z"
                    Recipient="https://sp.example.com/saml/acs"/>
            </saml2:SubjectConfirmation>
        </saml2:Subject>
        <saml2:Conditions>
            <saml2:AudienceRestriction>
                <saml2:Audience>https://sp.example.com</saml2:Audience>
            </saml2:AudienceRestriction>
        </saml2:Conditions>
        <saml2:AuthnStatement>
            <saml2:AuthnContext>
                <saml2:AuthnContextClassRef>
                    urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport
                </saml2:AuthnContextClassRef>
            </saml2:AuthnContext>
        </saml2:AuthnStatement>
    </saml2:Assertion>
</saml2:Response>

3.3 SAML 的常见攻击

攻击一:XML 签名剥离(Signature Wrapping)

漏洞原理: SP 只验证 Response 外层签名,但使用内层未签名的 Assertion。

攻击构造:

<!-- 原始合法 Response -->
<Response>
    <SignedInfo>...</SignedInfo>
    <SignatureValue>...</SignatureValue>
    <Assertion ID="original">  <!-- 有签名 -->
        <Subject><NameID>admin</NameID></Subject>
    </Assertion>
</Response>

<!-- 攻击者构造 -->
<Response>
    <SignedInfo>...</SignedInfo>  <!-- 原签名仍然有效 -->
    <SignatureValue>...</SignatureValue>
    <Assertion ID="evil">  <!-- 攻击者插入的新 Assertion,无签名 -->
        <Subject><NameID>admin</NameID></Subject>
    </Assertion>
    <Assertion ID="original">  <!-- 原始 Assertion 被移到后面 -->
        <Subject><NameID>user</NameID></Subject>
    </Assertion>
</Response>

如果 SP 的解析逻辑取第一个 Assertion,攻击者就能以 admin 身份登录。

防御:

  1. 严格验证签名覆盖的范围
  2. 确保使用的 Assertion 确实被签名
  3. 使用成熟的 SAML 库(如 OneLogin、OpenSAML),不要自行解析

攻击二:Audience 限制绕过

Assertion 中的 <Audience> 指定了合法的 SP。如果 SP 不验证 Audience:

<Conditions>
    <AudienceRestriction>
        <Audience>https://evil-sp.example.com</Audience>
    </AudienceRestriction>
</Conditions>

攻击者可以:

  1. 在合法 IdP 下注册一个恶意 SP
  2. 诱导用户登录恶意 SP,获取 SAML Response
  3. 将 Response 重放到目标 SP
  4. 如果目标 SP 不检查 Audience,攻击者就能登录

防御: SP 必须严格校验 Audience 是否匹配自己的 Entity ID。

攻击三:RelayState 操控与开放重定向

SAML 的 RelayState 参数用于保持登录前的状态,但常被滥用:

https://idp.example.com/sso?SAMLRequest=...&RelayState=https://attacker.com

如果 SP 登录后直接跳转到 RelayState 指定的地址,就是开放重定向。

防御:

  1. RelayState 使用白名单校验
  2. 或使用加密的 state 参数存储跳转目标

攻击四:SAML Response 重放

如果 SP 不跟踪已处理的 SAML Response ID(InResponseTo)或 Assertion ID:

  1. 攻击者截获合法的 SAML Response
  2. 在有效期内重复提交
  3. 如果 SP 不做去重,就能重复建立会话

防御:

# 维护已处理的 Assertion ID 集合
processed_assertions = set()  # 建议用 Redis,TTL 设为 Assertion 有效期

def validate_saml_response(response):
    assertion_id = response.assertion.id
    if assertion_id in processed_assertions:
        raise ReplayedAssertionError()
    processed_assertions.add(assertion_id)

攻击五:证书伪造与算法降级

SAML 依赖 XML 签名,如果:

  • IdP 使用弱签名算法(如 SHA-1)
  • 证书未正确校验有效期、颁发者
  • 证书轮换时 SP 未及时更新

攻击者可以构造碰撞或伪造证书。

防御:

  1. 强制使用 SHA-256 或更强的签名算法
  2. 建立证书轮换机制,提前通知 SP
  3. 定期检查证书有效期

四、SSO 安全加固清单

4.1 IdP 侧

检查项要求
Service 注册表严格白名单,精确匹配
票据有效期ST < 30s,TGT < 8h
Cookie 安全HttpOnly + Secure + SameSite=Strict
通信加密全链路 TLS 1.2+
签名算法SHA-256 或更强
审计日志记录所有认证、票据验证事件
多因素认证敏感系统强制 MFA

4.2 SP 侧

检查项要求
签名验证验证所有签名,不跳过
Audience 校验严格匹配自己的 Entity ID
时间校验检查 NotBefore、NotOnOrAfter
重放防护维护已处理的 Assertion ID
RelayState白名单或加密校验
会话管理独立会话,不依赖 SSO 票据
登出同步支持 SLO(Single Logout)

4.3 通用最佳实践

  1. 最小权限原则:SSO 只认证身份,授权(RBAC)在应用内独立管理
  2. step-up 认证:敏感操作要求重新输入密码或二次验证
  3. 监控异常:同一用户短时间内多地登录、异常时间登录
  4. 定期轮换:密钥、证书定期更换
  5. 渗透测试:定期对 SSO 流程做端到端安全测试

五、实战:检测 SAML 签名 wrapping 漏洞

使用 Burp Suite + SAML Raider 插件:

  1. 拦截 SAML Response
  2. 发送到 SAML Raider
  3. 尝试 "Sign SAML Message" / "Remove Signatures"
  4. 修改 Assertion 内的 NameID
  5. 观察 SP 是否接受篡改后的 Assertion

手动测试 Payload:

<saml2:Response>
    <saml2:Assertion ID="evil">
        <saml2:Subject>
            <saml2:NameID>[email protected]</saml2:NameID>
        </saml2:Subject>
    </saml2:Assertion>
    <!-- 原始 Assertion 移到后面,保留签名 -->
</saml2:Response>

六、总结

SSO 是"把鸡蛋放在一个篮子里"的典型——方便,但篮子必须足够结实。

协议主要风险核心防御
CASService 伪造、ST 劫持、TGC 窃取服务注册表、短票据、安全 Cookie
SAML签名 wrapping、Audience 绕过、重放严格签名验证、Audience 校验、ID 去重

无论使用哪种协议,记住:信任但验证。IdP 的声明不是圣旨,SP 必须独立验证每一个安全断言。


下一篇预告:《Session 管理安全:从 Cookie 到 Token》——深入会话生命周期,解析 Session Fixation、Cookie 安全属性,以及现代 Token 方案的攻防。