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
- CryptaChain signs the raw request body with your webhook secret
- The signature is sent in the
X-CryptaChain-Signatureheader - 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', 200Go
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.