Browser Crypto

What is Browser Crypto?

In recent years, web applications that utilize client-side cryptography (or ‘in-browser’ cryptography) to protect users’ private information have become increasingly prevalent. Examples of such applications include end-to-end encrypted chat/email/messaging services, cryptocurrency wallets, encrypted cloud file storage/sharing services, and many more. Perhaps one of the most widely-used applications to leverage in-browser cryptography is the encrypted email service Protonmail.

The idea is that crypto code (usually written in javascript) running in the user’s web browser encrypts the users secrets, so that only ciphertext reaches the server. Web applications that utilize client-side cryptography, or ‘in-browser’ cryptography, are often referred to as ‘browser-crypto’ applications.

Many browser-crypto applications claim that because all of the cryptography is done on the client-side (in the web browser), and only ciphertext reaches their servers, the operators of these applications have ‘zero-access’ to users’ private information. For example, see Protonmail’s claim here.


Is Browser Crypto Secure?

In 2011, security researcher Thomas Ptacek penned an oft-cited blog article titled, ‘Javascript Cryptography Considered Harmful’ (unfortunately, Ptecek’s article is no longer available at its originally published URL, or its subsequently published URL, but an archived version of his article can be found here). In his article, Ptacek elaborated on several reasons why browser-crypto was fraught with security problems.

Now, more than a decade later, we have solutions to many of the problems that Ptacek cited. Thanks to the Web Crypto API, we now have a Cryptographically Secure Pseudorandom Number Generator (CSPRNG) available in javascript. The Web Crypto API also provides us with a secure method of managing keys in browser-crypto. Subresource integrity provides us with a way of ensuring the integrity of dependent javascript and css files. Cross-site scripting (XSS) attacks can now be effectively mitigated using Content Security Policy (CSP) and sound coding practices. However, there is one problem that Ptacek cited in 2011 article that stubbornly persists today, which he refers to as the ‘browser crypto chicken-and-egg problem’.


The 'Browser Crypto Chicken-and-Egg Problem'

In essence, the ‘browser crypto chicken-and-egg problem’ can be summed up as follows: if you can't trust the server with your secrets, then how can you trust the server to serve secure crypto code? One could argue that a rogue server admin (or an attacker that has gained access to the server) could maliciously alter the browser-crypto code served by the server, such that the code captures the user’s private keys or plaintext information, and sends these secrets back to the server (or somewhere else). Ptacek argues that browser-crypto applications cannot legitimately claim that they have ‘zero-access’ to their users’ private information, because the operators of these applications can simply alter their browser-crypto code any time, and capture the users’ secrets.

In the desktop software world, users are able to protect themselves from threats stemming from tampered-with files, by cryptographically verifying the integrity of files before opening or running these files. After downloading a file, but before opening or running the file, there is an opportunity for the user to verify that the checksum hash of the file matches an expected checksum, or that a digital signature on the file was made by a trusted signer. If this verification fails, then this is an indication that the file has been changed since the expected checksum was taken, or since the file was signed.

But, in a typical web application, there is no (practical) method for the user to verify the integrity of a web page containing browser-crypto code. The page is served, then the page is immediately rendered by the browser, then browser-crypto code in the page is executed as the user interacts with the page. To make matters even more complicated, new pages are continuously served in place of previous pages as the user interacts with the application; and often these pages are dynamically generated by the server. Clearly, this model does not lend itself well to providing users with a practical way of verifying the integrity of the browser-crypto code in these applications.


Solution to the Browser Crypto Chicken-and-Egg Problem

We propose a two-pronged solution to the ‘browser crypto chicken-and-egg problem’.

First, instead of the traditional model for web applications, where new dynamically-generated web pages are served in place of previous pages as the user interacts with the application – we propose that browser-crypto applications should be designed using the Single Page Application (SPA) model, where a static web page is served at the beginning of the user’s session, and this static page remains loaded in the user’s browser throughout the user’s session. This page (and/or its dependent files) should contain all client-side crypto code needed to support all in-browser crypto operations throughout the user’s session. Requests are sent from the static page to the server by way of AJAX or XHR calls to the server, and the static page is updated with the server’s responses to these calls.

This design, based on the SPA model, with a single static web page that remains loaded in the user’s web browser throughout the user’s session, allows for the expected checksum hash of this static page to be known in advance, and allows for the static page to be digitally signed by one or more signers. This allows for the integrity of the static page to be verified at the user’s end, after the page is served, but before the user begins to interact with the page.

The second prong of the solution is the Page Integrity browser extension. The Page Integrity extension was developed to enable the user to verify the integrity of the static page, as described above. With the Page Integrity extension loaded in the user’s web browser, the integrity of the static page can be verified by the user immediately after the page is loaded, before the user begins to interact with the page. Page Integrity can be used to display the checksum hash of the static page, so that the user can verify that it matches the expected checksum. Or, Page Integrity can be used to verify that the static page was digitally signed by a trusted signer.