BrowserID is a step in the wrong direction

Mozilla Persona, the public face of the BrowserID initiative, is a fresh, dead simple, and compelling vision for how authentication should work on the web. Unfortunately, it’s also poorly executed and fundamentally flawed. If you are considering using BrowserID for authentication on your website, the following is my personal assessment on the shortcomings, flawed assumptions, and inherent weaknesses of the current implementation as well as the overall architecture that Mozilla has defined.

What is BrowserID?

BrowserID is a protocol “which enables users to sign into websites using their email address.” BrowserID is designed to use email addresses as a unique identifier for your users, and then try to prove the person at the keyboard ‘owns’ their email address, in order to log them in. So first and foremost, BrowserID requires that usernames must be email addresses. There are certainly pros and cons to this approach, and plenty of cases where it could be awkward, but I don’t fault BrowserID for going this route. In fact, the entire architecture is fundamentally based on this assumption. If emails-as-usernames don’t work for your site, then BrowserID won’t work for you. But for now, I’m just going to assume that emails make perfect usernames for the rest of the article, so let’s put that aside.

Mozilla’s Persona service bills itself as production quality and ready to use today.  But BrowserID is actually still a work-in-progress, with a long-term vision of how authentication should work in the future.  As such, there is BrowserID as it actually works today, and there is BrowserID how Mozilla claims it could work in the future. I will focus mainly on how BrowserID works today, since you can’t secure your website with a fairy tale, but I will also try to touch on the future vision as well.

It’s worth noting that in agreeing to Persona’s terms of service, you agree that you will not use it “for any purpose where an accurate verification of identity has critical or life-threatening consequences or has other significant or financial consequences.” It’s an interesting caveat which you should consider carefully.

How does it work?

The easiest way to learn how BrowserID works is to just try it out on your own site.  It literally should take no more than 10 minutes to get working. One nice thing about handing your site’s security over to a third party is you don’t have to work so hard. In the case of BrowserID, you don’t even have to register your own site with Mozilla, you can just start using it.

Before I get into the underlying architecture and code, I want to walk through what the end user will actually see, because it’s the easiest way to get a feel for what’s happening.

I implemented it myself, by turning this:

Into this:

So, what happens when you click the ‘Login’ button?  After including a Javascript file from browserid.org, you call a single client-side javascript function when the Login button is clicked, and your users will get a popup dialog which takes them through a process to create a new account:

The user will then receive an email sent from Mozilla’s servers that looks like this:

Note that anywhere you see ‘localhost’ or ‘localhost:19461’ what you would actually see is the URL for your own site; it’s just that I’m testing it on a local server, and BrowserID is picking that up automatically.

When you click the link in the email to ‘finish registration’, a new browser window will open that looks like this:

If your default browser (which opens when you click the email link) does not share cookies with the one you were originally using (for example, if you had originally visited the site using Incognito mode) then Persona.org will actually ask you to type your password in again, before logging you in and redirecting you back to the site:

Basically what’s happening is your browser won’t have a session established with Persona, so they want to confirm you really know your password. The ’email address’ field is not editable, and it appears to be completely ignored by the system (if you edit it with your browser’s inspector, you still need to enter the password for, and will be logged into, the account they are expecting).

After you click finish, you will find yourself logged in on the original site back where you started. At this point you actually have two browser windows open and both of them are now logged into the site; the original window where everything started, and the new window that opened when you clicked the link in the email.

If you then click the ‘Log out’ link in the site’s toolbar, you will be logged out from the site, but you will actually stay logged into Persona. If you try to log back in to the site, you will see this screen:

At this point, you can simply click the ‘sign in’ button, and you will be immediately signed back into the site.  Magic?  No; the certificate that Persona.org gave you is still valid, stored in your localStorage, so your browser can prepare it and send it off to the site to log you back in, sans password.

That completes the whirlwind tour of BrowserID. Maybe I should have just made a video…

Initial Weaknesses and Attack Vectors

Phishing Attacks

The first problem users will encounter with Persona is the strange popup dialog it lives in. One of the first things you teach someone on the internet is to be careful where they type their credentials. Always check the URL. Phishing attacks are extremely widespread, and BrowserID makes them easier to pull off by asking users to enter their credentials into a foreign popup.

Seriously: ‘Mozilla Foundation [US] https://login.persona.org/sign_in’.  To the average user, it might as well read: ‘Godzilla Foundation [CN] https://login.personify.cn/sign_in’.  It would be trivially easy to spoof this dialog to collect usernames and passwords. Teaching users that it’s OK to type their credentials into popups is just crazy. You have to keep it very simple if you want to educate more than the top 1% of users, and ‘never type your password in a popup’ is rule we should keep.

The Fake Logout

The second problem for users is the fake logout process. I say ‘fake’ because even though you click ‘Log out’ on the site you are using, your certificate is still valid, your session is still alive and well on Persona.org, and there’s no indication otherwise. If you click ‘Login’ again, you might be surprised to see your email address is pre-populated, and with one click (and no password) you are back in.

Even if you quit the browser, someone can relaunch the browser, go back to the site, click ‘Log in’ and they will see your email listed, and be able to simply click ‘sign in’ to access your account. This flies in the face of everything we’ve ever taught our users about how to keep their accounts secure.

I couldn’t find a good way to tell Persona to not to remember you. ‘Remember me’ should always be opt-in, but Persona does it by default. Every once in a while, after logging in, I did get this message:

I couldn’t figure out why it was displayed sometimes but not others, but in any case I think this is a very strange way to ask users if they want to be remembered.  “We’ll prompt you for your password in an hour” is an incredibly ambiguous statement, and seems to be setting users up for even more phishing attacks. Maybe this would make more sense if BrowserID was ever actually baked into the browser as theorized, and not just shimmed using a Persona.org popup dialog, but for now this is misleading at best.

First, you won’t actually get prompted in an hour for your password if the site issues you a cookie that’s valid for longer than that.  And second, even if you click ‘No’ in response to this dialog, after you Logout of the site, you can still log back in without a password. The only way I could find to actually clear out my cached credentials from Persona.org was to go back into the Login dialog and click the ‘This is not me’ button.

A Minor Bug

I ran across a minor bug while trying to collect screenshots for the blog. At one point I clicked the Login button and I was presented with the following screen:

The Login dialog opened as if an email address should have been listed, but no email was present.  Clicking the ‘sign in’ button produced the beautiful Javascript alert() saying “The selected email is invalid or has been deleted.” Very comforting. I’m not exactly sure what went wrong here…

A Closer Look – What’s Happening Underneath?

This is where distinguishing the long-term vision, and the present day reality of BrowserID and Persona gets a little difficult. For now, what’s essentially happening is users will register with Persona.org with an email and password, validate their email address with Persona.org, and then be able to login to Persona.org with that email/password combination. Users stay logged in with Persona.org, and in return, Persona.org gives your users certificates saying, “Yes, this is the owner of user@example.com.”

Back on your site, what you get back from your Javascript call is the certificate from Persona.org, but the user’s browser includes a two minute self destruct feature, Mission Impossible style. You send the whole bundle from client-side Javascript back to your server, and from your server you send it to Persona (actually, https://browserid.org/verify) so they can validate it for you, because validation is complicated enough they don’t want sites trying to do it themselves. If you get ‘okay this is user@example.com‘ back from the verify API, then you can mark your user’s session as authenticated as user@example.com.

A Door Left Wide Open

Unnecessary Complexity Lead to a Critical Vulnerability

It turns out the Mozilla developers weren’t wrong when they thought validating the user certificate was so complicated that individual sites shouldn’t try to do it themselves. Up until July 2, 2012, end users could alter their certificates to become any user they wanted (see Cert Chaining Vulnerability). The vulnerability was a result of needless complexity of the protocol, poor specifications, and insufficient test coverage. Mozilla’s fix was to simply remove the vulnerable functionality from the active code path in the ‘verify’ function, since there wasn’t even a purpose for the vulnerable functionality in the first place.

Since the errant functionality lived within the browserid.org/verify API, it was fixed easily enough, and didn’t require any changes to sites that supported BrowserID. But the lesson is clear enough – your site is entirely reliant upon that API to log in users.  Based on the recommended implementation by Mozilla, that API has absolute authority to authenticate any user.

A Window of Attack

Client-side Javascript is Not a Secure Channel

It’s worth repeating that client-side Javascript handles the entire data package being sent back to your server, which you forward onto the verify API. This is like waking up and discovering we’re back in 2002 in terms of web security. It’s been over a decade since we could mark cookies as HttpOnly but now we’re back to passing what is essentially a username/password (albeit with a 2 minute expiration period) via client-side scripting. Any XSS attack will be able to intercept these messages, which can be reused by the attacker to login as the end user within that 2-minute window.

An Uncertain Future

Persona.org is supposed to be a ‘bootstrapping mechanism’ in theory, but is a central authority in practice

Even though Mozilla designed BrowserID so that individual email providers can become their own identity providers and issue their own certificates directly, in practice the certificates are almost always issued by Persona.org, to users that are logged into Persona.org.

Once a domain advertises support for BrowserID, the end-user’s browser would go to that domain to authenticate the end-user and get a certificate, bypassing Persona for the certificate issuance at least, although many sites would still be using Persona’s centrally-hosted validator.

Anyone who wants to issue their own certificates can do so either for their own domain, or, for any domain that they have been delegated authority. The validator ensures the certificates it receives should be trusted by making an HTTPS request to a well-known URL on the certificate issuer’s domain, to retrieve the public key for the certificate.  Then, if the certificate issuer’s domain is not the same as the email address domain, it makes another HTTPS request to the email’s domain to see if the issuer is approved.

So for example, if you are user@gmail.com, and you want to present a certificate back to the browser issued by google.com, here’s how it would work:

  • The end-user’s browser would get JSON from gmail.com/.well-known/browserid and use the provided URLs to authenticate the end user and retrieve a certificate
  • The validator would see google.com was the issuer of the cert, and retrieve the public key for the certificate from google.com/.well-known/browserid
  • The validator would see the domains don’t match, and check gmail.com/.well-known/browserid to see if ‘google.com’ is approved to issue certs for gmail.com
  • If gmail.com properly delegated google.com to issue certificates for it, then the validator will accept the certificate

Examining the validator code that’s on GitHub, one interesting omission is that even if a domain does advertise support for BrowserID, that doesn’t exclude certificates from being accepted that are generated by Persona.org. So even after taking control of your own authentication, Persona.org certificates will apparently still be accepted by the central validator.

Even if one day we did have millions of domains hosting their own BrowserID authentication and provisioning services, would you really want your office IT department to have full access to any site that you log into using your office email address? Will users understand that the email address they are logging in with implicitly grants access to any administrator of that domain? It makes you wish for the good old days where a shared secret let you in.

A Case of Misplaced Trust

When it comes to authenticating users onto your website, you want a system designed with the underlying principle of trust, but verify. I find it simply audacious that sites are expected to pass along a magic string to an external API, and simply check if it returns ‘result.status == okay‘ and then happily authenticate the session as result.email.

BrowserID is authentication based on the lowest common denominator of security — the email address.  From there, it injects multiple parties (the certificate issuer, and the validator) and gives them both absolute power to grant anyone they want access to any account on your system.

  • Does BrowserID tell you if the user just recently reset their password? No.
  • Does BrowserID tell you how many invalid login attempts have been made since the last valid one? No.
  • Does BrowserID tell you if this is the first time this specific machine has logged in as this user? No.
  • Does BrowserID encourage users to choose different passwords for different sites? No, it actually precludes it.
  • Does BrowserID/Persona require a reasonable password strength? No, any 8 characters will do, even ‘password’.

The fact is, when BrowserID says ‘okay’, you don’t have any assurance the person on the other end even knows the user’s password.

What security platforms should be doing is providing tools to enhance a website’s ability to securely validate its own users, not requiring the website to abdicate all responsibility to a black box that just says ‘okay, let foo in’.

Discussion

Please feel free to discuss on HackerNews. If you have the karma, please feel free to post to Reddit as well, and I will link to discussion there as well.