Webhook Signature Verification

To verify the the authenticity that your scheduled webhook was triggered by cronhooks you can check the signature.

ASP.NET / C#


[Route("api/[controller]")]
public class CronhooksFailureWebHook : Controller
{
    // You can find your endpoint's secret in your API Keys settings
    const string secret = "wh_...";

    [HttpPost]
    public void Index()
    {
      var json = new StreamReader(HttpContext.Request.Body).ReadToEnd();
      var computedSignature = ComputeSignature(secret, json);
      var cronhooksSignature = Request.Headers["Cronhooks-Signature"];

      if (SecureCompare(computedSignature, cronhooksSignature))
      {
        // Verified
      }
    }

    private string ComputeSignature(string secret, string payload)
    {
      var secretBytes = Encoding.UTF8.GetBytes(secret);
      var payloadBytes = Encoding.UTF8.GetBytes(payload);

      using (var cryptographer = new HMACSHA256(secretBytes))
      {
        var hash = cryptographer.ComputeHash(payloadBytes);
        return BitConverter.ToString(hash).Replace("-", string.Empty).ToLowerInvariant();
      }
    }

    private bool SecureCompare(string a, string b)
    {
      if (a.Length != b.Length)
      {
        return false;
      }

      var result = 0;
      for (var i = 0; i < a.Length; i++)
      {
        result |= a[i] ^ b[i];
      }
      return result == 0;
    }
}
                
            

PHP


$webhook_secret = 'wh_...';

$payload = @file_get_contents('php://input');
$cronhooks_sig_header = $_SERVER['HTTP_CRONHOOKS_SIGNATURE'];

$signature = computeSignature($payload, $webhook_secret);

if(secureCompare($cronhooks_sig_header, $signature)){
  // Verified
}

private function computeSignature($payload, $secret){
   return hash_hmac("sha256", $payload, $secret);
}

private function secureCompare($secret, $computed){
  if(function_exists('hash_equals')){
    return hash_equals($secret, $computed);
  }

  if (strlen($secret) != strlen($computed)) {
    return false;
  }

  $result = 0;
  for ($i = 0; $i < strlen($secret); $i++) {
    $result |= ord($secret[$i]) ^ ord($computed[$i]);
  }
  return ($result == 0);
}
            
        

Typescript / Node


import crypto from 'crypto'

const cronhooksSignature = req.headers['cronhooks-signature']; // req.headers['Cronhooks-Signature']

let body = rawBody.toString();

const expectedSignature = crypto
  .createHmac('sha256', process.env.CRONHOOKS_SECRET)
  .update(body)
  .digest('hex');

let isValidRequest = (cronhooksSignature !== expectedSignature);