How to Secure a PHP Based API Using JWT

Secure a PHP Based API Using JWT

If you’ve ever had to code an API sooner rather than later you found yourself wondering how to secure this communication.

Beside the clear need to use a secure socket layer for keeping unwanted eyes out, there are many other concerns that need to be addressed, especially if the information being exchanged is sensitive.

In this article you’ll learn about JWT, a tool that helps improve the security of web based communications while keeping the overhead low and how to implement it in your PHP based applications.

What is JWT

Let’s start at the beginning: what does JWT stand for?

JWT stands for Json Web Tokens. It is a mechanism designed to securely transmit information between computers using Json Objects.

The security of JWT is based on the usage of well-known protocols to encode the tokens, namely HMAC or a public-private key pairs.

It’s full description can be found at this RFC.

Why you should use JWT

In any scenario where you need to make sure the information you’re receiving hasn’t been tampered with on the way or you want to keep the information private, JWT can come in handy.

Typical use cases for JWT are:

  • Authorization
  • Information exchange

In the case of the authorization, the idea is to generate a unique token once the user is authenticated.

This token will be sent in conjunction with every subsequent request and, on the server side, will be validated in order to determine whether the requested action is within the user’s capabilities.

As for the information exchange, JWTs make sender identity and message integrity verification possible.

There are a few alternatives to JWT, namely SAML (Security Assertion Markup Language) and SWT (Simple Web Token).

The pros of using JWT over it’s direct competitors are:

  1. Compactness (By being based on Json instead of XML the tokens themselves are much smaller, which makes it also more efficient when it comes to processing them)
  2. Availability of non-symmetrical signing (SWT can’t implement it and for the case of SAML, doing so implies signing XML which is cumbersome compared to the simplicity of doing it on a json string)

One more concrete scenario where you could leverage JWT is the implementation of a SSO mechanism since, as you’ll soon learn, the token can carry within all the information needed to assess both the issuer and the recipient, making it really easy for servers living in different domains to exchange authentication.

How does JWT work

The most secure way to use the JWT provided by the server is for the client to send it over the Authorization header in every subsequent request, like this:

Authorization: Bearer $TOKEN

So, the workflow would be the following:

  1. The client sends it’s credentials to the server
  2. The server generates the token and sends it back to the client
  3. The client issues a new request including the token
  4. The server verifies the token and responds accordingly

The token consists of three json strings separated by a dot and base64 encoded:

Header

The header has two keys:

  • typ: The token type, for the time being it’s always JWT
  • alg: The algorithm used to sign the token, it’s value must be one of the defined in the RFC

Payload

The payload is the most important part of the token, it’s where the claims are stored.

Claims can be used to exchange any kind of information your application needs, but there are a few standard fields typically used:

  • iss: This field is used to verify the authenticity of the token by establishing the issuing party’s identity. It consists of a string (typically a URL)
  • exp: Expiration time after which the token is to be considered invalid
  • aud: This field is for the recipient to validate that the token was issued for them. It consists of a list of strings (which may contain a single element) 
  • iat: A date value expressed as a number which value identifies the time at which the JWT was issued
  • nbf: A date value expressed as a number which is to be used to reject the token until this point in time is reached. It is most commonly used for avoiding synchronization issues.

You can see a full list of claims here

Signature

The final part of the token is what makes the whole mechanism work, it stores the result of applying the hashing algorithm specified in the header to the concatenation of the header encoded using base64, a dot, the payload encoded using base64.

For instance, if you are using HMAC combined with SHA 256 (“HS256”), you’d get the signature by calculating:

hash_hmac(‘sha256′, base64_encode($header).’.’.base64_encode($payload), $secret);

Which will result in the final composition of the token:

base64_encode($header).’.’.base64_encode($payload).base64_encode($signature).

One detail you need to be aware of is the fact that the contents of the token are not cyphered, meaning that, while they can’t be altered on the way from one party to the other, this communication can be intercepted and read by a malicious actor.

Having said that, you also have the possibility of using a private/public key pair in order to encrypt the contents of the token as well, by restorting to a different algorithm.

In order to prevent security issues the communication should be made over a secure sockets layer and the token should not be stored in a browser.

How to implement JWT in PHP

Of course you could create and validate the tokens by yourself, after all it’s just about processing some json and base64, right? How hard can it be to make a couple of calls to json_encode/json_decode here and there?

Well, that might be true but, as usual, there are nuances than can make php implementation harder than it needs to be.

In fact, there’s a library you can use for it: PHP-JWT.

Just run the command composer require firebase/php-jwt and you’ll get the library ready for usage inside your code.

Let’s have a look at how a REST server of your creation would create JWTs for it’s clients:

<?php

require_once ‘vendor/autoload.php’;

use Firebase\JWT\JWT;

$key = $_SERVER[“API-KEY”];

$secret = $_SERVER[“API-SECRET”];

if (!validate_credentials()) {

http_response_code(403);

die;

}

$key = “secure_coding”;

$payload = [

   “iss” => “http://securecoding.com”,

   “aud” => “http://securecoding.com”,

   “iat” => 1356999524,

   “nbf” => 1357000000

];

header(“Content-Type: application/json”);

echo JWT::encode($payload, $key);

In this example I’m assuming the credentials are sent via HTTP headers (API-Key and API-Secret) and in response the server sends back the newly created token, in this case:

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwOlwvXC9leGFtcGxlLm9yZyIsImF1ZCI6Imh0dHA6XC9cL2V4YW1wbGUuY29tIiwiaWF0IjoxMzU2OTk5NTI0LCJuYmYiOjEzNTcwMDAwMDB9.KcNaeDRMPwkRHDHsuIh1L2B01VApiu3aTOkWplFjoYI

Later on, when your application receives further requests you can check the values stored in the token like this:

<?php

require_once ‘vendor/autoload.php’;

use Firebase\JWT\JWT;

if (!array_key_exists(‘HTTP_AUTHORIZATION’, $_SERVER)) {

   http_response_code(401);

   die;

}

$key = “secure_coding”;

try {

   $jwt = preg_split(‘/ /’, $_SERVER[‘HTTP_AUTHORIZATION’])[1];

   $decoded = JWT::decode($jwt, $key, array(‘HS256’));

   if (!validate($decoded)) {

       http_response_code(401);

       die;

   }

} catch (UnexpectedValueException $exception) {

   echo $exception->getMessage();

}

It’s left for you to implement the validation method that suits your application’s needs, just make sure both the server and the client share the secret key to be used in applying the HMAC algorithm, in this case secure_coding.

Conclusion

In this article you learned what JWT is, the pros and cons of using it and how easy it is to bring it into your PHP applications.

Mauro Chojrin

Mauro Chojrin / About Author

Mauro is a PHP Trainer and Consultant. He’s been involved in the IT Industry since the year 1997 in a wide array of positions, going from which include technical support, development, team leadership, IT Management and, off course, teaching. Currently Mauro’s focus is on in-company training and consulting but also maintaining his blog and YouTube channel where he shares his knowledge with the world. LinkedIn | Twitter