When using SAML, we have two methods for starting Single Sign-On (SSO): SP-initiated or IdP-initiated. Both have their use cases, but one is more secure than the other. No points for guessing from the title.
IdP-Initiated SSO vs SP-Initiated SSO
Service Provider (SP) initiated SSO involves the SP creating a SAML request, forwarding the user and the request to the Identity Provider (IdP), and then, once the user has authenticated, receiving a SAML response & assertion from the IdP. This flow would typically be initiated by a login button within the SP.
Identity Provider (IdP) initiated SSO involves the user clicking on a button in the IdP, and then being forwarded to an SP along with a SAML message containing an assertion. This flow would typically be initiated by a page within the IdP that shows a list of all available SPs that a user can log into. Another common use case of this flow is to allow users to bookmark the IdP login page.
When using SP-initiated SSO, a modern SAML solution will do the following:
- Generate a request ID and include it in the SAML request message
- Generate a relay state (either (random) application state or just as a simple CSRF mechanism) and include it in the SAML request URL
- Securely store the two values before redirecting to the IdP (think a cookie or a server-side cache)
When the SP receives a SAML response, it will fetch the stored request ID and relay state values and check them against the received
InResponseTo and relay state values.
This allows us to prove that we are expecting an assertion (proven by the presence of the request ID and relay state) and that the response is intended for us (by matching the request ID and relay state).
This validation procedure is similar to the OpenID Connect usage of the state and nonce parameters.
When using IdP-initiated SSO, we do not get the same assurances as SP-initiated. Instead, the SP receives unsolicited SAML messages and assertions, and loses any protocol mechanism that allows them to detect whether that message has been stolen or replayed.
The Dangers of SAML IdP-Initiated SSO
IdP-Initiated SSO is highly susceptible to Man-in-the-Middle attacks, where an attacker steals the SAML assertion. With this stolen SAML assertion, an attacker can log into the SP as the compromised user, gaining access to their account.
It can also allow for attacks where an attacker can intercept the SAML assertion and replace it with another, causing the user to log in as the attacker.
This leaves Service Providers in a tricky place. An SP can see that the message and assertion are valid since it was issued by the expected issuer and signed with the expected key, but they cannot verify that a malicious party did not steal the assertion.
Another common complaint regarding IdP-Initiated SSO is that it can overwrite existing sessions within an SP. However, this is more often an implementation detail rather than a limitation in the protocol’s approach.
Modern IdP-Initiated SSO
We can try and mitigate some of the flaws in this flow using the following techniques:
Follow the Specification
Section 4.1.5 (Unsolicited Responses) of the SAML 2.0 profiles specification states that an SP must ensure that any unsolicited SAML responses received do not contain an
InResponseTo value. This prevents responses generated using SP-Initiated SSO from being stolen and re-used.
Use the Minimum Possible Response Trust Length
When validating SAML responses and assertions, you validate when they were issued and when they expire. The IdP can state when to no longer trust it, but the SP also gets a say. So, make sure that’s as low as possible. After all, the response will probably be generated by the IdP and immediately sent across to the SP. That shouldn’t take more than a few seconds.
Unfortunately, clock skew between the IdP and SP can be an issue with this validation (it is for many of our customers), so you’ll need to find an acceptable middle ground.
To try and prevent responses/assertions from being re-used, a replay detection mechanism could be implemented. By remembering what responses have already been seen, we can at least stop a stolen message from being reused. However, this approach does not prevent the response from being stolen and then used, or another response from being injected.
Rock Solid Knowledge SAML Implementation
Our SAML component supports SP-initiated and IdP-initiated SSO for both Service Provider and Identity Provider implementations.
To detect replayed SAML responses during the IdP-initiated SSO, our SAML Service Provider component uses your implementation of
IDistributedCache to remember SAML response IDs that it has previously received. By default,
IDistributedCache falls back to an in-memory implementation. For production scenarios, we recommend using a non-volatile cache such as Redis or SQL Server.
IdP-initiated SSO is disabled by default, and you will need to switch it on explicitly. Check out our SAML documentation for enabling IdP-initiated SSO in your SAML Service Provider and SAML Identity Provider.
SAML IdP-initiated SSO definitely has its flaws; however, by taking lessons learned from modern applications and protocols, we can try and mitigate some of these concerns. Unfortunately, we cannot prevent assertion theft and injection, but we can at least stop replay attacks.
These mitigations do push yet more complexity onto the service provider to combat the limitations of the IdP. I’ve seen some articles advocate IdP-initiated SSO due to the fact that an SP might not be capable of creating requests, but now instead, they must start caring about replay attacks. In my opinion, this is more complex to do in a load balanced environment than simply remembering a relay state in the context of the browser.
Check out our SAML component page for more information.