SAML Assertion/verification 如何与 X509 public 密钥配合使用

How SAML Assertion/verification works with X509 public key

在我的 asp.net MVC 网站中,我实施了 SSO,其中 IDP/ADFS 发送 SAML 响应,我验证 SAML 令牌以允许用户访问该网站。我正在使用自定义代码(使用 System.IdentityModel.dll 和 System.IdentityModel.Services.dll 库来验证 SAML 响应和令牌,而不是 having.net 框架使用 web.config 设置进行检查)。

到目前为止,代码似乎工作正常,但由于我是这个领域的新手,我担心黑客能够通过正确构造的 SAML 响应绕过验证。最近尝试了SAML token生成部分,发现如果token是智能生成的,我的token验证码是可以绕过的

在高层次上,这就是我验证令牌的方式:

  1. 从请求中提取安全令牌。
  2. 通过使用提供的 X509 public 密钥(存在于 SAML 响应中)检查摘要值来验证令牌是否有效且未被修改
    1. 提取用户claims/identity并允许访问。

我担心的是,如果黑客创建 SAML 令牌(就像我自己的令牌生成器代码)并在响应中添加 public 密钥并将其 post 添加到我的网站,我的网站将成功验证响应,因为响应本身格式正确且已签名。这是一个合理的担忧吗?我缺少一些基本的验证来处理这种情况? 我可以想到以下选项来降低风险:

  1. 检查 URLReferrer 并确保 SAML 响应是 post 来自预期实体的。我不确定是否有办法操纵 URLReferrer。

  2. 避免使用响应中出现的 public 键来验证摘要值。我可以在我这边存储 X509 证书并使用它来验证响应。黑客将无法使用相同的证书签署响应,因为他没有私钥。如果这是正确的方法,有人可以建议我如何指示“tokenhandler”忽略响应中存在的 public 键并使用显式 public 键吗?

  3. 有没有一种方法可以让我对 IDP 进行后端调用,可以调用 Web 服务,并检查我的网站收到的令牌是否确实是由 IDP 生成的?

由于我是新手,我可能缺少基本的 SAML 验证概念。所以请让我知道我的担忧是否合理。 这是我用来验证响应的示例代码。

public ActionResult SAMLAssert()
    {
        var fam = new WSFederationAuthenticationModule();
        var request = this.HttpContext.Request;

        // Get the security token from the SAML response
        var securityToken = fam.GetSecurityToken(request);

        var config = new SecurityTokenHandlerConfiguration
        {
            CertificateValidator = X509CertificateValidator.None,
            IssuerNameRegistry = new CustomIssuerNameRegistry(),

        };
        config.AudienceRestriction.AudienceMode = AudienceUriMode.Never;

        var tokenHandler = new Saml2SecurityTokenHandler
        {
            CertificateValidator = X509CertificateValidator.None,
            Configuration = config,

        };

        //// validate the token and get the ClaimsIdentity out of it
        var identity = tokenHandler.ValidateToken(securityToken);
        bool isSuccess = identity[0].IsAuthenticated;

        // Code to retrieve the claims/user information from the token
        //....
        return View();
    }

这里是习俗 "IssuerNameRegistry"。

public class CustomIssuerNameRegistry : IssuerNameRegistry
    {
        public override string GetIssuerName(SecurityToken securityToken)
        {
            X509SecurityToken x509Token = securityToken as X509SecurityToken;

            return x509Token.Certificate.Subject;
        }


    }

我怀疑自定义 class 是有问题的部分,因为它没有进行任何验证。

  1. 我认为您不应该检查引荐来源网址的值。它很容易被欺骗。

  2. IdP 使用其私钥对发送给您的响应进行签名。攻击者无权访问此私钥。因此,如果攻击者想要欺骗签名,他将需要使用自己的证书并将他的 public 密钥放入令牌中。虽然您认为验证代码使用嵌入式 public 密钥来验证签名是正确的,但 AFAICT 它还做了一件事:检查 public 密钥是否被我们的机器信任。可以通过将 public 密钥添加到您的 Windows 证书存储 -> TrustedPeople 来建立此处的信任。虽然我没有所有的代码来验证这一点,但它应该以这种方式工作,或者它应该为您提供一种方法来做到这一点。 如果您可以完全控制 IdP,替代嵌入式 public 密钥(又名 X509Data),您可以仅使用密钥名、主题名称和指纹。但是它们提供了多少安全性以及如何实现超出了这个问题的范围。

  3. 不,SAML 协议并非设计为以这种方式工作。与此最接近的是使用 Artifact 流,其中 IdP 仅 returns 一个 Artifact 到您的应用程序,它需要向 IdP 发出 ArtifactResolve 请求以获取实际响应。参见 What is the purpose of a SAML Artifact?。但是您仍然需要验证收到的响应的签名。