Webhooks Signature Verification

Why Verify Signatures?

When your application receives a webhook from OCUS, it's crucial to verify that the request is genuinely from OCUS and has not been tampered with. Webhook signature verification ensures the integrity and authenticity of the payload, protecting your system from unauthorized or malicious requests.

How Signature Verification Works

OCUS signs each webhook request using a unique signature (the signature is different for each single notification) , which is included in the request header ocus-signature. To verify the authenticity of the request:

  1. Retrieve the ocus-signature from the request headers.
  2. Use the secret key that was provided when registering the webhook URL.
  3. Recompute the HMAC SHA-256 hash using the response body.data and your secret key.
  4. Compare the computed signature with the received ocus-signature. If they match, the request is valid.

Implementation Example

const crypto = require('crypto');

/**
 * Verify the OCUS webhook signature.
 *
 * @param {Record<string, string>} headers - HTTP request headers.
 * @param {Buffer} rawBody - The exact raw request body bytes received on the wire.
 * @param {string} secret - The webhook secret shared between OCUS and the client.
 * @returns {boolean} True when the signature is present and valid; false otherwise.
 */
function verifyOcusSignature(headers, rawBody, secret) {
  const received = headers["ocus-signature"];
  if (!received || !rawBody) return false;

  const expected = crypto
    .createHmac("sha256", secret)
    .update(rawBody)
    .digest("hex");

  return crypto.timingSafeEqual(
    Buffer.from(expected, "hex"), 
    Buffer.from(received, "hex")
  );
}
package main

import (
    "crypto/hmac"
    "crypto/sha256"
    "encoding/hex"
    "fmt"
    "io/ioutil"
    "net/http"
)

// VerifyOcusSignature verifies the Ocus webhook signature.
func VerifyOcusSignature(r *http.Request, secretKey string) bool {
    receivedSignature := r.Header.Get("Ocus-Signature")
    if receivedSignature == "" {
        fmt.Println("Missing Ocus-Signature header")
        return false
    }

    body, err := ioutil.ReadAll(r.Body)
    if err != nil {
        fmt.Println("Error reading request body:", err)
        return false
    }
    defer r.Body.Close()

    h := hmac.New(sha256.New, []byte(secretKey))
    h.Write(body)
    calculatedSignature := hex.EncodeToString(h.Sum(nil))

    return calculatedSignature == receivedSignature
}

Error Handling and Security Considerations

  • Ensure the Secret Key is Secure: Never expose your secret key in client-side code or logs.
  • Reject Requests with Missing or Invalid Signatures: Log these attempts for monitoring.

By following these steps, you can ensure that only legitimate webhook requests from OCUS are processed by your application.