Notifications security

Verify signature and secure agains replay attacks

Introduction

To ensure the security of our webhook payloads, each request is signed with an HMAC signature and includes a timestamp. Clients can verify the signature and check the timestamp to confirm the payload's integrity and authenticity.

Headers

Verifying Signature and Timestamp

X-Signature: The HMAC signature of the payload.
X-Timestamp: The UNIX timestamp when the request was generated. Note that the timestamp also exists inside the payload.

Below are examples of verifying the signatures and checking the timestamps.

import * as crypto from 'crypto';

// Secret key known to both to Krayon and the Client
const secretKey = 'supersecretkey';

function verifySignatureAndTimestamp(receivedPayload: string, receivedSignature: string, receivedTimestamp: string, maxAgeSeconds: number = 300): [boolean, string] {
    // Calculate the expected signature
    const hmac = crypto.createHmac('sha256', secretKey);
    hmac.update(receivedPayload);
    const expectedSignature = hmac.digest('hex');

    // Verify the signature
    if (receivedSignature !== expectedSignature) {
        return [false, "Invalid signature"];
    }

    // Verify the timestamp
    const currentTimestamp = Math.floor(Date.now() / 1000);
    const receivedTimestampNumber = parseInt(receivedTimestamp, 10);
    if (Math.abs(currentTimestamp - receivedTimestampNumber) > maxAgeSeconds) {
        return [false, "Timestamp is not within the acceptable range"];
    }

    return [true, "Valid signature and timestamp"];
}

// Example usage
const receivedPayload = '{"data": "example_payload", "timestamp": "1633024800", "nonce": "unique-nonce"}';
const receivedSignature = 'received_signature_from_headers';
const receivedTimestamp = '1633024800';

const [isValid, message] = verifySignatureAndTimestamp(receivedPayload, receivedSignature, receivedTimestamp);
console.log(message);

import hmac
import hashlib
import json
import time

# Secret key known to both to Krayon and the Client
secret_key = b'supersecretkey'

def verify_signature_and_timestamp(received_payload, received_signature, received_timestamp, max_age_seconds=300):
    # Calculate the expected signature
    expected_signature = hmac.new(secret_key, received_payload.encode(), hashlib.sha256).hexdigest()

    # Verify the signature
    if not hmac.compare_digest(received_signature, expected_signature):
        return False, "Invalid signature"

    # Verify the timestamp
    current_timestamp = int(time.time())
    received_timestamp = int(received_timestamp)
    if abs(current_timestamp - received_timestamp) > max_age_seconds:
        return False, "Timestamp is not within the acceptable range"

    return True, "Valid signature and timestamp"

# Example usage
received_payload = '{"data": "example_payload", "timestamp": "1633024800", "nonce": "unique-nonce"}'
received_signature = 'received_signature_from_headers'
received_timestamp = '1633024800'

is_valid, message = verify_signature_and_timestamp(received_payload, received_signature, received_timestamp)
print(is_valid, message)


Summary

By following these examples, clients can ensure that the payloads received from the webhook are authentic and have not been tampered with. Both the signature and the timestamp are verified to protect against replay attacks and ensure data integrity.