Wednesday, December 10, 2014

Single Sign-On: The OpenID Protocol


In one of the previous posts, we explained SSO on the example of SAML.In this post, we will introduce another popular and widely deployed SSO Protocol: OpenID (if you are not familiar with SSO, we recommend you to read SSO on the example of SAML first).



The OpenID protocol exists in different versions. While OpenID 1.0 and 1.1 are still supported and deployed in the WWW, most implementations concentrate on OpenID 2.0 – so does this post.

Notation

 

We use the following notation for a cleared parameter intent. If you have worked on OpenID before, you may be more familiar with the OpenID specific parameter values, so here is an overview:

URL.IDC openid.claimed_id The identity requested by the Client C. In the context of OpenID, the identity is a URL e.g https://me.yahoo.com/a/identityname
URL.SP openid.return_to The URL of the Service Provider (SP), e.g. https://www.openstreetmap.org/login
URL.IdP openid.op_endpoint The URL of the Identity Provider (IdP), e.g. https://open.login.yahooapis.com/openid/op/auth
α openid.assoc_handle The value that identifies the signature verification key stored on the SP as well as on the IdP.
σ openid.sig This parameter contains the value of the token's signature. In fact, OpenID uses a Hash MAC (and not a signature), but has the OpenID specification uses the term “signature”, this Post will do this as well.


There are a lot more parameters in OpenID:
  • openid.ns defines the used protocol version, e.g. http://specs.openid.net/auth/2.0
  • openid.response_nonce contains a timestamp suffixed with a nonce value
  • openid.signed holds the parameter names that are signed, e.g. claimed_id, return_to,..
  • openid.ax.*, openid.sreg.* are extension parameters that can be used to transfer additional information, e.g. an email address or a birthday.
For a better understanding of the protocol flow, we only use the parameters mentioned in the table above in this Post.

OpenID Protocol Flow

 














The OpenID protocol can be separated into three phases:
  1. In the discovery phase, the Service Provider (SP) collects information about the user (Client C) who wants to login on it.
  2. The association phase takes place next. This phase establishes a shared secret between SP and the OpenID Identity Provider (IdP).
  3. The token processing phase includes the creation of the SSO token and its transport to the SP via C's user agent (browser). The SP then verifies the token and the user is logged in if it was valid.
The picture shown above describes the OpenID login flow more precisely. It shows the following entities:
  • The Client C using his User Agent (UA) who wants to authenticate on the SP, e.g. http://www.openstreetmap.org/.
  • The ID Server and C's IdP. For a lot of publicly available OpenID Providers, this is the same Server, e.g. Google. But OpenID allows to separate them to have more flexibility. This is explained more detailed at the end of this article.
The OpenID protocol performs the following steps as shown in the picture:
  • Step 1: C wants to login at SP and sends its Identity URL.IDC.
  • Step 2: The SP starts to discover URL.IDC by requesting the URL on the ID-Server.
  • Step 3: The ID-Server returns a document containing the URL of C's Identity Provider, i.e. URL.IdPC.
  • Step 4: The SP can optionally use URL.IdPC to establish a shared secret with the IdP. Basically, this is a Diffie-Hellman key exchange. If this step is performed, IdP chooses a value α to identify the key material. α is transferred to SP, so that both entities use the same identifier to store the key. Note that α does not contain any key material, it is just a reference to it.
    In this step, SP and IdP are communicating directly to each other, thus, the exchanged messages cannot be seen in the user's browser.
  • Steps 5-6: The SP now responds to C's initial login request (Step 1) and sends an authentication request that contains URL.IdPC, URL.SP and, if Step 4 was performed, α. This is an HTTP redirect instruction and C's UA forwards this message to URL.IdPC.
  • Step 7: If C is not yet logged in on his IdP, he now must authenticate on it.
  • Step 8-9: IdPC creates a token t that contains C's identity URL.IDC, plus its own URL address URL.IdPC and URL.SP.
    IdPC then signs t by using the key identified by α. token t together with its signature σ is then sent back to C who forwards the message to the SP.
  • Steps 10-11: The SP can optionally start a rediscovery. These steps are identical to Steps 2-3. Rediscovery is used to verify, that the URL.IdP parameter returned in Step 11 is equal to the URL.IdP parameter contained in the token t.
  • Step 13-14 are described separately below.
  • Step 14: If the signature σ is valid, the SP maps URL.IDC to a local username and grants access to C.

 

Direct Verification

 

Step 4 is optional.Consequently,an SP does not need to establish a shared secret with the IdP. This is for example useful, if the SP is unable to store the key material (e.g., it has no database).
The question raised from this fact is: How can the SP verify a tokens signature without knowing the key?
The answer is called direct verification.
The protocol flow in this case is slightly changed. If no association is established in Step 4, α is not contained in Step 5-6. This leads IdPC to create a fresh secret key in Step 8 to sign the token t. The secret key itself is then only stored at the IdP. The IdP additionally creates a value α as a key identifier and puts α into t. If the SP receives the token in Step 9, it sends the whole token together with σ to IdPC in Step 12. Then, IdPC verifies the token and sends the result back to SP in Step 13.
Again, this is direct communication between the SP and the IdP and the OpenID specification assumes that there is no Man-in-the-Middle attacker (the token should be exchanged over TLS).

Discovery in Detail

 

In Step 3 of the protocol, SP receives a document that contains URL.IdPC. This document can either be HTML or an XRDS document. A minimal example of an HTML document has the following structure:


<html>
     <head>
         <title/>
         <link rel="openid2.provider" href="https://myidp.com/" />
     </head>
     <body/>
</html>

The href attribute in the link element contains URL.IdPC.
XRDS document contains the same information, but stored in XML data format:


<xrds:XRDS xmlns:xrds="xri://$xrds" xmlns="xri://$xrd*($v*2.0)" xmlns:openid="http://openid.net/xmlns/1.0">
    <XRD version="2.0">         <Service priority="0"> 
            <Type>http://specs.openid.net/auth/2.0/server</Type> 
            <URI>http://myidp.com/</URI>
        </Service>
    </XRD>
</xrds:XRDS>

An interesting side note on the XRDS discovery is, that the use of XML as a data description language offers an attacker trying to inject XML Entities. We have already described, how to apply XXE Attack on SAML, and the OpenID Discovery phase allows to apply this technique to OpenID. Reginaldo Silva applied the XXE Attack on Facebook's password recovery feature, that uses OpenID in the background and earned $33,500 as a bug bounty.

ID Server

 

Regarding the discovery documents described above, this explains the flexible design of OpenID. It allows one to store such a discovery document on its own server (http://mysite.com) but declares Google, and Yahoo as an Identity Provider. When the user then wants to login, he can simply use his own website (http://mysite.com). He is then automatically forwarded to his IdP (e.g. Google or Yahoo).
One may have noticed, that URL.IDC is not contained in Step 5.
This can be optionally included in the discovery document, so that the SP can include it in Step 5.
For HTML discovery, this is possible via a further link attribute:


<link rel="openid2.provider" ref="http://xml.nds.rub.de/simpleid/www" />


In XRDS documents, this is also possible:


<xrds:XRDS xmlns:xrds="xri://$xrds" xmlns="xri://$xrd*($v*2.0)" xmlns:openid="http://openid.net/xmlns/1.0">
    <XRD version="2.0">
        <Service priority="0">
            <Type>http://specs.openid.net/auth/2.0/signon</Type>
            <URI>http://myidp.com</URI>
            <LocalID>http://myidp.com/its_me</LocalID>
        </Service>
    </XRD>
</xrds:XRDS>


Note that in the XRDS case, the Type parameter has changed from “server” to “signon”.

Authors of this Post

Vladislav Mladenov
Christian Mainka (@CheariX)

Beliebte Posts