在单点登录场景里,浏览器会被重定向到 IdP 的端点且携带一系列查询参数用于传递 SAML 请求的内容及签名信息。该请求通过 HTTP-Redirect 绑定方式发送,其中 SAMLRequest、RelayState、SigAlg 与 Signature 四个参数合力保证了消息的有效载荷及完整性。参数传输过程中会先对 XML 报文进行 DEFLATE 压缩,再使用 Base64 编码,最后通过 URL 编码嵌入到 URL 查询字符串中。在对参数进行签名时,签名算法则由 SigAlg 参数指定,并在签名计算后将结果通过 Base64 与 URL 编码写入 Signature 参数中,以确保消息未被篡改并验证发送者身份。
SAML2(Security Assertion Markup Language 2.0)是一种用于在身份提供者(IdP)与服务提供者(SP)之间传递认证与授权信息的开源标准。SAML 协议支持多种传输绑定(Binding),其中 HTTP-Redirect 绑定利用浏览器重定向与 URL 查询参数来传递 SAML 请求,实现了对 GET 方法的兼容。在这种方式下,SAML 消息首先被压缩、编码并签名,随后嵌入到跳转 URL 的查询字符串里,以便在安全性与兼容性之间取得平衡。
服务提供者在发起单点登录时,将构造一个 AuthnRequest XML 文档,随后应用以下步骤生成 HTTP 重定向 URL:
http://www.w3.org/2001/04/xmldsig-more#rsa-sha256
; SAMLRequest 是核心参数,包含了经过 DEFLATE 压缩与 Base64 编码后的 AuthnRequest XML 内容。压缩可大幅减少查询字符串长度,编码后可确保二进制数据以 ASCII 字符形式传输。使用 URL 编码后,它会变成类似 fZJRb8IgFIX...
这样的长字符串。
RelayState 用于在 SP 与 IdP 之间携带应用自定义状态信息,例如登录前的目标页面地址。标准建议将其长度限制在 80 字节以内,并在响应端保持原样返回,以便 SP 将用户重定向回原始目标页面。
SigAlg 指明签名使用的算法 URI,例如 http://www.w3.org/2001/04/xmldsig-more#rsa-sha256
,对应 RSA-SHA256 签名算法。它必须与 Signature 参数中的签名算法一致,以便接收方正确验证签名。
Signature 包含对待签名字符串进行数字签名后的结果,先对字符串进行签名,再使用 Base64 编码,最后 URL 编码。待签名字符串的构造顺序为:
SAMLRequest=<urlencoded SAMLRequest>
[&RelayState=<urlencoded RelayState>]
&SigAlg=<urlencoded SigAlg>
如果不包含 RelayState,则省略相关部分。接收方会按照相同方式重新构建待签名字符串并使用已知公钥验证签名。
在浏览器跳转 URL 生成过程中,需要依次执行多种编码与签名操作来保障传输安全与可靠性:
+
, /
, =
等字符转换为 %xx
形式,以满足 URL 查询参数规范。 在 HTTP-Redirect 绑定中,消息暴露在 URL 查询参数中,容易被中间人窃听或篡改。签名机制与压缩编码的结合可有效抵御篡改攻击,但仍然建议全程使用 HTTPS 以防止敏感信息泄露。此外,RelayState 中的自定义信息不应包含过多敏感数据,且应配合短生命周期管理。
下面示例演示如何在 SP 端使用 Node.js 对 SAMLRequest 与相关参数进行压缩、编码与签名。
const zlib = require('zlib');
const crypto = require('crypto');
const querystring = require('querystring');
// 原始 AuthnRequest XML
const xmlRequest = `<samlp:AuthnRequest xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol"
AssertionConsumerServiceURL="https://sp.example.com/consume"
ProtocolBinding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"
ID="_abc123" Version="2.0"
IssueInstant="2025-04-23T12:34:56Z">
<saml:Issuer xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion">https://sp.example.com/</saml:Issuer>
</samlp:AuthnRequest>`;
// DEFLATE 压缩并 Base64 编码
function encodeRequest(xml) {
const deflated = zlib.deflateRawSync(Buffer.from(xml, 'utf8'));
return deflated.toString('base64');
}
// 构建签名
function signQuery(params, privateKeyPem) {
const sorted = ['SAMLRequest', 'RelayState', 'SigAlg']
.filter(k => params[k] !== undefined)
.map(k => `${k}=${encodeURIComponent(params[k])}`)
.join('&');
const signer = crypto.createSign('RSA-SHA256');
signer.update(sorted);
const signature = signer.sign(privateKeyPem, 'base64');
return encodeURIComponent(signature);
}
// 主流程
const samlRequest = encodeRequest(xmlRequest);
const relayState = 'https://sp.example.com/home';
const sigAlg = 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha256';
const privateKeyPem = `-----BEGIN PRIVATE KEY-----
...Your RSA Private Key...
-----END PRIVATE KEY-----`;
const params = { SAMLRequest: samlRequest, RelayState: relayState, SigAlg: sigAlg };
params.Signature = signQuery(params, privateKeyPem);
const redirectUrl = 'https://demand.com/saml2/idp/sso/ondemand.com?' +
querystring.stringify(params);
console.log('Redirect to IdP URL:', redirectUrl);
在上述示例里,DEFLATE 与 Base64 编码结合 URL 编码封装了 AuthnRequest,随后对 SAMLRequest
、RelayState
与 SigAlg
进行排序拼接,再使用 RSA-SHA256 算法对其签名,最终将签名结果填入 Signature
参数。浏览器访问该 URL 后,即可完成对 IdP 的跳转并携带必要信息。
在理解该 URL 的时候,关注这四个查询参数的编码与签名流程,能够帮助准确掌握 HTTP-Redirect 绑定的实现原理与安全保证。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。