Introduction to secured client server API

Subscribe to my newsletter and never miss my upcoming articles

Preface

In modern software architecture it's often necessary to communicate between applications a common way to do this is by using an API. APIs (Application Programming Interface) are interfaces optimised for applications rather than humans as their purpose is to transfer data from either backend to frontend, for example PHP to JavaScript or from one application to another.

This post I will focus on the second case, between 2 applications. When doing so, you transfer data across the internet and must therefore, for a numerous reasons, retain more focus on security, such as man-in-the-middle-attacks, spoofing and so on. In other words you must make sure both server and client can identify and validate each other and data between is encrypted.

Implementation

The idea is that you send some unencrypted data as headers, this could be:

  • Timestamp
  • Version
  • Token
  • Signature

Along the headers the body contains an encrypted message that needs all or some of the headers to be decrypted with a secret.

Timestamp

The time the message was sent, this can be used to monitor transfer delay but also as a parameter to the signature.

Version

Tells the server which version the client is on, you can both use this data to know how to handle the incoming request on the server, but also to know when a version is not used anymore or which clients to upgrade.

Token

The token, also often known as key, tells the server which client or configuration, is the author to lookup for the secret stored on the server. It could be as simple as an user id.

Signature

The signature is generated with a set set of the parameters to create a unique key that the server can use to validate the authenticity of the sender.

Secret

The secret is very important here, it's the key that should only be known by host and client and never shared and also easy to regenerate in case it was mistakenly shared.

Implementation in Laravel

Signature

First the client should generate the signature, let's assume we have these variables we are going to work with:

$timestamp = time();
$version = '1.0.0';
$token = 'my_key';
$secret = 'my_secret';

To validate the signature, both server and client must have the same method in order to parse the parameters to the same result. A method to create a signature using hash_hmac could be:

function createSignature(int $timestamp, string $secret): string
{
    return hash_hmac('sha256', $timestamp . $secret);
}

The advantage here using a hash algorithm is that it's not designed to be decrypted again. We are not interested in the actual content of the plain text, just if we can match the string on both servers.

The message

Now we have the following variables:

$timestamp = time();
$version = '1.0.0';
$token = 'my_key';
$secret = 'my_secret';
$signature = createSignature($timestamp, $secret);

Unlike the signature, we want to be able to decrypt the content of the message. Since this guide is based on Laravel I benefit from the build in Encryption class Illuminate\Encryption\Encrypter.

use Illuminate\Encryption\Encrypter;

function encrypt(string $secret, string $message): string
{
    $encrypter = new Encrypter($secret, 'AES-128-CBC');

    return $encrypter->encrypt($message);
}

Here we use the secret in order to encrypt the content of our message, if you have an array you could use json_encode/json_decode.

The secret

This illustrates why it's important to keep the secret secured as it's the magic key to break into your encrypted system.

Sending data

When using Laravel I will recommend using Guzzle as it's straight forward easy to work with. Here's how you can do it:

function send(string $message, string $key, string $secret, string $url) {
    $client = new Client();
    $response = null;
    $timestamp = time();
    $version = '1.0.0';

    try {
        $response = $client->request(
            'POST',
            $url,
            [
                'headers' => [
                    'timestamp' => $timestamp,
                    'version' => $version,
                    'token' => $key,
                    'signature' => createSignature($timestamp, $secret),
                ],
                'form_params' => [
                    'data' =>encrypt($secret, $message)
                ]
            ]
        );
    } catch (Exception $e) {
        throw $e;
    }

    return $response;
}

CloudMonitor.dk – Free Laravel Maintenance- and Bug Management

No Comments Yet