Коли HelpCrunch надсилає webhook-запит, він містить заголовок X-HelpCrunch-Signature. Цей заголовок використовується для перевірки того, що запит справді надійшов від HelpCrunch і що його payload не був змінений під час передавання.
Якщо ваш застосунок відхиляє вхідні webhook-запити через невалідний підпис, найпоширеніша причина полягає в тому, що підпис перевіряється не за оригінальним сирим(raw) payload, а за вже розпарсеним або зміненим тілом запиту.
Як працює перевірка підпису webhook
Щоб перевірити webhook-запит від HelpCrunch на вашому боці:
-
Отримайте сире тіло запиту (raw payload) саме в тому вигляді, в якому воно було отримане.
-
Згенеруйте HMAC-SHA1 хеш, використовуючи ваш ключ підпису webhook.
-
Порівняйте згенерований хеш зі значенням із заголовка
X-HelpCrunch-Signature.
Підпис завжди передається в webhook-запитах HelpCrunch, тому якщо перевірка не проходить, проблема зазвичай полягає в тому, як обробляється payload до моменту валідації.
Важливі вимоги
Використовуйте сире тіло запиту
Підпис потрібно перевіряти саме за сирим тілом запиту (raw payload), до будь-якого JSON-парсингу або повторної серіалізації.
Використовуйте порівняння за константний час
Натомість використовуйте функцію порівняння за константний час, наприклад:
-
hash_equalsу PHP -
timingSafeEqualу Node.js -
compare_digestу Python
Це допомагає захистити ваш endpoint від "атак по часу".
Формат підпису
HelpCrunch використовує:
-
Алгоритм: HMAC-SHA1
-
Формат результату: шістнадцятковий рядок
-
Довжина: 40 символів
Приклад для PHP
function verifyWebhookSignature(string $rawBody, string $signingKey, string $receivedSignature): bool
{
$expectedSignature = hash_hmac('sha1', $rawBody, $signingKey);
return hash_equals($expectedSignature, $receivedSignature);
}
// Usage
$rawBody = file_get_contents('php://input');
$receivedSignature = $_SERVER['HTTP_X_HELPCRUNCH_SIGNATURE'] ?? '';
$signingKey = 'your-webhook-signing-key';
if (!verifyWebhookSignature($rawBody, $signingKey, $receivedSignature)) {
http_response_code(401);
exit('Invalid signature');
}
Приклад для Node.js
const crypto = require('crypto');
function verifyWebhookSignature(rawBody, signingKey, receivedSignature) {
const expectedSignature = crypto
.createHmac('sha1', signingKey)
.update(rawBody)
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(expectedSignature),
Buffer.from(receivedSignature)
);
}
// Usage (Express)
app.post('/webhook', express.raw({ type: 'application/json' }), (req, res) => {
const receivedSignature = req.headers['x-helpcrunch-signature'];
if (!verifyWebhookSignature(req.body, process.env.SIGNING_KEY, receivedSignature)) {
return res.status(401).send('Invalid signature');
}
// process webhook...
});
Приклад для Python
import hmac
import hashlib
def verify_webhook_signature(raw_body: bytes, signing_key: str, received_signature: str) -> bool:
expected_signature = hmac.new(
signing_key.encode(),
raw_body,
hashlib.sha1
).hexdigest()
return hmac.compare_digest(expected_signature, received_signature)
# Usage (Flask)
from flask import Flask, request
@app.route('/webhook', methods=['POST'])
def webhook():
received_signature = request.headers.get('X-HelpCrunch-Signature', '')
if not verify_webhook_signature(request.get_data(), SIGNING_KEY, received_signature):
return 'Invalid signature', 401
Поширені причини, чому перевірка підпису не проходить
Якщо підпис не збігається, перевірте:
1. Чи тіло запиту не є сирим
Це найпоширеніша проблема. Переконайтеся, що ви хешуєте оригінальне тіло запиту, а не розпарсений JSON-об’єкт і не його повторно серіалізовану версію.
2. Чи використовується неправильний ключ підпису
Ще раз перевірте, що ваш застосунок використовує саме той webhook signing key, який налаштований для цього webhook.
3. Чи підпис порівнюється неправильно
Використовуйте функцію порівняння за константний час, а не звичайну перевірку на рівність.
4. Чи тіло запиту змінюється middleware
Деякі фреймворки автоматично парсять або трансформують вхідний JSON ще до того, як виконується ваш код перевірки. У такому разі потрібно налаштувати endpoint так, щоб він мав доступ до сирого тіла (raw payload) запиту в першу чергу.
Якщо webhook працює в Postman, але не проходить перевірку з реальними запитами від HelpCrunch, проблема часто не в самому заголовку, а в тому, як ваш сервер обробляє payload до моменту обчислення хешу.
Іншими словами, якщо ви вручну надсилаєте запит із таким самим значенням заголовка в Postman, це ще не означає, що перевірка підпису у вашому production-середовищі реалізована правильно.