Skip to content

Securing external bulk operations

Working with webhooks presents a potential security vulnerability in your customer solution if you trust every request made to your public endpoints. To ensure that your systems only trust requests made by Bizzkit, there are a number of options.

IP whitelisting

We recommend that you whitelist the public IP address used by PIM, and prevent connections from all other IP addresses. While not providing complete certainty as to the authenticity of webhook requests from Bizzkit products, it provides an effective baseline. Furthermore, if PIM and your customer solution can communicate via a private network, limiting communication to this network can provide additional security.

Request signatures

As an additional layer of security, we guarantee that all webhook requests from PIM include a digital signature of their bodies. You can use this signature to ensure that a request is genuine, as well as to protect against replay attacks. We strongly recommend that you always verify the signature on webhook requests!

Configuring request signing

When creating an external bulk operation, a symmetric secret is generated for you, and presented to you only once. This secret should be known only to PIM and the customer solution. After creation, there is no way to view the secret again via the dashboard or the public API. Thus, it is your responsibility that the secret is stored securely and persistently for the customer solution. If, for some reason, the secret is lost, you have the option to request for a new secret to be generated via the dashboard or the public API. The secret is presented as a base64 encoded string.

Verifying request signatures

When PIM sends a webhook request to the customer solution endpoint, the request will always contain the following headers:

  • X-Bizzkit-Signature: The request signature as a series of key-value pairs algo1=<signature1>,...,algoN=<signatureN> specifying signature algorithms and base64 encoded signatures respectively.
  • X-Bizzkit-Signature-Timestamp: The UNIX timestamp specifying when the webhook request was initiated by PIM.

The signature header must be treated as a series of comma-separated key-value pairs, despite only a single key-value pair being present for now. This is to support the rotation of algorithms in the future, should the current algorithm become insufficient. Signature algorithms are deprecated in due time before removal. You cannot assume any ordering of algorithms in the signature header, but should instead search for and extract the signatures for those algorithms your customer solution supports.

PIM currently only uses an SHA-256 HMAC signature with base64 encoding to sign its requests. The secret is also treated as base64, whereas the request body and timestamp are treated as UTF-8 encoded strings. The customer solution side needs to reproduce this signature to verify the authenticity of the request. To further provide protection against replay attacks, the signature is calculated from both the request body and timestamp as such:

ToBase64(HMACSHA256(FromBase64(secret), FromUTF8(Timestamp + Body)))

Note

The signature is generated from the raw request body, and not a prettified representation with indentation and newlines, as many frameworks will generate.

Example

Sample signature verification
private static bool VerifySignature(string secret, string body, string xBizzkitSignature, string xBizzkitTimestamp)
{
  var signature = CreateSignature(Convert.FromBase64String(secret),
    Encoding.UTF8.GetBytes(xBizzkitTimestamp + body));
  var expectedBizzkitSignature = $"sha256={signature}";

  // Consider using a constant time comparison to avoid timing attacks
  return expectedBizzkitSignature == xBizzkitSignature;
}

private static string CreateSignature(byte[] key, byte[] payload)
{
  var hash = HashHmacSha256(key, payload);
  return Convert.ToBase64String(hash);
}

private static byte[] HashHmacSha256(byte[] key, byte[] payload)
{
  using var hmac = new HMACSHA256(key);
  return hmac.ComputeHash(payload);
}