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.