Securing your HTTP API with JavaScript Object Signing and Encryption

One thing that is always difficult, enough to deserve its own book, is to secure HTTP API that interact with client-side applications: today, after a discussion about how to face the problem in our company, we bumped into the JOSE – JavaScript Object Signing and Encryption – specification.

Basically, the specification defines 4 entities:

For the sake of basic knowledge, we will only have a look at JWS and JWT / JWE now: the specifications about these entities are quite extensive and not very straightforward, so for further details you should really give them a look.

JWT

Basically, the token (JWT) is the simplest structure that you will deal with while implementing JOSE in our architecture; it is a string representation of some data base64 encoded (other types of encoding might be applied, but this is not madatory): the JWT differs from raw base64-encoded data since it also includes informations about the encoding itself, in the token’s header; by concatenating the base64-encoded version of the token header and payload (the actual data) you obtain what the specification calls signature input, which will then be used to create the signature (JWS).

JWS and JWE

After the JWT comes the JWS, which is a signed representation of the JWT; it differs from the token just because of the signature; on an higher step of the ladder comes the JWE instead, which lets you encrypt the data in order to achieve an higher security level: the examples in the ietf draft show you how to create JWEs with a pair of private / public keys.

Use case: how to authenticate stateless AJAX calls?

One of the needs that you might have is to, from JavaScript, make authenticated HTTP calls to one of your webservices: since you don’t want to expose the WS credentials on the JS service (the credentials would be readable by any client) a good solution might be to generate a JWS with a private OpenSSL key in your webservice, store it into a cookie accessible to the JS service, which would execute those calls including that cookie1, which you can then verify while authenticating the call.

This workflow is pretty easy to understand, but the actual implementation is more than tricky, since the specification is quite abundant – especially about encryption algorithms.

In PHP we can use at least 3 libraries: one of them, Akita_JOSE, is pretty old (since the last commit was more than 7 months ago) but is very understandable and quite easy to use; another one, gree/jose, has itw own package on packagist and can be easily installed via composer: from a fast look at the source code on GitHub it looks good, even though it needs the phpsec library to be able to work2.

The third option, which is the one that I built in the last couple of hours, is namshi/jose, which is very, very easy to use3: it currently only supports the RSA algorithm with sha256 hashing, but I guess that implementing other algorithms is less than trivial.

For example, let’s see how you would generate the JWS to be stored in a cookie:

Generating a JWS after authentication and storing it into a cookie
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?php

use Namshi\JOSE\JWS;

if ($username == 'correctUsername' && $pass = 'ok') {
    $user = Db::loadUserByUsername($username);

    $jws  = new JWS('RS256');
    $jws->setPayload(array(
        'uid' => $user->getid(),
    ));

    $privateKey = openssl_pkey_get_private("file://path/to/private.key");
    $jws->sign($privateKey);
    setcookie('identity', $jws->getTokenString());
}

and then the apps that want to execute authenticated calls on behalf of the user by using this cookie just need to include it in these calls; the server will just need to verify that the JWS in the cookie is valid:

1
2
3
4
5
6
7
8
9
10
11
12
<?php

use Namshi\JOSE\JWS;

$jws        = JWS::load($_COOKIE['identity']);
$public_key = openssl_pkey_get_public("/path/to/public.key");

if ($jws->verify($public_key)) {
    $paylod = $jws->getPayload();

    echo sprintf("Hey, my JS app just did an action authenticated as user #%s", $payload['id']);
}

That’s it: far from being a stable library, this is more a proof of concept that we, an Namshi, would like to see developing in the next weeks / months.

As always, comments, rants or – even better – pull requests are more than welcome!

Notes
  1. One of the disadvantages of this approach is that it relies on cookies, only available in the HTTP protocol. If you want to use another protocol for you application - a very rare and extreme use case - this wouldn’t work for you.
  2. I honestly never heard of this library before, so I can’t really say what it does and why it’s needed
  3. Since I’m not an expert in encryption and security, I would suggest to give it a look and come up with feedbacks
comments powered by Disqus