Search code examples
c#saml-2.0adfsx509certificate2ws-federation

How SAML Assertion/verification works with X509 public key


In my asp.net MVC web-site, I have implemented SSO in which the IDP/ADFS sends the SAML response and I verify the SAML token to allow users to access the web-site. I am using customized code (using System.IdentityModel.dll and System.IdentityModel.Services.dll libraries to verify the SAML response and token, instead of having.net framework do the checking with web.config settings).

So far the code seems to be working fine, but since I am new to this domain have concern that hacker would able to bypass the validation with properly constructed SAML response. Recently I tried my hands on the SAML token generation part and I realized that my token verification code can be bypassed if the token is generated intelligently.

At high level this is what I am doing to verify the token:

  1. Extract the security token from the request.
  2. Verify the token is valid and untouched by checking the digest value with the provided X509 public key(which is present in the SAML response)
    1. Extract the claims/identity of the user and allow the access.

My concern is that if hacker creates SAML token (like my own token generator code) and add public key in the response and post it to my web site, my web site will successfully validate the response as the response itself is well formed and signed. Is this a valid concern? I am missing some basic validations to handle this scenario? I can think of following options to mitigate the risk:

  1. Check the URLReferrer and make sure that the SAML response is posted from the expected entity. I am not sure if there is a way to manipulate the URLReferrer.

  2. Avoid using the public key present in the response to validate the digest value. I can store the X509 certificate on my end and use it to validate the response. The hacker won’t able to sign the response with the same certificate as he won’t have the private key. If this is the right way, can someone suggest me how to instruct “tokenhandler” to ignore the public key present in the response and use explicit public key?

  3. Is there a way by which I can make back-end call to IDP, May a web-service call, and check that the token received by my web site is indeed generated by the IDP?

Since I am a newbie, I might be missing the basic SAML validation concept. So please let me know if my concern is legitimate. Here is the sample code I use to validate the response.

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();
    }

And here is the custom "IssuerNameRegistry".

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

            return x509Token.Certificate.Subject;
        }


    }

I have suspicion that the custom class is the problematic part as it’s not doing any validation.


Solution

    1. I don't think you should check the referrer value. It can easily be spoofed.

    2. The IdP uses its private key to sign responses sending to you. An attacker just doesn't have access to this private key. Therefore, if an attacker wants to spoof a signature, he will need to use his own certificate and put his public key to the token. While you are right that validation code uses the embedded public key for verifying signature, AFAICT it also does one more thing: check if the public key is trusted by our machine. The trust here can be established by adding the public key to your Windows Certificate store -> TrustedPeople. I don't have all the code to verify this though but it should work this way, or it should provide you a way to do that. If you have full control over the IdP, alternative to embedded public key (aka X509Data), you can use keyname, subject name, thumbprint only. But how much more secure they provide and how to implement are out of the scope of this question.

    3. No, SAML protocol wasn't designed to work that way. The closest thing to this is to use Artifact flow in which the IdP only returns an Artifact to your application and it needs to make an ArtifactResolve request to the IdP to get the actual Response. See What is the purpose of a SAML Artifact?. But then you still need to verify signature of the received Response.