拆开 SAML Assertion
目标
解码真实格式的 SAML Response,亲自提取 Issuer、NameID、Conditions、属性和签名元素,并整理 SP 必须验证的项目。
为什么重要
SAML 集成通常由库完成,但发生故障后最终仍要直接查看 XML。收到“Audience 不匹配”“超过 NotOnOrAfter”“缺少 InResponseTo”等错误时,必须知道该在文档的哪个位置检查。
本实验真正的目的,是理解签名验证的陷阱。XML Signature Wrapping 攻击可以在签名验证通过的同时,让应用读取到伪造数据。“签名是否有效”与“我读取的节点是否就是被签名的节点”是两个不同问题。亲自在文档中确认这一点后,就能解释为什么不应自行实现 SAML。
步骤
- 解码
/opt/lab/fixtures/auth/saml/response.b64,保存为/root/saml/response.xml。它必须是有效 XML。 - 创建
/root/saml/head.txt,共四行,格式如下。issuer=<Response 의 Issuer 값> destination=<Destination 속성> inresponseto=<InResponseTo 속성> status=<StatusCode 의 Value 속성> - 创建
/root/saml/subject.txt,共两行。nameid=<NameID 값> format=<NameID 의 Format 속성> - 创建
/root/saml/conditions.txt,共四行。notbefore=<NotBefore> notonorafter=<NotOnOrAfter> audience=<Audience 값> valid=<현재 시각 기준 유효하면 Y, 아니면 N> - 提取
AttributeStatement的属性,创建/root/saml/attrs.csv。第一行为name,value,按名称升序排列。 - 创建
/root/saml/sig.txt,共三行。reference_uri=<Reference 의 URI 속성> digest_alg=<DigestMethod 의 Algorithm 속성> signature_alg=<SignatureMethod 의 Algorithm 속성> - 将 XML 中的
X509Certificate内容恢复为 PEM 格式,并保存到/root/saml/idp.pem。openssl x509 -in /root/saml/idp.pem -noout -subject必须成功。 - 编写
/root/saml/checklist.md。列出 SP 必须验证的至少 7 项,并为每项补充一行验证失败时的风险。正文中必须出现Audience、NotOnOrAfter、InResponseTo、Destination、Issuer、서명、재사용七个词。
参考
- 忽略 namespace 查找元素:
xmlstarlet sel -t -v "//*[local-name()='Issuer']" -n 파일.xml - 属性值:
xmlstarlet sel -t -v "//*[local-name()='Response']/@Destination" -n 파일.xml - 恢复 PEM:在
-----BEGIN CERTIFICATE-----与-----END CERTIFICATE-----之间放入 base64 正文,并每 64 个字符换行(fold -w 64)。 - 常见错误 1:在 XPath 中直接使用 prefix(
saml:、samlp:),导致找不到任何内容。 - 常见错误 2:混淆
Response的 Issuer 与Assertion的 Issuer。 - 常见错误 3:把 PEM 写成一整行,导致 openssl 无法读取。
解码 SAMLResponse
解码 /opt/lab/fixtures/auth/saml/response.b64,
保存为 /root/saml/response.xml。它必须是有效 XML。
SAMLResponse 参数采用 base64。请同时记住,POST binding 不压缩,而 Redirect binding 还会增加 deflate 压缩。
提取 Response header 信息
创建 /root/saml/head.txt,共四行,格式如下。
issuer=<Response 의 Issuer 값>
destination=<Destination 속성>
inresponseto=<InResponseTo 속성>
status=<StatusCode 의 Value 속성>
这是包含多个 namespace 的 XML。在 xmlstarlet 中按 local name 查找,就不会受 prefix 影响。
提取 NameID 与格式
创建 /root/saml/subject.txt,共两行。
nameid=<NameID 값>
format=<NameID 의 Format 속성>
NameID 的 Format 会影响 SP 识别用户的方式。想一想 persistent 与 transient 的区别。
检查有效时间与受众
创建 /root/saml/conditions.txt,共四行。
notbefore=<NotBefore>
notonorafter=<NotOnOrAfter>
audience=<Audience 값>
valid=<현재 시각 기준 유효하면 Y, 아니면 N>
NotOnOrAfter 表示“从该时刻起无效”。请与当前时间比较后判断;此 fixture 是过去签发的。
提取属性
提取 AttributeStatement 的属性,创建 /root/saml/attrs.csv。
第一行为 name,value,按名称升序排列。
AttributeStatement 中有多个 Attribute,每个又可能有一个以上的值。请把名称与值配对并排序。
检查签名元素
创建 /root/saml/sig.txt,共三行。
reference_uri=<Reference 의 URI 속성>
digest_alg=<DigestMethod 의 Algorithm 속성>
signature_alg=<SignatureMethod 의 Algorithm 속성>
关键在于 Reference 的 URI 指向什么。确认它与我们实际读取的节点相同,是签名验证的一半。
恢复 IdP 证书
将 XML 中的 X509Certificate 内容恢复为 PEM 格式,
并保存到 /root/saml/idp.pem。
openssl x509 -in /root/saml/idp.pem -noout -subject 必须成功。
X509Certificate 元素只包含 PEM 的正文(base64)。必须补上 header、footer 和换行,openssl 才能读取。
编写 SP 验证检查清单
编写 /root/saml/checklist.md。列出 SP 必须验证的
至少 7 项,并为每项补充一行验证失败时的风险。
正文中必须出现 Audience、NotOnOrAfter、InResponseTo、Destination、Issuer、
서명、재사용 七个词。
“签名是否有效”与“我读取的节点是否就是被签名的节点”是两个不同问题。这一区别正是 XML Signature Wrapping 攻击的核心。