Вебхук в n8n по умолчанию открыт для любого, кто узнал URL - и это не теоретическая дыра, а то, с чем я регулярно сталкиваюсь на аудите чужих сценариев. Настроить n8n webhook авторизацию можно за 10-15 минут, если понимать разницу между Basic Auth в Webhook-ноде, проверкой секретного токена в заголовке и HMAC-подписью тела запроса. Дальше разберу все три варианта на живых кейсах: Telegram-бот, который раньше был написан на aiogram и слушал обновления сам, вебхуки Т‑Банка и СДЭК с подписью в теле, и кастомный скрипт на Tilda, который дёргает n8n напрямую.
Почему открытый вебхук в n8n - это не мелочь
URL вебхука в n8n выглядит как случайная строка, но это не защита, а обфускация. Если сценарий пишет заказы в CRM, шлёт сообщения в Telegram или списывает токены у платного API, любой, кто нашёл или подсмотрел ссылку - в логах браузера, в исходнике сайта, в переписке - может дёргать её сколько угодно раз.
На практике встречал сценарий интернет-магазина на WooCommerce, где n8n принимал вебхук о новом заказе и сразу отправлял письмо клиенту и создавал накладную в СДЭК. Без авторизации на вебхуке это значит, что кто угодно может засыпать эндпоинт фиктивными заказами и заставить систему генерировать реальные накладные - лишние расходы на доставку и разгребание отчётности потом.
Разница между n8n Cloud и self-hosted тут ни при чём: URL публичный в обоих случаях, если явно не настроена авторизация в самой Webhook-ноде.
Basic Auth в Webhook-ноде - быстрый вариант для своих скриптов
В настройках Webhook-ноды есть поле Authentication с вариантами None, Basic Auth, Header Auth и, в последних версиях, JWT. Basic Auth - самый простой способ: создаю credential с логином и паролем, n8n сам проверяет заголовок Authorization: Basic и отклоняет запрос без него кодом 403 ещё до того, как сработает остальной сценарий.
Подходит, когда вебхук дёргает код, который я сам пишу и контролирую - например, кастомный скрипт на Tilda, отправляющий данные формы в n8n через fetch, или внутренний сервис на бэкенде. Там логин и пароль просто прописываются в заголовке запроса на стороне отправителя.
Не подходит для вебхуков от внешних провайдеров - Т‑Банка, СДЭК, Telegram Bot API, GitHub. У них нет настройки «добавить Basic Auth заголовок» в личном кабинете: они присылают запрос в своём формате, и подстраиваться приходится вам, а не им.
Секретный токен в заголовке - гибкий вариант для ботов и API
Header Auth в n8n работает иначе: указываю имя заголовка и ожидаемое значение, n8n сравнивает их напрямую, без base64 и логина. Это ровно тот механизм, который нужен для Telegram Bot API - с 2023 года при вызове setWebhook можно передать параметр secret_token, и Telegram будет присылать его в заголовке X‑Telegram-Bot-Api-Secret-Token с каждым обновлением.
Переносил на n8n бота, который раньше был написан на aiogram и работал через polling - там авторизация по сути не требовалась, всё крутилось внутри своего процесса на сервере. При переезде на вебхуки в n8n сразу завёл Header Auth credential с secret_token, чтобы Telegram Trigger нода не реагировала на запросы, которые не от Telegram, - иначе кто-то теоретически мог слать поддельные обновления на тот же URL.
Так же поступаю с интеграциями, где сервис даёт возможность задать произвольный заголовок при настройке колбэка - некоторые платёжные агрегаторы и CRM позволяют это прямо в панели администратора.
Бесплатный материал
🎁 Полезный скрипт в подарок
Подпишитесь на Telegram - пришлю готовый скрипт по этой теме.
Без спама. Отписка в 1 клик.
HMAC-подпись тела запроса - обязательна для денег и логистики
Т‑Банк, СДЭК и большинство платёжных провайдеров не поддерживают ни Basic, ни Header Auth в привычном виде. Вместо этого они подписывают тело запроса общим секретом и кладут результат в отдельное поле - иногда в заголовке, но чаще прямо в JSON-теле. Задача принимающей стороны - пересчитать хеш по тем же правилам и сравнить со значением, которое пришло.
Header Auth в Webhook-ноде тут бесполезен: она сравнивает статичную строку, а подпись каждый раз разная. Поэтому ставлю Webhook-ноду без встроенной авторизации (Authentication: None), обязательно включаю Raw Body в её настройках и сразу за ней - Code-ноду, которая пересчитывает подпись:
const crypto = require('crypto');
const secret = $env.WEBHOOK_SECRET;
const signature = $input.item.json.headers['x-signature'];
const rawBody = $input.item.json.body;
const expected = crypto
.createHmac('sha256', secret)
.update(rawBody)
.digest('hex');
if (expected !== signature) {
throw new Error('Подпись вебхука не совпадает');
}
return $input.item;
Raw Body здесь критичен: если оставить его выключенным, n8n сам распарсит JSON, а сериализация обратно в строку почти никогда не совпадает байт в байт с тем, что подписывал отправитель - из-за порядка полей, пробелов, экранирования. Полдня однажды потратил на дебаг именно этой мелочи, когда подпись СДЭК не совпадала, хотя секрет был верный.
После Code-ноды ставлю IF-ноду: если подпись не совпала - веду сценарий в Respond to Webhook с кодом 403 и пустым телом, если совпала - дальше по обычной логике. У меня в библиотеке готовых сценариев лежит такой шаблон Code-ноды для проверки подписи - беру его за основу под каждую новую интеграцию с эквайрингом или транспортными службами, чтобы не пересобирать логику с нуля.
Сравнение способов авторизации вебхука в n8n
| Способ | Где настраивается | Что проверяется | Когда использовать |
|---|---|---|---|
| Basic Auth | Webhook-нода, Credentials | Заголовок Authorization с логином и паролем | Свои скрипты - Tilda, внутренние сервисы, кастомные формы |
| Header Auth | Webhook-нода, Credentials | Произвольный заголовок со статичным значением | Telegram Bot API (secret_token), сервисы с настраиваемым заголовком |
| HMAC-подпись | Code-нода после Webhook | Хеш от тела запроса, пересчитанный по алгоритму провайдера | Платёжные и логистические вебхуки - Т‑Банк, СДЭК, эквайринг |
| JWT | Webhook-нода, Credentials | Подпись и срок действия токена | Сервисы, которые сами выпускают JWT для колбэков |
Частые ошибки при настройке n8n webhook авторизации
- Оставляют тестовый URL (webhook-test) в продакшен-сценарии - он живёт только пока открыт редактор n8n, а после закрытия вкладки провайдер начинает получать 404 вместо ответа.
- Хранят секрет прямо в теле workflow как обычный текст в Code-ноде, а не в Credentials или переменных окружения - при экспорте JSON сценария секрет уезжает вместе с файлом.
- Забывают включить Raw Body при проверке HMAC-подписи - пересчитанный хеш от перепарсенного JSON никогда не совпадёт с оригиналом.
- Сравнивают токены обычным равенством строк, что нормально для большинства сценариев, но для платежей лучше использовать
crypto.timingSafeEqual, чтобы не давать шанс подбору токена по времени ответа. - Не логируют отказы авторизации в отдельную ветку сценария - о переборе токена узнают только когда что-то уже сломалось, а не по метрикам.
Связка сервисов без программистов
Автоматизация / n8n
от 25 000 ₽
Подробнее →Частые вопросы
Нужна ли авторизация вебхука, если сценарий работает только в n8n Cloud?
Да. URL вебхука публичный независимо от хостинга - кто угодно с этой ссылкой может отправить запрос, даже если сам инстанс n8n закрыт паролем от чужих глаз в интерфейсе. Сама ссылка на вебхук по умолчанию не защищена.
Чем Header Auth отличается от Basic Auth в Webhook-ноде n8n?
Basic Auth ожидает заголовок Authorization с логином и паролем в base64 - подходит, когда отправитель поддерживает стандартную HTTP-авторизацию. Header Auth сравнивает значение произвольного заголовка с заданной строкой, что удобнее для сервисов вроде Telegram Bot API, которые сами кладут secret_token в свой заголовок при каждом обновлении.
Можно ли проверить подпись вебхука без написания кода в n8n?
Для простого сравнения статичного токена - да, через Header Auth credential в самой Webhook-ноде. Для HMAC-подписи, где нужно пересчитать хеш от тела запроса и сравнить его со значением из провайдера, без Code-ноды не обойтись: готовой ноды под это в n8n нет.
Что делать, если провайдер присылает подпись в теле запроса, а не в заголовке?
Ставлю Webhook-ноду без встроенной авторизации, включаю Raw Body в её настройках и сразу после неё - Code-ноду, которая достаёт поле подписи из тела, пересчитывает хеш по алгоритму провайдера и через IF-ноду решает, отвечать 200 или обрывать выполнение кодом 403.
При работе с СДЭК, Т‑Банком и похожими интеграциями я обычно сразу закладываю такую проверку в архитектуру сценария, а не добавляю её потом - переписывать готовый рабочий workflow под авторизацию задним числом выходит дороже, чем сделать правильно с первого раза.