Fixing OpenID Covert Redirect in the Fedora Infrastructure

So today a lot of media coverage has been produced about a security vulnerability dubbed "OpenID Covert Redirect" by the announcer, Wang Jing.

In this blog post, I will describe my understanding of this bug and the impact in the Fedora Infrastructure.

TL;DR: At the Fedora Infrastructure, we have mitigated this problem as much as we are able to.

What is this vulnerability?

The vulnerability described is basically an Open Redirect, where a malicious attacker could get a user to go to their website by clicking on a link that looks legit, in combination with an OpenID (or OAuth) session.

In this blog, I'm only going to explain about OpenID, as that is the only protocol we use in the Fedora Infrastructure of the two protocols where this vulnerability was announced for.

So let me first give a very brief overview of how OpenID works.

How does OpenID work?

First, the user goes to a website that allows him/her to sign in using an OpenID identity, and enters his/her identity URI (at Fedora services, the identity URI is automatically entered to be the Fedora OpenID).

This website then contacts the OpenID identity provider (the server behind the identity URI), redirects the user to it to authenticate, and sends the URL to which the user should be redirected after finishing authentication.

The provider will check that the return_url has the trust_root as it's base, so that it only sends the user back to the website they agreed with (it is supposed to show the trust_root so the user can check what website his details will be sent to).

After authentication, the provider redirects the user to the return_to url specified in the OpenID request.

Now what is the attack?

The problem is that most websites where you can login with OpenID want to be user-friendly, and as such redirect users back to the page they were when they clicked the login button.
Most websites implement this by adding a ?next= argument to the return_url, so they can keep track of this even with the OpenID redirects in between.

The problem occurs when the website that asked the user to sign on with his OpenID (the Relying Party), does not check this argument!
So what would happen if in the OpenID request, instead of ?next=/question/1234/, we put ?next=http://www.maliciouswebsite.com/?

The most websites running a Relying Party, just rely on this value to be completely sane when returned from OpenID, and just have something saying to redirect the user to this url.
So if I would enter http://www.maliciouswebsite.com/ as the value of this argument before authentication, it would after authentication plainly redirect the user to www.maliciouswebsite.com!

This is the core of the problem reported by Wang Jing.

Worst case scenario

The part where I do not agree with Wang Jing is on the impact of the issue.
Yes, with this bug it is possible to use a vulnerable OpenID Relying Party for an Open Redirect attack, where the malicious attacker gets the user redirected to a random website of his choice.

What I do not agree with though, is the fact that this always leaks the user's information.

(Please note I am only talking about the OpenID protocol here, I am not yet confident enough in the OAuth protocol to make any claims regarding it!).

The problem with leaking the user's data is in the fact that the redirect to the next page is only done after the OpenID protocol finished.

The OpenID protocol has two ways to redirect the user back from the Provider to the Relying Party:

It can use a standard HTTP redirect if the response is small enough (because the length of a complete GET request with parameters is limited).
If the user is sent back with this, the user would be redirected to something like http://www.relyingparty.com/login_finish?next=http://www.maliciouswebsite.com/&openid.mode=... (so here follow all of the OpenID response content sent by the Provider).
After this, the Relying Party will redirect the user to http://www.maliciouswebsite.com/.
Please note that this does not include the openid variables (which contain your user data), but those are included in the HTTP Referer header, which is a header the browser sends after a link or redirect so the new page knows where the user came from.
So in this case, the malicious website would get the information sent to the Relying Party.

The other way to redirect the user back to th Relying Party is by using an HTTP POST, with all the user's data in hidden form fields.
When this is used, the user gets sent back to http://www.relyingparty.com/login_finish?next=http://www.maliciouswebsite.com/.
As you can see, the OpenID response content is not sent in the query arguments, these are sent as POST values.
when the user is redirected to maliciouswebsite.com, the forward will be a GET request without the OpenID response.
This method will thus NOT leak the user's data, even if the vulnerable Relying Party redirects the user.

Mitigation

Because the OpenID provider can not know for sure what a valid return_to url for any Relying Party is, it is not able to check for this attack without explicitely registering the return URL per Relying Party and only accepting that URL as return_to uri.
The problem with this is that this would require any website where a user would want to sign on with his OpenID identity to register themselves at the OpenID provider, which beats the complete open purpose of OpenID where you can use any OpenID identity to sign on to any Relying Party.

One way by which the impact of this attack can be minimized to just the redirect (so without leaking user information) is by making the server always post the OpenID response by means of an HTTP POST.
As such, I have modified FedOAuth to always take this approach.

The best way to completely mitigate this attack though is by making sure that the Relying Parties do not redirect the user to a page outside of their control.

How does this impact the Fedora Infrastructure?

The Fedora Infrastructure uses OpenID to make it possible for users to sign on to our websites using a single, centralized authentication system with a consistent look and feel for the login page.
We run both an OpenID Provider and OpenID Relying Parties for this.

The OpenID Provider runs a software suite written by me, called FedOAuth (Federated Open Authentication provider).
As explained in the previous section, FedOAuth has been modified to protect as much as possible against this attack by always posting the responses (instead of using HTTP GET) which makes sure that even if the Relying Party redirects the user, the information is not leaked.
There is not much more that FedOAuth can do to mitigate this attack.
What FedOAuth has added though is it shows the user the return_to url, so that the user can verify himself where he is being redirected back to.

Our Relying Parties

We run a lot of different OpenID Relying Parties, as all of our websites where someone can login with Fedora OpenID are basically a Relying Party.

These Relying Parties run a mix of different OpenID implementations, but the majority runs either Flask_fas_openid or Flask-OpenID, I maintain both of these.

Flask-OpenID has contained a mechanism to mitigate this attack since release 1.2, called safe_roots.
The problem with this system, is that it is disabled by default as I wanted to make sure not to break current implementations.
What we have done here is enable this system on all of our apps that use Flask-OpenID.

flask_fas_openid was an OpenID Relying Party implementation designed to make porting from the old FAS integration to OpenID very easy by just changing the import.
This did not have any mechanism to prevent this attack, so I ported my safe_roots system from Flask-OpenID in a very rudimentary form over to flask_fas_openid.
Toshio "abadger1999" Kuratomi has made an emergency release of python-fedora (the package containing flask_fas_openid) with this system in and enabled by default.

After these patches, our Relying Parties are now secure against this attack.

Summary

This is defenitely not a new vulnerability, and the impact can be minimized by using HTTP POST.

I would like to thank Toshio Kuratomi, Ralph Bean and Kevin Fenzi for their help in fixing the Fedora Infrastructure Relying Parties.

I would like to thank Kevin Fenzi, Michael Scherer and Paul Wouter for notifying me of this vulnerability (and I probably miss some people who wanted to notify me but saw any of these already ping me. Thanks to them as well).

As a final word, I would like to say that if anyone finds any security vulnerabilities in FedOAuth or any of the Fedora OpenID Infrastructure, please send me an email at puiterwijk@redhat.com, preferably encrypted with my GPG key.
Thanks in advance!
(I will be setting up a real security issue email address soon and will update the blog post once I have).

Fixing OpenID Covert Redirect in Flask-OpenID

Tutorial, how to use Off-The-Record (conversation encryption) with Pidgin