My First Project: Improving the User Experience of HTTPS-Only By Adding Suggestions

This blog post will be more of a storytelling than the last ones, where we discussed web protocols and the HTTPS-Only feature in Firefox. This time we will be looking more into what I’m actually doing and more importantly the things I learned on the way.

The goal

HTTPS-Only tries to upgrade every HTTP connection to HTTPS. In the majority of cases that goes smoothly and you are surfing the web more securely without even noticing. But there are some few cases where that does not work, because the server you want to connect to does not have a HTTPS certificate. Then you end up at an error page. And this error page is my play field. 

The goal is to implement a suggestions box on this error page that offers different secure alternatives, e.g. by checking if the site can be reached by adding www.

www Suggestion

Sometimes websites don’t have a certificate for all subdomains. So they might have one for www.example.com but not for example.com. So it is possible to connect to www.example.com over HTTPS but not to example.com. If that is the case a button to www.example.com will show up in the suggestion box. 

The journey

I started working on the www button back in October as part of my application for this outreachy internship. It sounded simple at first glance, but I quickly encountered Murphy’s Second Law:  Nothing is as easy as it looks. (That is the version I found on the internet … I remember a professor telling me it was ‘If a problem looks easy, you haven’t understood it yet.’ Which is what I was going for, but it seems to be that professor’s interpretation of Murphy’s Second Law). 

The Firefox browser consists of hundreds of thousands of  files. When you download the browser you only get one file that is specifically tailored for your operating system (eg. Linux, MacOS or Windows) but those are already pre-build versions. When you work on the code you download the source code so you can work in all those different files and change things. (If you are curious you can find out how to get the source code for Firefox). 

Hacky Solution for the Application

I started to implement a simple fetch on the error pages JavaScript file, checking if I could reach a website over HTTPS when putting a www. in front of the URL and if so display a button. I soon found out that browsers – and browsers with all the security features enabled even more so – don’t want me to send random requests through the internet. I crashed with the Content Security Policy and CORS. CORS stands for Cross-Origin Resource Sharing and is a mechanism that checks if the sub-resources that are being loaded have the same scheme (e.g. HTTP or HTTPS) and the same domain. But for correct implementation of CORS the URL example.com is not the same domain as www.example.com so trying to fetch that violates this security feature. I found that the error message in the browser console was different for ‘there is no such page’ and ‘there is such a page but you are not allowed to see it because of CORS’ so I tried to use that error message but soon learned that due to security restrictions the error message is not accessible from my JavaScript file. So after some frustration I found this very helpful blog post that explains how CORS can be tricked by sending the request through a proxy because somehow CORS only has a problem with browser-server-communication and not with server-server-communication so if I add a server in between my request, it does work. That is of course no way to really implement this but in order to have something for my application it was good enough. 

More serious and less hacky approach – Writing Product Code

Then my outreachy internship started and I got back to my www button. Now it was time to approach this in a more sustainable way. And with the beginning of my outreachy internship I also have the luxury of a mentor whom I can ask all the questions I find on the way. 

iframes

After discussing the problems I ran into he suggested implementing the test if I can reach a secure page in an iframe. iframes are Inline Frames and are often used for loading external content like maps onto a webpage. This approach does not clash with CORS but I soon learned that not every page is happy being loaded into an iframe. Maps are usually fine with it because that is a major use case to be shown at the contact page of a company but websites can tell the browser that they don’t appreciate being loaded onto any other site (by stating that in the response header). Which again makes a lot of sense that you can’t just include other pages into yours. So that wasn’t the solution. 

ParentPages

Then I learned about ParentPages. Not all the files in the Firefox source code have the same privileges. The error page file I was working on only has content privileges but there are ParentPages with browser privileges. So these parent pages are allowed to do the request I want. The next step is to tell the appropriate parent page to do that. So I send a message from my error page to the parent file, the parent file checks if a secure connection exists and sends the result back. And to prevent possible attacks on that system you need to register those messages in the Remote Page Access Manager file. The Remote Page Access Manager builds upon a declarative security model and defines what files are allowed to send which messages and read which information. 

Localization

Once the www-button worked I learned that I should not hard code text into the code because not everyone will look at the error page using English as their browser language. I have seen different languages in programs before but I never really thought about how this is done. Instead of writing the text directly into the error page file you use l10n-tags and these tags get filled by the language set in the browser setting. 

l10n stands for localization (because localization has 12 letters so l – 10 letters – n … it’s a very weird way to shorten words. When I first saw l10n i tried to figure out if it was leetspeak so lion or some kind of word play l-ten-n but nothing made any sense. I only found very few examples for this shortening scheme and they mostly seem to be related to translation: l10n for localization, i18n for internationalization, t9n for translation and a11y which looks like some leetspeak for ally but stands for accessibility … in one of the most inaccessible ways … there might be a metaphor hidden somewhere).

But apart from my irritation with the naming scheme it is a very fascinating concept. I only have to write the text blocks in English and if my button ever ends up in the future Firefox, others will write the fluent files (that’s what those text files are called) in a lot of different languages so my button could be used by people with whom I don’t share a common language. That is absolutely wild. 

Prefs

I thought I was pretty much done at this point, but it’s my first contribution to a huge codebase and there are so many things I was not aware of at the beginning. The next step was to hide my code behind a pref (preference). That is kind of like a switch that can be turned on or off. And my code only does things, when this switch is turned on. That makes the browser more robust. Of course I have to test my code before I can submit it but if it turns out later that it does cause errors it is much easier to just turn the pref off for a quick fix before someone would have to go over it and remove all the parts that are causing troubles. Or in a less pessimistic scenario it is also possible to switch it off for specific use cases when you don’t want to get suggestions. 

Tests

And of course one has to write tests before sending out any software even if it is just a little button. So I had to figure out how to test this thing. This part is still not fully done, as I ran into troubles just yesterday but the most interesting part so far was that I’m not allowed to use random domains I use to test in my code but only servers who are registered with the testing suite. And none of them had this specific case where there is only an HTTP version available but with www in front a secure HTTPS version is possible. So I had to add fake servers to the test servers and give them fake certificates. 

What’s next 

So it is still work in progress and all for a small button that will send you to a secure page. But at this point in time I wrote 364 lines of code touching 17 different files. It is a very different experience than any coding I have done in the past. The lines of code are not very much considering how long I’m working on it, but I never touched that many different files, often only changing one line after reading up on the concept for hours. 

Since my patch is currently still work in progress I was handed a clean up bug so I can have a patch landed last week. I haven’t written a single line of code for that bug but deleted 45 lines of code removing an unused pref. And these lines of code will be missing from all future Firefoxes! And I did that! And apart from being a great milestone for me to land code for the first time, it was also really nice how appreciative all the code reviewers were. They happily accepted my patch and even applauded me for removing code. Apparently removing code is even harder and more appreciated than adding code – or as a friend would say: “Only deleted code is good code”. But of course I am also looking forward to adding code. And if everything goes well there will be a button that I made in the Firefox browser on the computers of people I never met in my life.