Published on

Authenticating APIs with HMAC

Authors
  • avatar
    Name
    Parminder Singh
    Twitter

Hash based message authentication code (HMAC) is a cryptographic signature that can be used to authenticate messages between two parties. This article will discuss how HMAC can be used to secure APIs.

Flow of HMAC Authentication

The idea is to generate a unique hash based on the content of the message and a secret key. This hash is then sent along with the message to the receiver. The receiver can then regenerate the hash using the same secret key and verify if it matches the hash sent by the sender. If the hashes match, it means that the message has not been tampered with.

Client side implementation

To generate the HMAC hash on the client side, the following components can be used:

  1. Request Payload: E.g. POST/PUT body. For calls that do not require a payload, the payload can be an empty string.
  2. HTTP Method: E.g. GET/POST/PUT/DELETE.
  3. URL: The full URL of the API endpoint. E.g. https://api.example.com/v1/resource.
  4. Random nonce: A random string to prevent replay attacks.
  5. Timestamp: A timestamp is used to prevent replay attacks. Use Unix epoch time.

The HMAC hash can be generated using the following steps:

  1. Concatenate the payload, HTTP method, URL, timestamp and nonce into a single string.
  2. Using the shared secret, generate the HMAC hash of the concatenated string. The HMAC hash can be generated using a cryptographic hash function like SHA-256.
  3. Include the generated hash, timestamp, and nonce in the request headers.

Server side implementation

On the server side, the following steps can be used to verify the HMAC hash:

  1. Extract the payload, timestamp, HTTP method, URL, and nonce from the request.
  2. Concatenate the payload, timestamp, HTTP method, URL, and nonce into a single string.
  3. Using the shared secret, generate the HMAC hash of the concatenated string.
  4. Compare the generated hash with the hash sent by the client. If they match, the request is authentic.

Example code

The following example demonstrates how to generate and verify the HMAC hash using Python.


import os
import hmac
import datetime

def verify_hmac(request_method:str, url: str, headers, response_body: str) -> str:
    payload = response_body
    if payload is None:
       payload = ""

    sent_hmac = headers["auth"] if "auth" in headers else None
    nonce = headers["nonce"] if "nonce" in headers else None
    timestamp = headers["timestamp"] if "timestamp" in headers else None

    # check if any of the required headers are missing
    if timestamp is None or nonce is None or request_method is None or url is None or payload is None or sent_hmac is None:
        return False

    # check if timestamp is within a reasonable time frame. I'll use a minute in this example
    # Helps prevent replay attacks
    if abs(int(timestamp) - datetime.datetime.now().timestamp()) > 60:
        return False

    # construct payload to be hashed
    payload = f"{request_method}\n{url}\n{payload}\n{nonce}\n{timestamp}"

    # fetch shared secret
    secret = os.environ.get("HMAC_SECRET").encode()
    expected_hmac = hmac.new(secret, payload.encode(), digestmod='sha256').hexdigest()

    if expected_hmac != sent_hmac:
        return False

This code snippet demonstrates how to verify the HMAC hash on the server side. The verify_hmac function extracts the payload, timestamp, HTTP method, URL, and nonce from the request and generates the HMAC hash using the shared secret. It then compares the generated hash with the hash sent by the client to verify the authenticity of the request. On the client side, a similar process can be used to generate the HMAC hash and include it in the request headers.