Tag Archives: SAML

Use of clockskew in SAML Assertion in SAML Protocol

In this post, I will explain how we can use clockskew in SAML Assertion to avoid a general problem when a Service Provider (SP) receives a SAML response from Identity Provider (IdP) and if both of them are on different machines which is generally the scenario.

So when a user accesses a SP application, SP sends an authentication request to IdP and IdP sends a SAML response to SP. The typical SAML response looks like below:

<saml2p:Response Destination="https://localhost:8443/spring-security-saml2-sample/saml/SSO"
                 ID="_8ced26c57648ea420c5e27ac7f3d78b3"
                 InResponseTo="a3ei0bej41ie0dg738jji60271f1bca"
                 IssueInstant="2018-10-31T18:39:05.109Z"
                 Version="2.0"
                 xmlns:saml2p="urn:oasis:names:tc:SAML:2.0:protocol"
                 xmlns:xsd="http://www.w3.org/2001/XMLSchema"
                 >
    <saml2:Issuer xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion">https://sts.betterjavacode.com/sts</saml2:Issuer>
    <ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
        <ds:SignedInfo>
            <ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" />
            <ds:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1" />
            <ds:Reference URI="#_8ced26c57648ea420c5e27ac7f3d78b3">
                <ds:Transforms>
                    <ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature" />
                    <ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#">
                        <ec:InclusiveNamespaces PrefixList="xsd"
                                                xmlns:ec="http://www.w3.org/2001/10/xml-exc-c14n#"
                                                />
                    </ds:Transform>
                </ds:Transforms>
                <ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256" />
                <ds:DigestValue>k6k9seVwZT8qEfainB+HDUJmv7wLwPjJxRARgrFLk3E=</ds:DigestValue>
            </ds:Reference>
        </ds:SignedInfo>
        <ds:SignatureValue>
eYl7/5Bg4f2ZtXYl1Tp/ZW4CIqna1sjHjVSB/zhj3NHncQu7pq9PBTdWz0JEaE8+efICz7trDQvI
J0tih35Vg7NqDtOeps7vMttV0XvrnWKIXEqrscyNgkhwy+KW2oprKIWTJq6y3Z0kQ7n2DlTRkfAE
yLRPxqr8AZU+77Tbv4DmTQVp934ivibUaMNU79nkhMMo7vf0ldpeNCe5Ll5Q7nxgNBCrL4mhbGdU
DNJDVqJIhQZDJUYhBVZSgLo6mYLxf0ndQr5+GdcvO3i8VlooH49I5ZO6LfsBlNiSU6WezC0Fcz8J
biodrT3h62Jny8FKUgYfXA8i0ZoAerCxHmLGFQ==
</ds:SignatureValue>
        <ds:KeyInfo>
            <ds:X509Data>
                <ds:X509Certificate>MIICpTCCAY2gAwIBAgIHAKQk/Tty7TANBgkqhkiG9w0BAQsFADATMREwDwYDVQQDDAhJbmZvclNU
UzAeFw0xNzExMTAxNjIwMjVaFw0xOTExMTAxNjIwMjZaMBMxETAPBgNVBAMMCEluZm9yU1RTMIIB
IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuy40GofgfSldiu7AXRSWQZo8YIUmdJOXV5gd
YUiIBggcm5J+jttd21av9AWBDLtvekIqgG+nM1SEarDNlCgrnThqDtdsBDnT+B5FQowLwqNQZb0C
6PSWccp484OLHhv3YwbjV/IxgW6wlv2EejPF93NTPc5TkkpIKIEARJwKiUvuwzkcX1atD8HESj8/
5wZhK3g3MSv+CaJb1Y732U2aa34RI6HYRNlKRsqRj55SZnPs3AmIBL/rdVbt5eYJdK+jXSOMQ9Sb
TNkgTAYKnlSQP8NhsNc1Y5ZJ231KD3rSqHEXwpKVXFuEgtBnCL9utqyUU0Fu9PNJJj0hf9otMJw2
pQIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQAsqxA/+llqNtJq/VPtAa0XwtldB7/4Gew5LgfU5Yj7
/ruHuxCh03xfnLyjo0bxrFeMbhHMg4MzJhgah0zTIl4WMRI781CiMxGfMdvQAHhY5hgpryqKUTbH
qcIoW1WkTEL+TrOSk2gjL/n/4KsUJ3XKeI4j4h2RDvbIF8u9cbrB5RupEwJlo/pK3mCr3GQGFxVX
4yli+AysMmz8JWz2hWQad0QQr9KIYmZdgauNt7uYn4u+7cngtTtQF/EMoZU1pAFI5lDP13mxI1rR
eYGzoKhGfHIy7TiXYvqX0vFomzkgr6D0DnRlRkQR8J5EVtnGZ7sF50KlNviG+IxrPNndUdeX</ds:X509Certificate>
            </ds:X509Data>
        </ds:KeyInfo>
    </ds:Signature>
    <saml2p:Status>
        <saml2p:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Success" />
    </saml2p:Status>
    <saml2:Assertion ID="_43514fee402111bfa8ef07e2bb3e1816"
                     IssueInstant="2018-10-31T18:39:05.109Z"
                     Version="2.0"
                     xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion"
                     >
        <saml2:Issuer>https://sts.betterjavacode.com/sts</saml2:Issuer>
        <saml2:Subject>
            <saml2:NameID Format="urn:oasis:names:tc:SAML:2.0:nameid-format:transient"
                          NameQualifier="https://sts.betterjavacode.com/sts"
                          SPNameQualifier="https://localhost:8443/spring-security-saml2-sample/saml/metadata"
                          >-74375301938178262591541011144180</saml2:NameID>
            <saml2:SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer">
                <saml2:SubjectConfirmationData InResponseTo="a3ei0bej41ie0dg738jji60271f1bca"
                                               NotOnOrAfter="2018-10-31T18:44:05.109Z"
                                               Recipient="https://localhost:8443/spring-security-saml2-sample/saml/SSO"
                                               />
            </saml2:SubjectConfirmation>
        </saml2:Subject>
        <saml2:Conditions NotBefore="2018-10-31T18:39:05.109Z"
                          NotOnOrAfter="2018-10-31T18:44:04.109Z"
                          >
            <saml2:AudienceRestriction>
                <saml2:Audience>https://localhost:8443/spring-security-saml2-sample/saml/metadata</saml2:Audience>
            </saml2:AudienceRestriction>
        </saml2:Conditions>
        <saml2:AttributeStatement>
            <saml2:Attribute Name="http://schemas.microsoft.com/claims/Tenant">
                <saml2:AttributeValue xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                                      xsi:type="xsd:string"
                                      >ABC Company</saml2:AttributeValue>
            </saml2:Attribute>
            <saml2:Attribute Name="http://schemas.microsoft.com/claims/Identity">
                <saml2:AttributeValue xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                                      xsi:type="xsd:string"
                                      >testuser</saml2:AttributeValue>
            </saml2:Attribute>
            <saml2:Attribute Name="http://schemas.xmlsoap.org/claims/CommonName">
                <saml2:AttributeValue xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                                      xsi:type="xsd:string"
                                      >Test User</saml2:AttributeValue>
            </saml2:Attribute>

            <saml2:Attribute Name="http://schemas.microsoft.com/claims/Email">
                <saml2:AttributeValue xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                                      xsi:type="xsd:string"
                                      >test.user@betterjavacode.com</saml2:AttributeValue>
            </saml2:Attribute>
            <saml2:Attribute Name="http://schemas.microsoft.com/claims/Session">
                <saml2:AttributeValue xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                                      xsi:type="xsd:string"
                                      >-65311121040491585821541011144177</saml2:AttributeValue>
            </saml2:Attribute>                        
        </saml2:AttributeStatement>
        <saml2:AuthnStatement AuthnInstant="2018-10-31T18:39:05.109Z"
                              SessionIndex="31079178551135950171541011144177"
                              >
            <saml2:AuthnContext>
                <saml2:AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport</saml2:AuthnContextClassRef>
            </saml2:AuthnContext>
        </saml2:AuthnStatement>
    </saml2:Assertion>
</saml2p:Response>

Now SP and IdP are on different machine. And there can be a case that their clocks are not synced. So when IdP generates the response, it has an element SubjectConfirmationData that contains NotBefore and NotOnOrAfter. Subject indicates details about the user and relying party who is receiving the assertion. Relying party has to verify that the assertion is coming from the right issuer.

NotBefore – Time before which subject can not be validated.

NotOnOrAfter – Time on or after which subject has expired.

Similarly, there will be Conditions element in the assertion and this element will also contain attributes NotBefore or NotOnOrAfter. These two attributes indicate at what time assertion will be valid and after what time, it will be expired.

Problem

Now if we take into account a scenario where system clocks of IdP and SP are not in sync and if Idp clock is ahead of Sp clock, how do we handle this scenario?

Solution

So as described in above scenario, then assertion validation will fail because of NotBefore time will be greater than the time on SP machine.

To solve this issue, we can either make sure IdP and SP clocks are in sync. But this seems human-dependent solution and getting clocks in sync is not always practical solution.

Another way this can be solved by adding a clockskew in SAML assertion. A relying party that is consuming the assertion should apply small clockskew to time to accommodate small changes in time.

private int clockSkew = 120;

SubconfirmationData subjectConfirmationData = subjectConfirmation.getSubjectConfirmationData();

if(subjectConfirmationData.getNotOnOrAfter().plusSeconds(clockSkew).isBeforeNow())
{
 // throw error - Subject Confirmation Data has expired
}

/***
*
*
*/

if (conditions.getNotBefore() != null && conditions.getNotBefore().minusSeconds(clockSkew).isAfterNow()) 
{
      throw new SAMLException("Assertion condition notBefore is not valid");
}

if (conditions.getNotOnOrAfter() != null && conditions.getNotOnOrAfter().plusSeconds(clockSkew).isBeforeNow()) 
{
      throw new SAMLException("Assertion condition notOnOrAfter is not valid");
}

 

References

  1. SAML Specification
  2. SAML assertion with NotBefore and NotOnOrAfter

SSO with PingFederate using SAML

Ping Federate is a third party vendor that provides capabilities for Single Sign On (SSO) using either SAML or WS-Federation protocol. I recently worked on a project where we had to provide these capabilities to applications.

Here I document how I achieved this through SAML protocol.

SAML stands for Security Assertion Markup Language and it is an open-standard data format for exchanging information related to authentication and authorization (Source-Wikipedia – SAML ). SAML is used mostly for web browser SSO.

Ping Federate plays the role of an Identity Provider or Service Provider depending on what purpose you are using it for.

In this particular post, we will be seeing how an SP-initiated SSO works with Ping Federate.

SSO with Ping  Federate-

Create an SP connection in Ping Federate

Firstly, create a unique connection for your SP service in Ping Federate, this unique connection will be identified by Ping Federate with Entity Id which you will create in Ping Federate. Provide an Assertion Consumer Service (ACS) URL in your connection in Ping Federate. Basically, Ping will send a response back at ACS URL. There is a step-by-step process to create an SP connection in Ping Federate.

You will need to specify a protocol for this connection. For our post purposes, we are using SAML 2.0. What binding to use? Post, Redirect, Artifact, SOAP. For this post, we will be using Post or Redirect.

During the process, you also provide an IdP adapter in the connection. IdP adapter is nothing but a way of authentication – how do you want a user to be authenticated? Through an HTML form or Windows Account?

You will also need to provide a signing certificate if you are going to send a signed login request to Ping Federate.

Once you create a connection, you set that connection as ACTIVE in ping.

Changes on SP Side

Now when you send a Login request to ping, it will post on the protocol endpoint URL from the ping side. So Ping provides certain static endpoints for your connection. If Ping is installed on a server called abc.com, the endpoint for Ping will be abc.com/idp/SSO.saml2 and this is where you will post your login request. Here is a sample Login request looks like


<samlp:AuthnRequest xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" ID="_bec424fa533dj2ff020502892fghjjf221" Version="2.0" IssueInstant="2016-02-10T11:39:34Z" ForceAuthn="false" IsPassive="false" ProtocolBinding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" AssertionConsumerServiceURL="http://abc.bloodycoders.com/login/saml2/sp/AssertionConsumerService.php">
<saml:Issuer xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion">
urn:mace:bloodycoders.com:services:abc.bloodycoders.com
</saml:Issuer>
<samlp:NameIDPolicy xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" Format="urn:oasis:names:tc:SAML:2.0:nameid-format:persistent" SPNameQualifier="abc.bloodycoders.com" AllowCreate="true" />
<samlp:RequestedAuthnContext xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" Comparison="exact">
<saml:AuthnContextClassRef xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion">
urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport
</saml:AuthnContextClassRef>
</samlp:RequestedAuthnContext>
</samlp:AuthnRequest>

Ping Federate will verify the request based on entity id and where it needs to send the response. If the request is valid, it will send a response. On the SP side, you then verify the response if it is coming from an authentic source.

(I have not included a response back from Ping Federate for post purposes).

If you enjoyed this post, send me a response here.