Skip to content

Verify Webhook Signatures ​

Every webhook delivery includes an HMAC-SHA256 signature. You must verify this signature to ensure the payload was sent by CryptaChain and not tampered with.

How it works ​

  1. CryptaChain signs the raw request body with your webhook secret
  2. The signature is sent in the X-CryptaChain-Signature header
  3. You recompute the signature and compare

Verification examples ​

Node.js ​

javascript
const crypto = require('crypto');

function verifyWebhook(rawBody, signature, secret) {
  const expected = 'sha256=' + crypto
    .createHmac('sha256', secret)
    .update(rawBody, 'utf8')
    .digest('hex');

  return crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(expected)
  );
}

// Express.js middleware
app.post('/webhooks/cryptachain', express.raw({ type: 'application/json' }), (req, res) => {
  const signature = req.headers['x-cryptachain-signature'];
  if (!verifyWebhook(req.body, signature, process.env.WEBHOOK_SECRET)) {
    return res.status(401).send('Invalid signature');
  }
  const event = JSON.parse(req.body);
  // Process event...
  res.status(200).send('OK');
});

Python ​

python
import hmac, hashlib

def verify_webhook(raw_body: bytes, signature: str, secret: str) -> bool:
    expected = 'sha256=' + hmac.new(
        secret.encode('utf-8'),
        raw_body,
        hashlib.sha256
    ).hexdigest()
    return hmac.compare_digest(signature, expected)

# Flask example
@app.route('/webhooks/cryptachain', methods=['POST'])
def handle_webhook():
    signature = request.headers.get('X-CryptaChain-Signature')
    if not verify_webhook(request.data, signature, WEBHOOK_SECRET):
        abort(401)
    event = request.get_json()
    # Process event...
    return 'OK', 200

Go ​

go
func verifyWebhook(rawBody []byte, signature, secret string) bool {
    mac := hmac.New(sha256.New, []byte(secret))
    mac.Write(rawBody)
    expected := "sha256=" + hex.EncodeToString(mac.Sum(nil))
    return hmac.Equal([]byte(signature), []byte(expected))
}

Java ​

java
Mac mac = Mac.getInstance("HmacSHA256");
mac.init(new SecretKeySpec(secret.getBytes(UTF_8), "HmacSHA256"));
String expected = "sha256=" + Hex.encodeHexString(mac.doFinal(rawBody));
return MessageDigest.isEqual(signature.getBytes(), expected.getBytes());

Common mistakes ​

Use raw body

Always verify against the raw request body (bytes), not a parsed/re-serialized JSON object. JSON serialization may change whitespace or key ordering, breaking the signature.

Timing-safe comparison

Always use constant-time comparison (timingSafeEqual, hmac.compare_digest) to prevent timing attacks.

CryptaChain API — built by CryptaCount Luxembourg S.a r.l.