Learning From a Recent Security Vulnerability in Persona

Lloyd Hilaiel

The purpose of our “Bug Bounty Program” is to encourage contributors to test and experiment with our code for the purposes of improving its functionality, security and robustness. Through this program we were recently alerted to a potential security flaw in one of our web services products, Persona.

In short, the issue reported could have allowed an attacker to impersonate any gmail.com or yahoo.com user on a website that supports Persona.  We have no evidence that it was exploited, and the issue has been resolved in production.  You can read a summary of the timeline in our recent disclosure.

Background: Persona and Identity Bridging

 

To understand the vulnerability, a little background on Persona is required:  Persona is a federated protocol which allows a user to verify their ownership of an email address by directly authenticating with their email provider.

If a user’s email provider does not support Persona, Mozilla can temporarily act as a trusted third party and vouch for the user. Mozilla will only vouch for a user if they can prove ownership of their email address, often by clicking a confirmation link mailed to that address. To streamline this process, Mozilla recently introduced a feature known as “Identity Bridging.” Bridging allows Mozilla to use existing public APIs, like OAuth or OpenID, to verify a user’s identity directly in the browser, without needing to send a confirmation email.

You can learn more about Identity Bridging in the original announcement. Generally, bridges are designed as a temporary measure. They allow Mozilla to deliver a streamlined login experience in advance of email providers supporting Persona themselves. Currently, Mozilla operates two bridges, one for Google and one for Yahoo users, both based on OpenID.

When logging into a website, Persona prompts the user to type an email address. Persona then performs discovery against the email’s domain to determine if the given email provider supports Persona.

If so, the user is sent directly to the provider to authenticate. If not, the user is sent to Mozilla’s fallback.

The fallback maintains a list of email domains and associated identity bridges. If a bridge is available, the user is sent to that bridge to authenticate. If not, Mozilla sends the user a confirmation email to verify ownership of the address.

Authenticating at a bridge occurs as a normal OpenID transaction:

  1. The bridge redirects users to the OpenID endpoint of the respective provider, requesting confirmation of ownership of an email.
  2. Subsequent to user interaction, the user is redirected back to the bridge with information supplied by the email provider in GET parameters.
  3. Online server-server verification of the authenticity of the request parameters is performed.
  4. Once verified, the user’s email address is considered confirmed, and the bridge issues a certificate which allows the user to assert ownership in a separate offline operation.

 

What Went Wrong?

 

The flaw that was discovered was in step #4 above.  Using OpenID to verify a user’s ownership of an email address is tricky process.  OpenID Attribute Exchange is the standard used that allows an email provider (an identity provider or IdP) to encode arbitrary attributes (such as email addresses) in the GET parameters of the URL of the website using OpenID (the relying party, or RP).  OpenID authentication further allows the IdP to provide a signature that covers a subset of the returned values.

The vulnerability could have allowed a malicious user to trick Persona into trusting unsigned parameters. To demonstrate the nature of related exploits, let’s explore a valid response and associated, simplified attacks:

 

A Valid OpenID Provider Response

 

To begin, in order to successfully confirm ownership of an email address via OpenID, one must verify that the OpenID endpoint is authoritative for the email in question.  For gmail.com addresses, Google is authoritative – for yahoo.com addresses, Yahoo is authoritative.

Once you’ve identified the correct endpoint for the email in question, the simplified example below (many required fields are removed) demonstrates a valid verification response:

  openid.op_endpoint: https://www.google.com/accounts/o8/ud
  openid.signed: op_endpoint,ns.ext1,ext1.mode,ext1.type.email,ext1.value.email
  openid.sig: <base 64 encoded signature>
  openid.ns.ext1: http://openid.net/srv/ax/1.0
  openid.ext1.mode: fetch_response
  openid.ext1.type.email: http://axschema.org/contact/email
  openid.ext1.value.email: example@gmail.com

In this response, the openid.sig parameter contains a signature that covers the fields enumerated in the openid.signed parameter.  Included in the signature are the openid.ext1 fields, which contain the email address of the user at Gmail.  Popular OpenID providers provide a service that allows online validation of signatures. However verification of a signature is not enough, and many popular OpenID libraries may appear deceptively simple.

To explore the nature of data validation required, let’s walk through a couple possible attacks.

 

Attack #1: Unsigned Email Attribute

 

Consider the following hypothetical return parameters from the IdP:

  openid.op_endpoint: https://www.google.com/accounts/o8/ud
  openid.signed: op_endpoint
  openid.sig: <base 64 encoded signature>
  openid.ns.ext1: http://openid.net/srv/ax/1.0
  openid.ext1.mode: fetch_response
  openid.ext1.type.email: http://axschema.org/contact/email
  openid.ext1.value.email: example@gmail.com

This set of response data has a valid signature, but the signature does not cover the email address returned by the IdP. Many popular OpenID libraries will return a response to calling code that is identical, regardless of whether return attributes are signed.  This would allow an attacker to generate a valid OpenID response which does not include an email address, and then append their own claimed email address.  This simple attack combined with behavior common in OpenID libraries can lead to vulnerabilities.

 

Attack #2: Data Extraction Flaws

 

Depending on the RP’s OpenID implementation, it’s possible that an attacker can spoof an email address despite the presence of expected values that are valid and signed.  Consider the following:

  openid.ns.foo: http://openid.net/srv/ax/1.0
  openid.foo.mode: fetch_response
  openid.foo.type.email: http://axschema.org/contact/email
  openid.foo.value.email: victim@gmail.com
  openid.op_endpoint: https://www.google.com/accounts/o8/ud
  openid.signed: op_endpoint,ns.ext1,ext1.mode,ext1.type.email,ext1.value.email
  openid.sig: 
  openid.ns.ext1: http://openid.net/srv/ax/1.0
  openid.ext1.mode: fetch_response
  openid.ext1.type.email: http://axschema.org/contact/email
  openid.ext1.value.email: example@gmail.com

In this case, there is a proper signed email present in the response, however an unsigned email also precedes it.  Online verification will succeed, and depending on how value extraction is implemented, this might be enough to cause the incorrect (unsigned) email to be extracted and returned to calling code, resulting in unauthorized access to victim@gmail.com‘s account.  In a popular OpenID implementation we found a lack of rigor in value extraction that could lead to vulnerabilities of this nature.

 

How Can Others Avoid These Pitfalls?

 

The simplified attacks in this post give you a flavor of the attention to detail required when using OpenID to confirm email ownership.  The same cautions apply to any sensitive information that you rely on that is returned from OpenID.

Our suggestions are twofold:

For library authors: Go the extra mile.  If you perform extraction of values returned by the IdP, perform rigorous validation, and expose the trust level of the return value.  For instance, in JavaScript, rather than returning an email property,  scope that property under an .unsigned or .untrusted namespace.  Make it blindingly obvious what values are, and are not, trustworthy.

For site owners using OpenID:  Give your implementation a deep look.  Ensure that identifying values are signed, types are as you expect, and parsing is rigorous.  For the IdPs that you use, research their suggested best practices.

 

Parting Thoughts

 

This was a serious vulnerability in Persona, however, our evidence indicates it was addressed before users were affected.  Moving forward, we will be conducting a review of the bridging code and the third-party libraries it uses, and our Bug Bounty program will continue to be a vehicle for researches to disclose vulnerabilities in our critical services. Finally, we will continue to address discovered vulnerabilities with all due effort, and openly and responsibly disclose them.