---
title: "Webhook-подпись в n8n: HMAC без подделок | Nodbot"
source_url: "https://nodbot.ru/workflows/webhook-signature-validation/"
canonical_url: "https://nodbot.ru/workflows/webhook-signature-validation/"
language: "ru"
content_type: "WorkflowTemplate"
section: "workflows"
generated_at: "2026-05-30"
word_count_source: 1056
---

# Проверка подписи webhook в n8n: HMAC, timestamp и защита от replay

## AI summary

AI-friendly Problem/Solution-мануал: как настроить проверку HMAC-подписи webhook в n8n, защититься от подделки запроса и replay-атак, сохранить тестовый payload, code node, curl, HowTo и production-чеклист.

## Best used for

Полноценный Problem/Solution-мануал для внедрения в n8n: импортировать workflow JSON, настроить безопасность/API/backup, выполнить production-тесты и передать решение команде.

## Table of contents

- Проблема: почему webhook без подписи можно подделать
- Архитектура workflow проверки HMAC-подписи
- Контракт входных данных и headers
- Code Node: HMAC SHA256, timestamp tolerance и replay key
- Готовый workflow JSON: скачать и импортировать
- Пошаговая настройка webhook signature validation
- Тесты через curl: валидная и битая подпись
- Production-риски безопасности webhook
- Полезные ссылки и смежные workflow
- Критерии готовности

## Key topics

- n8n webhook
- HMAC SHA256
- signature validation
- replay protection
- timestamp tolerance
- webhook security

## Source outline

Проверка подписи webhook в n8n: HMAC, timestamp и защита от replay ¶ Обновлено: 2026-05-30 AI summary: AI-friendly Problem/Solution-мануал: как настроить проверку HMAC-подписи webhook в n8n, защититься от подделки запроса и replay-атак, сохранить тестовый payload, code node, curl, HowTo и production-чеклист. Шаблон для внедрения Скачать workflow JSON Скачать test payload Скопировать curl Импортируйте workflow в n8n, задайте WEBHOOK_SIGNING_SECRET и проверьте подпись до подключения CRM. Содержание Проблема: почему webhook без подписи можно подделать Архитектура workflow проверки HMAC-подписи Контракт входных данных и headers Code Node: HMAC SHA256, timestamp tolerance и replay key Готовый workflow JSON: скачать и импортировать Пошаговая настройка webhook signature validation Тесты через curl: валидная и битая подпись Production-риски безопасности webhook Полезные ссылки и смежные workflow Критерии готовности Проблема: публичный webhook в n8n может вызвать кто угодно, если у него есть URL. Простая настройка вебхука удобна для интеграции, но без подписи злоумышленник или ошибочный партнёрский скрипт может отправить фальшивую заявку, повторить старый запрос или запустить платную бизнес-логику. Решение: принимать событие только после проверки HMAC SHA256 по raw body, заголовку timestamp и replay key. Такая схема нужна для CRM, платежей, партнёрских кабинетов и любых сценариев, где передача данных из формы или внешней системы должна быть доказуемой. Проверка подписи идёт до любых HTTP Request, CRM update и записи в базу. Проблема: почему webhook без подписи можно подделать ¶ Webhook URL часто попадает в логи, скриншоты, документацию подрядчика или историю браузера. Если workflow сразу пишет в CRM, Google Sheets или Битрикс24, то любой POST похожей формы становится “валидным”. Подпись решает эту боль: n8n пересчитывает HMAC из исходного тела запроса и общего секрета, а затем сравнивает его с подписью отправителя. Отдельно нужен timestamp tolerance. Даже настоящую подпись можно повторить через час или неделю, если не проверять время события и event_id . Поэтому защита webhook — это не один IF node, а связка HMAC, времени и durable replay-хранилища. Архитектура workflow проверки HMAC-подписи ¶ Нода Роль Что проверить Webhook input Принимает raw body и headers Production URL, POST, доступ к заголовкам Verify HMAC signature Считает sha256 и сравнивает подпись Secret из ENV, timing-safe compare Check replay key Проверяет уникальность event_id Postgres unique index или другое durable storage Business logic Запускает CRM/API только после verified Нет побочных эффектов до проверки Respond safe status Возвращает 200/401 без лишних деталей Не отдавать stack trace и секреты Контракт входных данных и headers ¶ Партнёр должен подписывать строку timestamp.rawBody , а не уже распарсенный JSON. Иначе разный порядок полей или пробелы сломают проверку. В n8n важно сохранить исходное тело или договориться с отправителем о стабильной сериализации. { "event_id": "evt_9f8c1", "event": "lead.created", "created_at": "2026-05-30T10:00:00Z", "data": { "lead_id": "crm-10492", "phone": "+7 916 123-45-67", "source": "partner_webhook" } } Какие headers нужны X-Webhook-Timestamp — Unix timestamp события. X-Webhook-Signature — строка вида sha256=<hex> . Content-Type: application/json — чтобы payload был предсказуемым. Code Node: HMAC SHA256, timestamp tolerance и replay key ¶ Этот скрипт n8n останавливает workflow до бизнес-логики. Для ротации секрета можно проверять два секрета: текущий и предыдущий, но хранить их нужно в ENV/credentials, а не в тексте ноды. const crypto = require('crypto'); const body = $json.rawBody ?? JSON.stringify($json.body ?? $json); const headers = $json.headers ?? {}; const timestamp = Number(headers['x-webhook-timestamp'] ?? headers['X-Webhook-Timestamp']); const signature = String(headers['x-webhook-signature'] ?? headers['X-Webhook-Signature'] ?? ''); const secret = $env.WEBHOOK_SIGNING_SECRET; if (!secret) throw new Error('WEBHOOK_SIGNING_SECRET is not configured'); if (!timestamp || Math.abs(Date.now() - timestamp * 1000) > 5 * 60 * 1000) { throw new Error('Webhook timestamp is outside 5 minute tolerance'); } const expected = 'sha256=' + crypto.createHmac('sha256', secret).update(`${timestamp}.${body}`).digest('hex'); const ok = signature.length === expected.length && crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected)); if (!ok) throw new Error('Invalid webhook signature'); const parsed = typeof $json.body === 'object' ? $json.body : JSON.parse(body); return [{ json: { verified: true, replay_key: `webhook:${parsed.event_id}`, event: parsed.event, body: parsed } }]; Готовый workflow JSON: скачать и импортировать ¶ Полный workflow JSON лежит в архиве и доступен по кнопке. В нём есть места для Postgres replay key, безопасного ответа и комментарии, какие credentials нужно заменить. Скачать готовый workflow JSON Скачать тестовый payload { "name": "Nodbot - Webhook HMAC signature validation", "nodes": [ { "name": "Webhook input", "type": "n8n-nodes-base.webhook", "purpose": "Принять raw body и headers" }, { "name": "Verify HMAC signature", "type": "n8n-nodes-base.code", "purpose": "Проверить sha256 HMAC и timestamp tolerance" }, { "name": "Check replay key", "type": "n8n-nodes-base.postgres", "purpose": "Не обработать event_id повторно" }, { "name": "Business logic", "type": "n8n-nodes-base.httpRequest", "purpose": "Запустить CRM/API только после verified=true" }, { "name": "Respond safe status", "type": "n8n-nodes-base.respondToWebhook", "purpose": "Вернуть 200/401 без stack trace" } ], "connections": "Webhook → Verify HMAC → Check replay → Business logic → Respond" } Пошаговая настройка webhook signature validation ¶ Создайте секрет подписи и передайте его партнёру через защищённый канал. Добавьте в n8n ENV WEBHOOK_SIGNING_SECRET . Импортируйте workflow JSON и включите production URL webhook. Подключите Postgres/Redis для replay key с уникальным индексом или TTL. Проверьте валидный curl, битую подпись, старый timestamp и повторный event_id . Тесты через curl: валидная и битая подпись ¶ timestamp=$(date +%s) body='{"event_id":"evt_9f8c1","event":"lead.created","created_at":"2026-05-30T10:00:00Z","data":{"lead_id":"crm-10492","phone":"+7 916 123-45-67"}}' signature="sha256=$(printf "%s.%s" "$timestamp" "$body" | openssl dgst -sha256 -hmac "$WEBHOOK_SIGNING_SECRET" -hex | sed 's/^.* //')" curl -X POST "https://YOUR-N8N-DOMAIN/webhook/webhook-signature-validation" -H "Content-Type: application/json" -H "X-Webhook-Timestamp: $timestamp" -H "X-Webhook-Signature: $signature" --data "$body" Второй тест — изменить один символ в body после расчёта подписи. Workflow должен вернуть отказ и не вызвать downstream-ноды. Production-риски безопасности webhook ¶ Подписывается parsed JSON. Из-за пробелов и порядка ключей подпись становится нестабильной. Нет replay protection. Настоящий запрос можно повторить и создать дубль. Secret в Code Node. Его увидит любой с доступом к workflow export. Ошибка раскрывает детали. Внешний отправитель не должен получать stack trace. Бизнес-логика стоит до проверки. Даже отклонённый запрос успевает изменить CRM. Полезные ссылки и смежные workflow ¶ Смотрите также: Webhook idempotency в Postgres , Retry и DLQ для HTTP Request , Error Workflow с Telegram-алертом . Внешняя документация: n8n Webhook node , n8n Crypto node . Критерии готовности ¶ Невалидная подпись не вызывает ни одной бизнес-ноды. Timestamp старше допустимого окна отклоняется. Повторный event_id не обрабатывается второй раз. Секрет хранится в ENV/credentials и имеет план ротации. Команда знает, как проверить подпись через curl и где смотреть rejected-события. Нужна безопасная интеграция webhook? Nodbot настроит HMAC, replay protection, журнал событий и алерты так, чтобы внешний POST не мог сломать CRM или запустить лишние действия. Обсудить внедрение

## Test payload

```json
{
  "event_id": "evt_9f8c1",
  "event": "lead.created",
  "created_at": "2026-05-30T10:00:00Z",
  "data": {
    "lead_id": "crm-10492",
    "phone": "+7 916 123-45-67",
    "source": "partner_webhook"
  }
}
```

## Key implementation snippet

```javascript
const crypto = require('crypto');
const body = $json.rawBody ?? JSON.stringify($json.body ?? $json);
const headers = $json.headers ?? {};
const timestamp = Number(headers['x-webhook-timestamp'] ?? headers['X-Webhook-Timestamp']);
const signature = String(headers['x-webhook-signature'] ?? headers['X-Webhook-Signature'] ?? '');
const secret = $env.WEBHOOK_SIGNING_SECRET;

if (!secret) throw new Error('WEBHOOK_SIGNING_SECRET is not configured');
if (!timestamp || Math.abs(Date.now() - timestamp * 1000) > 5 * 60 * 1000) {
  throw new Error('Webhook timestamp is outside 5 minute tolerance');
}

const expected = 'sha256=' + crypto.createHmac('sha256', secret).update(`${timestamp}.${body}`).digest('hex');
const ok = signature.length === expected.length && crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected));

if (!ok) throw new Error('Invalid webhook signature');

const parsed = typeof $json.body === 'object' ? $json.body : JSON.parse(body);
return [{ json: { verified: true, replay_key: `webhook:${parsed.event_id}`, event: parsed.event, body: parsed } }];
```

## Importable workflow structure

```json
{
  "name": "Nodbot - Webhook HMAC signature validation",
  "nodes": [
    { "name": "Webhook input", "type": "n8n-nodes-base.webhook", "purpose": "Принять raw body и headers" },
    { "name": "Verify HMAC signature", "type": "n8n-nodes-base.code", "purpose": "Проверить sha256 HMAC и timestamp tolerance" },
    { "name": "Check replay key", "type": "n8n-nodes-base.postgres", "purpose": "Не обработать event_id повторно" },
    { "name": "Business logic", "type": "n8n-nodes-base.httpRequest", "purpose": "Запустить CRM/API только после verified=true" },
    { "name": "Respond safe status", "type": "n8n-nodes-base.respondToWebhook", "purpose": "Вернуть 200/401 без stack trace" }
  ],
  "connections": "Webhook → Verify HMAC → Check replay → Business logic → Respond"
}
```

## Retrieval hints

- Использовать HTML как canonical source.
- Markdown удобен для LLM-ответов, извлечения workflow-контракта, кода и чеклистов.
- Для ссылок пользователю отдавать canonical URL.
