Skip to content

安全与最佳实践

如果你想得到当前实现最稳妥的行为,直接遵循这几条:

  • 保持 strictSecurity: true
  • 默认不要开启 allowLegacySha1
  • 默认不要开启 allowCertificateUsageMismatch
  • 优先信任 metadata,而不是手写证书和 endpoint
  • 使用默认算法组合:RSA_SHA512 + AES_256_GCM + RSA_OAEP_MGF1P

大多数入口会按下面顺序处理:

  1. 严格解析 XML
  2. 做 schema 验证
  3. 做签名验证和必要的解密
  4. IssuerDestinationInResponseTo、时间窗口等字段级校验

重点不是“尽早验签”,而是先把结构歧义和 schema 异常挡在外面。

  • 拒绝 XXE / DOCTYPE / ENTITY 注入
  • 非法 XML 直接失败
  • 不再把解析器错误噪声写到 stderr
  • 普通 SAML 消息走 XSD 校验
  • SOAP Artifact 消息走 XSD 校验
  • Metadata 走独立 metadata XSD 校验
  • Response 只接受单个顶层 Assertion 或单个顶层 EncryptedAssertion
  • 发现 wrapping 风险结构会直接拒绝
  • 默认拒绝 SHA-1 家族签名算法
  • 默认要求证书用途匹配
  • ArtifactResponse 只允许一个 resolved message
  • 多个 resolved message 会直接报错,不会退化成“取第一个继续解析”

这是最重要的全局安全姿态开关。

const sp = ServiceProvider({
...config,
strictSecurity: true
});

如果你把 strictSecurity 设成 false

  • 部分历史兼容行为会被放宽
  • 如果你没有显式指定 allowLegacySha1,当前实现还会连带放开 SHA-1 兼容

所以它不适合拿来做“临时试一下”的万能开关。

默认值是 false

只在你明确知道对端仍然依赖 SHA-1,并且你已经接受风险时,才显式开启:

const sp = ServiceProvider({
...config,
allowLegacySha1: true
});

更推荐的做法是推动对端升级到 SHA-256 以上。

默认值是 false

当前实现会校验证书用途,例如:

  • signing cert 应用于签名验证
  • encryption cert 应用于加密用途

只有在对端 metadata 非常不规范、而你又必须兼容时,才考虑显式开启:

const idp = IdentityProvider({
...config,
allowCertificateUsageMismatch: true
});

当前实现更适合以 metadata 为准来驱动这些关键决策:

  • endpoint
  • 证书
  • want*Signed
  • NameIDFormat

这样做的好处是:

  • 减少本地手写配置漂移
  • 让验签和证书选择更一致
  • 和当前高层实体构造逻辑完全对齐
  • 对端实体优先从 metadata 初始化
  • 本端实体把 endpoint、证书和私钥都配置完整
  • RelayStateInResponseTo、主要状态码记录到业务日志
  • 把 Artifact 当成引用而不是载荷
  • 在业务层实现 artifact 的单次消费和过期控制
  • 关闭 strictSecurity 当成长期兼容方案
  • 不看证书用途,直接混用 signing / encryption cert
  • 收到 schema 不合法的消息后还试图继续往下解析
  • 允许多断言或多 resolved message 模糊通过

如果你遇到这些问题,可以下探到低层模块排查:

  • 签名验证失败:看 SamlLib.verifySignature()
  • Metadata 结构可疑:看 validateMetadata()
  • SOAP 包层异常:看 validate(xml, true)Soap
  • 字段抽取异常:看 Extractor

但正常业务接入仍然建议优先停留在高层实体 API。