Solving the browser crypto problem

Many developers have worked hard to port critical cryptographic functionality to JavaScript. We all agree that there is a clear requirement in a safer world to have asymmetric crypto support in the web. Porting code to JavaScript is great for users that don’t really care about the strength but only that the data is encrypted. Those people usually believe that they do not need perfect crypto, as long as it is any form of crypto it is “good enough” for them.

There are many problems with porting cryptographic functions directly to JavaScript and we see many great ideas failing on doing things properly. JavaScript cryptography is very young when comparing its lifespan to established binary solutions, such as GnuPG, that have been audited for long. GnuPG has been around since 1999 and GPG4Browsers, now OpenPGP.js, since 2011.

Auditing JavaScript ports leads to better design and less failures in time, but even when everything has been solved some problems remain due to design. Web browsers live in a very hostile world and we systematically witness XSS vulnerabilities and 0day exploits which enable dumping critical data, like private keys, as soon as either the DOM or HTML5 local storage is accessed. We can take care of badly implemented cryptography but we can’t take care of the way that the JavaScript implemented cryptography is accessible to anything that can execute JavaScript in the correct environment. As long as cryptography is done in JavaScript this will always be a huge threat.

The users that care more about their security and privacy are demanding solutions aligned with their requirements, and JavaScript implemented cryptography is by design insecure due to the surrounding threats in its domain. These users are actively choosing not to use JavaScript ported functionality but instead continue to use their local binaries that have been around and audited for decenniums more than newborn ports. And they are completely correct in doing so, because how can we actually trust JavaScript? We are stepping over the security requirements in order to deliver working solutions faster than science can keep up with it. We are impatient and we need something to work as soon as possible, especially in modern day and age with the ongoing war against free unmonitored online communication. By doing so we bypass the most important core ideas of implemented cryptography: security and privacy.

The solution

In order to expose GnuPG functionality to the web we must create an API for it which can perform cryptographic operations with non sensitive elements, such as armored public keys and private key metadata, without exposing anything of importance. The best way of doing it and successfully integrating it into web browsers is to run a webserver locally which pre accepted remotely served content can communicate with. The most important detail is that private keys should never ever be available for the web browser but instead reside in the local GnuPG keyring which the API manipulates through the local GnuPG binary.

I came up with a solution that I named pygpghttpd which I am currently working on supporting in my OpenPGP plugin for Roundcube: rc_openpgpjs. pygpghttpd is an open source minimalistic HTTPS server written in Python. pygpghttpd exposes an API enabling GnuPG’s cryptographic functionality to be used in web browsers and other software which allows HTTP requests. pygpghttpd runs on the client’s localhost and allows calling GnuPG binaries from the user’s browser securely without exposing cryptograhically sensitive data to hostile environments. pygpghttpd bridges the required elements of GnuPG to HTTP allowing its cryptographic functionality to be called without the need to trust JavaScript based PGP/GPG ports. As pygpghttpd calls local GnuPG binaries it is also using local keyrings and relying on it entirely for strength. In short pygpghttpd is just a dummy task router between browser and GnuPG binary.

pygpghttpd acts as a HTTPS server listening on port 11337 for POST requests containing operation commands and parameters to execute. When a request is received it checks the “Origin”, or if missing the “Referer”, HTTP header to find out which domain served the content that is contacting it. It then detects if the domain is added to the “accepted_domains.txt” file by the user to ensure that it is only operational for pre accepted domains. If the referring domain is accepted it treats the request and serves the result from the local GnuPG binary to the client. In the response a Cross-origin resource sharing HTTP header is sent to inform the user’s browser that the request should be permitted. If the referring domain is missing from accepted_domains.txt the user’s browser forbids the request in accordance with the same origin security policy.

The HTTPS certificate used by pygpghttpd is self signed and is not used with the intention to enhance security since all traffic is isolated to the local network interface. It uses HTTPS to ensure that both HTTPS and HTTP delivered content can interact with it.

pygpghttpd exposes metadata for both private and public keys but only allows public keys to be exported from the local keyring. The metadata for private keys is enough for performing cryptographic actions. Complete keypairs can be generated and imported into the local keyring.

For example, generating a keypair with cURL:

curl -k –data “cmd=keygen&type=RSA&length=2048&name=Alice&email=[email protected]&passphrase=foobar” -H “Origin: https://accepted.domain.com” https://localhost:11337/

Or from JavaScript:

$.post("https://localhost:11337/", {
  cmd: "keygen",
  type: "RSA",
  length: "2048",
  name: "Alice",
  email: "alice\@foo.com",
  passphrase: "foobar"
}, function(data) {
  if(data == "1")
    return true;
  return false;
});

Please see the project on Github, API documentation and example for full details.

3 Responses to “Solving the browser crypto problem”

  1. Mailserver working, updated gpg-mailgate. | ed1torial.org Says:

    […] have to save it and manually decrypt with APG.   I’d like to take  a second here and thank Niklas Szilagyi ( qnrq ) for his awesome rc_openpgpjs plugin for […]

  2. Robert Lehareng Says:

    As far as “solving the browser crypto problem” is concerned, the Javascript approach seems fine to me. But why bother to such lengths trying to secure memory content against some “online adversary” when simply disconnecting the computer does the job ? I would recommend a look at John Walker’s work on these issues http://www.fourmilab.ch/javascrypt/

  3. Tommy G Says:

    Thank you for describing an interesting approach! But I wonder: how do you solve the fact that the browser does not accept self-signed certificates from your https-deamon? I cant see how the browser will be able to communicate with this deamon at all from HTTPS delivered content due to its untrusted certificate? Please enlighten me! :-)

Leave a Reply

Leave a Reply

Your email address will not be published.

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>