SAML2 HTTP 重定向绑定 URL 详解

6 阅读5分钟

在单点登录场景里,浏览器会被重定向到 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:

  1. 对 AuthnRequest XML 文档进行 DEFLATE 压缩,再进行 Base64 编码,形成可传输的字符串;
  2. 将上述编码结果作为 SAMLRequest 参数,并对其进行 URL 编码,确保在查询字符串中合法;
  3. 可选地添加 RelayState 参数,用于在请求与响应之间维持会话状态,其长度应限制在 80 字节以内;
  4. 指定 SigAlg 参数来告知对查询字符串签名时所用的算法,例如 http://www.w3.org/2001/04/xmldsig-more#rsa-sha256
  5. 构造待签名字符串(包括已编码的 SAMLRequest、RelayState 与 SigAlg),使用私钥对其进行数字签名后,将签名结果 Base64 编码并 URL 编码后放入 Signature 参数;
  6. 浏览器接收到重定向响应后,将根据此 URL 访问 IdP,并在 IdP 端验证签名及解码 SAMLRequest,从而继续完成认证流程。

URL 参数详解

SAMLRequest 参数

SAMLRequest 是核心参数,包含了经过 DEFLATE 压缩与 Base64 编码后的 AuthnRequest XML 内容。压缩可大幅减少查询字符串长度,编码后可确保二进制数据以 ASCII 字符形式传输。使用 URL 编码后,它会变成类似 fZJRb8IgFIX... 这样的长字符串。

RelayState 参数

RelayState 用于在 SP 与 IdP 之间携带应用自定义状态信息,例如登录前的目标页面地址。标准建议将其长度限制在 80 字节以内,并在响应端保持原样返回,以便 SP 将用户重定向回原始目标页面。

SigAlg 参数

SigAlg 指明签名使用的算法 URI,例如 http://www.w3.org/2001/04/xmldsig-more#rsa-sha256,对应 RSA-SHA256 签名算法。它必须与 Signature 参数中的签名算法一致,以便接收方正确验证签名。

Signature 参数

Signature 包含对待签名字符串进行数字签名后的结果,先对字符串进行签名,再使用 Base64 编码,最后 URL 编码。待签名字符串的构造顺序为:

SAMLRequest=<urlencoded SAMLRequest>
[&RelayState=<urlencoded RelayState>]
&SigAlg=<urlencoded SigAlg>

如果不包含 RelayState,则省略相关部分。接收方会按照相同方式重新构建待签名字符串并使用已知公钥验证签名。

参数编码与签名流程

在浏览器跳转 URL 生成过程中,需要依次执行多种编码与签名操作来保障传输安全与可靠性:

  • DEFLATE 压缩:对原始 XML 进行压缩,降低 URL 长度并支持 GET 方法。
  • Base64 编码:将压缩后的二进制数据转成可打印字符序列,确保在 HTTP 协议中正确传输。
  • URL 编码:将 Base64 编码中可能出现的 +, /, = 等字符转换为 %xx 形式,以满足 URL 查询参数规范。
  • 签名计算:使用私钥对包含全部查询参数名称与编码值的字符串进行签名,算法由 SigAlg 参数指定,输出再经过 Base64 与 URL 编码后写入 Signature 参数。

安全性考虑

在 HTTP-Redirect 绑定中,消息暴露在 URL 查询参数中,容易被中间人窃听或篡改。签名机制与压缩编码的结合可有效抵御篡改攻击,但仍然建议全程使用 HTTPS 以防止敏感信息泄露。此外,RelayState 中的自定义信息不应包含过多敏感数据,且应配合短生命周期管理。

Node.js 示例

下面示例演示如何在 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,随后对 SAMLRequestRelayStateSigAlg 进行排序拼接,再使用 RSA-SHA256 算法对其签名,最终将签名结果填入 Signature 参数。浏览器访问该 URL 后,即可完成对 IdP 的跳转并携带必要信息。

在理解该 URL 的时候,关注这四个查询参数的编码与签名流程,能够帮助准确掌握 HTTP-Redirect 绑定的实现原理与安全保证。