By Mike Sun September 19, 2012
Originally published at: https://blog.idonethis.com/openid-security
iDoneThis recently added itself to the Google Apps Marketplace and the Google Chrome Web Store, providing OpenID single-sign-on access to iDoneThis through Google accounts. It’s a great feature to have, but as we found during our implementation, one rife with security concerns. Security advisories from both Google [1] and the OpenID foundation [2] pointed out possible vulnerabilities with various OpenID implementations related to the failure to check for signed AX attributes. But the failed check for signed AX attributes by certain implementations of OpenID is really a peripheral issue. A more fundamental security threat results from the incongruent use of the OpenID protocol for trust when it was meant for identification. This article discusses how our integration of Google OpenID single-sign-on addresses the issues brought forth by the security advisories as well as the more central issue of proper OpenID usage.
Our application is built on Python+Django and as a result, we looked to django-social-auth as a framework to provide OpenID authentication with the existing Django users account framework. django-social-auth itself relies on the python-openid library to handle the actual OpenID protocol implementation. One feature that django-social-auth provides is the ability to associate existing Django user accounts with new OpenID identifies if they share the same email address. This is great for our existing users that now want to be able to access their iDoneThis account via Google Apps or Chrome. But using email addresses as identities for account associating could be a security risk.
During the OpenID authentication process, an OpenID consumer, like iDoneThis, can request and receive an OpenID user’s email address as part of the Attribute Exchange (AX) extension. Last year, based on research from Wang, Chen and Wang, Google [3] and the OpenID foundation published security advisories [1][2] that pointed out that certain OpenID implementations did not check that certain information passed through AX was properly signed. The Google advisory highlights one possible exploitation of this vulnerability:
“A specific scenario identified involves a website that accepts an unsigned AX attribute for email address, and then logs the user in to a local account on that website associated with the email address. When a website asks Google’s OpenID provider (IDP) for someone’s email address, we always sign it in a way that cannot be replaced by an attacker. However, many websites do not ask for email addresses for privacy reasons among others, and so it is a perfectly legitimate response for the IDP to not include this attribute by default. An attacker could forge an OpenID request that doesn’t ask for the user’s email address, and then insert an unsigned email address into the IDPs response. If the attacker relays this response to a website that doesn’t notice that this attribute is unsigned, the website may be tricked into logging the attacker in to any local account.”
Since our implementation utilized django-social-auth’s email-based account association feature, we were potentially vulnerable to such an attack. We weren’t able to find any documentation or reports that indicated the python-openid library checked for properly signed AX attributes, so we peformed our own security audit of the code. Our audit showed that the current version of python-openid (2.2.5) does, by default, only return properly signed AX attributes. The crucial snippet of code comes from ax.py in the python-openid library:
def fromSuccessResponse(cls,success_response,signed=True):
"""
@param signed: Whether non-signed args should be processsed.
If True (the default), only signed arguments
will be processsed.
@returns: A FetchResponse containing the data from the
OpenID message, or None if the SuccessResponse did
not contain AX extension data.
"""
self = cls()
ax_args = success_response.extensionResponse(self.ns_uri, signed)
The django-social-auth code utilizes this default behavior of python-openid, meaning we were in the clear with regard to this attack. ****
As we were researching this OpenID security issue, we found a number of people advising against the use of any AX attribute (including an email address) as a means of identification. One poster on the OpenID mailing list wrote:
“Actually, you should never use anything but the
openid.claimed_id
in the positive assertion to identify the user. This oropenid.identity
are the only values that could possibly be used to identify the user.”
The chairman of the OpenID foundation himself, wrote on his blog [4]:
“The correct behavior is to identify the user using
openid.claimed_id
. Other parameters MUST NOT be used to identify the user. If RP uses any other parameter to identify the user, then it is a security hole. This is the root of problem.”