Using BrowserID and Content Security Policy together

While looking into why BrowserID logins on Libravatar didn't work on Firefox, I remembered that I had recently added Content Security Policy headers. Here's what I had to do to make BrowserID work on a CSP-enabled site.

This is what the login button looked like before CSP:

<form id="browserid-form" action="/account/login_browserid" method="post">  
<input id="browserid-assertion" type="hidden" name="assertion" value="">  
<input style="display: none" type="submit">  
</form>  

<a href="javascript:try_browserid()">Login with BrowserID</a>  

<script type="text/javascript">  
function try_browserid() {  
navigator.id.getVerifiedEmail(function(assertion) {  
    if (assertion) {  
        document.getElementById('browserid-assertion').setAttribute('value', assertion);  
        document.getElementById('browserid-form').submit();  
    }  
});  
}  
</script>

The hidden form is there because the assertion needs to be sent to the application via POST to avoid leaking it out, but otherwise the code is pretty straightforward.

Now of course, with CSP turned ON, there are two problems:

So we can start by converting the login link to:

<a id="browserid-link" href="#">Login with BrowserID</a>  

<script src="browserid_stuff.js" type="text/javascript">

then moving the try_browserid() function to a separate file to be served from the same domain and finally hooking it up to the try_browserid() function using Javascript (in that same browserid_stuff.js file):

var link = document.getElementById('browserid-link');  
link.onclick = try_browserid;  
link.addEventListener('click', try_browserid, false);

Exposing the right X-Content-Security-Policy header

In order to load the Javascript code from browserid.org, we need the following as part of the policy:

script-src https://browserid.org

but that's not enough since the BrowserID login form seems to use some sort of <iframe> trick and so we need to add this extra permission as well:

frame-src https://browserid.org

Here is the final policy I ended up setting (using Apache mod_headers) for the Libravatar login page:

<Location /account/login>  
Header set X-Content-Security-Policy: "default-src 'self'; frame-src 'self' https://browserid.org ; script-src 'self' https://browserid.org"  
</Location>
Ideal OpenSSL configuration for Apache and nginx

The TLS world is moving fast and this post is now out of date! You might want to look at the SSL configuration recommended by Mozilla.

After recently reading a number of SSL/TLS-related articles, I decided to experiment and look for the ideal OpenSSL configuration for Apache (using mod_ssl since I haven't tried mod_gnutls yet) and nginx.

By "ideal" I mean that this configuration needs to be compatible with most user agents likely to interact with my website as well as being fast and secure.

Here is what I came up with for Apache:

SSLProtocol TLSv1  
SSLHonorCipherOrder On  
SSLCipherSuite RC4-SHA:HIGH:!kEDH

and for nginx:

ssl_protocols  TLSv1;  
ssl_ciphers  RC4-SHA:HIGH:!kEDH;  
ssl_prefer_server_ciphers   on;

Cipher and protocol selection

In terms of choosing a cipher to use, this configuration does three things:

Testing tools

The main tool I used while testing various configurations was the SSL labs online tool. The CipherFox extension for Firefox was also quite useful to quickly identify the selected cipher.

Of course, you'll want to make sure that your configuration works in common browsers, but you should also test with tools like wget, curl and httping. Many of the online monitoring services are based on these.

Other considerations

To increase the performance and security of your connections, you should ensure that the following features are enabled:

  • SSL session caching with a session store shared between all of your web servers
  • HSTS headers to let browsers know that they should always visit your site over HTTPS

Note: If you have different SSL-enabled name-based vhosts on the same IP address (using SNI), make sure that their SSL cipher and protocol settings are identical.