HMAC Authentication and Endpoint Security

Securing the flow of data between two endpoints is crucial to prevent unauthorized access, data breaches, and malicious attacks. It ensures data integrity, confidentiality, and authenticity, thus protecting sensitive information from being intercepted or tampered with. Secure communication prevents impersonation and ensures that both parties in the transaction are verified and trusted, thus maintaining the overall security and reliability of the system.

In Veriff’s context, it is important to secure the data flow between the API endpoints and webhook listeners. Veriff uses the X-HMAC-SIGNATURES and allowed IP lists and ranges for that purpose.

X-HMAC-SIGNATURES are a security measure, which Veriff generates for the API requests and webhook listeners, to make sure that the exchanged data has not been tampered with.

When generating a signature, make sure that you sign correct data with you shared secret key. Different API endpoints require you sign different things.

As a rule of thumb:

  • POST / PATCH endpoints require you sign the request body

  • GET / DELETE endpoints require you sign the session ID

Check the specific API endpoint's documentation for detailed info.

Prerequisites

If you want to generate the X-HMAC-SIGNATURE for an actual API request, have the following info at hand and use these in your preferred signature generation tool:

  • API key - use it as X-AUTH-CLIENT header

  • shared secret key - use it to sign the payload

  • content-type header - in Veriff’s API requests case always application/json

  • API request’s payload - defined in each endpoint’s headers explanation

→ Use the mock data provided below to test signature generation or validation in your local machine

Generate a X-HMAC-SIGNATURE

There are several ways to do that:

  • For ease of use, go for a dedicated HMAC signature generator which supports the generation of HMAC-SHA256 signatures, like OpenSLL, OAuth.io or Postman

  • If you want control and flexibility, you can create a script (see some mock scripts here) or use a command-line tool (a mock openssl script)

  • If you are familiar with a programming language, you can use built-in cryptographic libraries (see a mock Python example here)

  • If you need the signature quickly and security is not a priority, you can use an online tool (see an example below)

Do not use sensitive data or expose personally identifiable information (PII) even when testing.

Scripts

Click on the box to expand/collapse

JavaScript example

Here is an example in JavaScript of how you can generate an HMAC-SHA256 hash and the corresponding X-HMAC-SIGNATURE using the built-in crypto module.

⚠️ Note that you may need to add this module.

const crypto = require("crypto");

const sharedSecretKey = "abcdef12-abcd-abcd-abcd-abcdef012345";

const payload = "{\"verification\":{\"callback\":\"https://veriff.com\",\"person\":{\"firstName\":\"John\",\"lastName\":\"Smith\"},\"document\":{\"type\":\"PASSPORT\",\"country\":\"EE\"},\"vendorData\":\"unique id of the end-user\",\"timestamp\":\"2016-05-19T08:30:25.597Z\"}}";
const hash = crypto

  .createHmac("sha256", sharedSecretKey)
  .update(payload)
  .digest("hex");

const xHmacSignature = hash;

console.log(xHmacSignature);

  1. First, set the shared secret key. Note that the shared secret key is hardcoded to the script, so you should take extra care to keep it secret

  2. Next, prepare the payload data and generate the HMAC-SHA256 hash:

    1. Use the crypto.createHmac() method, which takes the algorithm name ("sha256") and the shared secret key as arguments, and returns an HMAC object

    2. Call the update() method of the HMAC object with the payload data to update the HMAC with the payload data

    3. Then, call the digest() method with the argument "hex" to obtain the hash value in hexadecimal format

  3. Store the X-HMAC-SIGNATURE and output it:

    1. Store the hash value directly in the xHmacSignature variable

    2. Finally, log the xHmacSignature to the console

C# example

This is an example of a script that generates an X-HMAC-SIGNATURE using only the built-in System.Text.Encoding and System.Security.Cryptography namespaces.

⚠️ In this example, the shared secret key and the payload are hardcoded. If you think that you do not want to hardcode them, one option is to create a prompt using the Console.ReadLine() or Console.ReadKey() methods, which asks you to enter the shared secret key and payload data.

using System;
using System.Text;
using System.Security.Cryptography;

public class Program
{
    public static void Main()
    {
        // Set the shared secret key and payload data
        string sharedSecretKey = "abcdef12-abcd-abcd-abcd-abcdef012345";
        string payload = "{\"verification\":{\"callback\":\"https://veriff.com\",\"person\":{\"firstName\":\"John\",\"lastName\":\"Smith\"},\"document\":{\"type\":\"PASSPORT\",\"country\":\"EE\"},\"vendorData\":\"unique id of the end-user\",\"timestamp\":\"2016-05-19T08:30:25.597Z\"}}";

            // Convert the shared secret key and payload to byte arrays
            byte[] sharedSecretKeyBytes = Encoding.UTF8.GetBytes(sharedSecretKey);
            byte[] payloadBytes = Encoding.UTF8.GetBytes(payload);

            // Generate the HMAC-SHA256 hash using (var hmac = new HMACSHA256(sharedSecretKeyBytes))
            {
                byte[] hash = hmac.ComputeHash(payloadBytes);

                // Convert the hash to a hexadecimal string
                string xHmacSignature = BitConverter.ToString(hash).Replace("-", "").ToLower();

                Console.WriteLine(xHmacSignature);
            }
    }
}

  1. First set the shared secret key and payload data as strings.

  2. Next, convert them to byte arrays using the Encoding.UTF8.GetBytes() method.

  3. Then generate the HMAC-SHA256 hash using the HMACSHA256 class, passing in the shared secret key bytes as the key and the payload bytes as the data to be hashed. The result is the hash value as a byte array.

  4. Convert it to a hexadecimal string using the BitConverter.ToString() method. Remove the hyphens using the Replace() method, and convert the string to lowercase using the ToLower() method to obtain the X-HMAC-SIGNATURE.

  5. Finally, write the X-HMAC-SIGNATURE to the console using the Console.WriteLine() method.

PHP example

This is an example of a script that generates an X-HMAC-SIGNATURE using only the built-in hash_hmac() and strtolower() functions.

$sharedSecretKey = 'abcdef12-abcd-abcd-abcd-abcdef012345';
$payload = '{"verification":{"callback":"https://veriff.com","person":{"firstName":"John","lastName":"Smith"},"document":{"type":"PASSPORT","country":"EE"},"vendorData":"unique id of the end-user","timestamp":"2016-05-19T08:30:25.597Z"}}';

$signature = hash_hmac('sha256', $payload, $sharedSecretKey);
$signature = strtolower($signature);

echo "X-HMAC-SIGNATURE: " . $signature . PHP_EOL;
            

  1. Define the shared secret key and the payload as strings.

  2. Generate the HMAC-SHA256 signature by calling the hash_hmac() function, passing the hashing algorithm ('sha256'), the payload, and the shared secret key.

  3. Then, convert the signature to lowercase using the strtolower() function.

  4. Finally, echo the X-HMAC-SIGNATURE to the output using the echo statement.

Command-line tools

openssl example

This is an example of how to create the signature using openssl command-line tool.


In order to avoid hardcoding the shared secret key and the payload into your script or command and thus keep it secure and prevent accidental disclosure and misuse, we have saved the two values to separate files: secret_key.txt and payload.txt.

set /p secret_key=<secret_key.txt && set /p payload=<payload.txt && echo | set /p dummy="%payload%" | openssl dgst -sha256 -hmac "%secret_key%" -hex

  1. Open the command-line tool and navigate to the folder where the .txt files are.

  2. Then, run the command above in the command prompt.

  3. The set /p command is used to read the content of the files without adding a newline character. The echo | set /p dummy= part is used to output the content without a newline character.

  4. After running the command, you should get the HMAC signature as output.

Built-in crypto libraries

Python cryptographic library example

Most modern programming languages have built-in cryptographic libraries that provide functions for generating HMAC signatures.

Below is a general overview of how to use built-in cryptographic libraries to generate an HMAC signature:

  1. Choose the SHA-256 cryptographic algorithm to generate signatures with Veriff.

  2. Next, get the payload that you want to sign, and the shared secret key to generate the X-HMAC-SIGNATURE.

  3. Once you have the payload and the shared secret key, initialize the HMAC function in the cryptographic library. This typically involves creating an HMAC object or specifying the algorithm and the secret key.

  4. After initializing the HMAC function, update it with the payload you want to sign. This tells the function to incorporate the payload into the HMAC signature.

  5. Finally, call a function in the cryptographic library to generate the HMAC signature based on the payload and secret key. This function should return the HMAC signature as a string or byte array, depending on the library and language you are using.

Here is an example of how you might generate an X-HMAC-SIGNATURE in Python using the built-in hmac and hashlib libraries:

import hmac
import hashlib

shared_secret_key = b'abcdef12-abcd-abcd-abcd-abcdef012345'
payload = b'{"verification":{"callback":"https://veriff.com","person":{"firstName":"John","lastName":"Smith"},"document":{"type":"PASSPORT","country":"EE"},"vendorData":"unique id of the end-user","timestamp":"2016-05-19T08:30:25.597Z"}}'

hash = hmac.new(shared_secret_key, payload, hashlib.sha256)

x_hmac_signature = 'sha256=' + hash.hexdigest()

print(x_hmac_signature)

  1. The script imports the hmac library for generating the HMAC signature and the hashlib library to access the SHA256 hashing algorithm.

  2. The shared secret key and payload are set as byte literals (strings with a b prefix) to ensure proper encoding for the HMAC calculation.

  3. The hmac.new() function takes three arguments to generates an HMAC hash:

    1. the shared secret key,

    2. the payload, and

    3. the hashing algorithm (in this case, SHA256 from the hashlib library)

  4. The hash.hexdigest() method converts the HMAC hash into a hexadecimal string. The string 'sha256=' is then prepended to the hexadecimal representation of the hash, creating the final X-HMAC-SIGNATURE

  5. The X-HMAC-SIGNATURE is printed to the console.

Online tools

Use your favorite HMAC-SHA256 calculator to calculate the hash, e.g. Codebeautify HMAC Generator [↗]

  1. Select SHA256 from the algorithm dropdown

  2. Paste the shared secret key to the "Enter Key" field

  3. Paste the appropriate payload to the "Enter the Plain or Cipher Text" field

  4. Click on the Generate HMAC button

  5. The result is a generated hash, which you can copy-paste to the X-HMAC-SIGNATURE in your API request Headers field.

Validate a signature locally

Below are few examples how to validate the generated X-HMAC-SIGNATURE. Click on the box to expand/collapse.

Do not use sensitive data or expose personally identifiable information (PII) even when testing.

Use Javascript isSignatureValid function

The purpose of the function is to validate that the provided signature is correct for the given payload and shared secret key. It does this by generating an X-HMAC-SIGNATURE of the payload using the shared secret key, and then comparing this HMAC to the provided signature.


Below is an example where we are using the mock shared secret key, signature and payload values. You can run this in a Node.js environment to validate the signature. To test a real signature, replace the mock values with real ones, but never expose PII when testing!

const crypto = require('crypto');

function isSignatureValid({ signature, shared_secret_key, payload }) {
    if (payload.constructor === Object) {
        payload = JSON.stringify(payload);
    }

    if (payload.constructor !== Buffer) {
        payload = Buffer.from(payload, 'utf8');
    }

    const digest = crypto
        .createHmac('sha256', shared_secret_key)
        .update(Buffer.from(payload, 'utf8'))
        .digest('hex')
        .toLowerCase();

    return digest === signature.toLowerCase();
}

const signature = "0dcab73ddd20062616d104231c7439657546a5c24e4691977da93bb854c31e25";
const shared_secret_key = "abcdef12-abcd-abcd-abcd-abcdef012345";
const payload = {
    "verification": {
        "callback": "https://veriff.com",
        "person": {
            "firstName": "John",
            "lastName": "Smith"
        },
        "document": {
            "type": "PASSPORT",
            "country": "EE"
        },
        "vendorData": "unique id of the end-user",
        "timestamp": "2016-05-19T08:30:25.597Z"
    }
};

console.log(isSignatureValid({ signature, shared_secret_key, payload }));

If both match, the function returns true, indicating that the signature is valid, and the payload is likely to be authentic. If they don't match, it returns false, meaning the signature is invalid, and the payload's authenticity cannot be guaranteed.

Test the validation function in a local server

We recommend setting up a simple Express.js server in your local machine, to test the validation functionality independently of our webhook or API service.

Below is a simple Express.js server application written in JavaScript that listens for incoming POST requests to the /verification/ endpoint. The server validates the HMAC-SHA256 signature of the request payload using a shared secret key and responds with the validation status (true or false).

const express = require('express');
const bodyParser = require('body-parser');
const { createHmac } = require('crypto');
const { Server } = require('http');
const app = express();

const SERVICE_PORT = 3001;
const SECRET_KEY = 'abcdef12-abcd-abcd-abcd-abcdef012345';

function isSignatureValid({ signature, sharedSecretKey, payload }) {
if (payload.constructor === Object) {
payload = JSON.stringify(payload);
}

    if (payload.constructor !== Buffer) {
        payload = new Buffer.from(payload, 'utf8');
    }

    const digest = createHmac('sha256', sharedSecretKey)
        .update(Buffer.from(payload, 'utf8'))
        .digest('hex')
        .toLowerCase();

    return digest === signature.toLowerCase();
}

app.use(bodyParser.json());
let server = Server(app);
app.post('/verification/', (req, res) => {
    res.json({
        isSignatureValid: isSignatureValid({
            signature: req.get('x-hmac-signature'), sharedSecretKey: SECRET_KEY, payload: req.body
        })
    });
})
server.listen(SERVICE_PORT, () => console.log('Server is UP \n Listening port:', SERVICE_PORT));

Now, post prepared data to the server you have set up:

curl --request POST "http://localhost:3001/verification/" -k --header "accept:application/json" --header "x-auth-client:abcdef12-abcd-abcd-abcd-abcdef012345" --header "x-hmac-signature:0dcab73ddd20062616d104231c7439657546a5c24e4691977da93bb854c31e25" --header "content-type:application/json" --data "{\"verification\":{\"callback\":\"https://veriff.com\",\"person\":{\"firstName\":\"John\",\"lastName\":\"Smith\"},\"document\":{\"type\":\"PASSPORT\",\"country\":\"EE\"},\"vendorData\":\"unique id of the end-user\",\"timestamp\":\"2016-05-19T08:30:25.597Z\"}}"

This curl above should return {"isSignatureValid":true}

Mock data to test signature generation locally

Beware the hardspaces: generating the value may vary depending on the programming language used, e.g., some languages include the hard spaces in the body, and some omit them.

  1. Mock shared secret key:

    abcdef12-abcd-abcd-abcd-abcdef012345

  2. POST /sessions request mock payload (as .json):

    {"verification":{"callback":"https://veriff.com","person":{"firstName":"John","lastName":"Smith"},"document":{"type":"PASSPORT","country":"EE"},"vendorData":"unique id of the end-user","timestamp":"2016-05-19T08:30:25.597Z"}}

    To generate an actual signature for your live API requests, the Headers explanation in each API request’s section tells you what to use as payload for encryption.


  1. If your preferred method is correct, you should get the following mock X-HMAC-SIGNATURE:

    0dcab73ddd20062616d104231c7439657546a5c24e4691977da93bb854c31e25