SecuredKit

Опубліковано: · Оновлено: · автор:

Генератор API-ключів для автоматизації контенту (2026)

Як згенерувати секрет вебхука для Zapier, Make і n8n, увімкнути їхню вбудовану автентифікацію і перевіряти HMAC на сирому тілі запиту. Перевірено в Node 22.

Генератор API-ключів для автоматизації контенту має видавати випадковий секрет щонайменше на 128 біт із криптографічно стійкого джерела випадковості. Цей секрет ви передаєте в заголовку запиту з обох боків вебхука (у флоу Zapier, Make чи n8n і в ендпоінті, який він викликає), а ендпоінт відхиляє кожен запит без нього. Якщо автоматизація витрачає гроші або щось публікує, ендпоінт має ще й перевіряти HMAC-підпис, обчислений над сирим тілом запиту.

Я сам не запускаю контентні пайплайни з API-ключами і жодного разу не мав випадку, щоб ключ автоматизації хтось використав, тож у цьому гайді немає бойових історій. Він побудований на тому, що я запустив для цього оновлення: тесті в Node 22 найпоширенішої помилки з HMAC і скануванні gitleaks, яке показує, які секрети автоматизацій сканер розпізнає, а повз які проходить. Сам секрет зручно створити в Генераторі секретних ключів: він працює у вкладці браузера через Web Crypto API, і ключ не надсилається на жоден сервер, зокрема на мій.

Які ключі ви генеруєте, а які лише бережете

У стеку автоматизації є два типи ключів, і ламаються вони по-різному.

Ключі провайдерів вам видають: OpenAI, Anthropic або токен соцмережі. Зробити їх надійнішими ви не можете. Уся ваша робота в тому, щоб вони не потрапили туди, звідки витікають, а про вкрадений ключ зазвичай дізнаєтеся з рахунку.

Секрети вебхуків ви створюєте самі. Це спільний рядок, який доводить, що запит до вашого ендпоінта прийшов із вашого сценарію, а не від сканера, що знайшов URL. Саме тут потрібен генератор API-ключів: ви створюєте значення, вставляєте його в інструмент автоматизації та у свій ендпоінт, а все, що приходить без нього, отримує 401.

  Make / Zapier / n8n              Ваш ендпоінт
  ┌──────────────┐                 ┌──────────────────────────┐
  │  сценарій    │── POST ────────▶│ header != secret → 401   │
  │  + секрет    │  X-Webhook-     │ інакше → запуск задачі   │
  └──────────────┘  Secret header  └──────────────────────────┘

Як згенерувати секрет вебхука: 128 біт із CSPRNG

Чи можна вгадати випадковий секрет, вирішує звичайна математика ентропії:

H = L × log₂(R)

H — це ентропія в бітах, L — довжина в символах, R — кількість можливих символів. Рядок із 32 hex-символів (R = 16) дає 32 × 4 = 128 біт. Навіть якщо нападник отримав SHA-256-хеш секрету і перебирає його на одній RTX 4090 зі швидкістю близько 22 мільярдів спроб на секунду (hashcat 6.2.6), середній час пошуку такий:

СекретЕнтропіяСередній офлайн-перебір, SHA-256, 1× RTX 4090
8 hex-символів32 біти~0,1 секунди
16 hex-символів64 біти~13 років
32 hex-символи128 біт~2,5 × 10²⁰ років

Це правда лише для справді випадкових значень. Summer25 або назва проєкту з роком падає під атакою за словником за секунди. На 128 випадкових бітах угадування вже не варіант і лишається тільки крадіжка, тому більша частина статті саме про неї.

Випадковість має походити з CSPRNG (криптографічно стійкого генератора псевдовипадкових чисел). Math.random() у V8 — це xorshift128+, і його стан можна відновити з кількох виходів, тож «128-бітний» ключ на його основі секретний рівно настільки, наскільки секретні ці виходи. У коді crypto.randomBytes(32).toString('base64url') у Node і secrets.token_urlsafe(32) у Python читають з генератора операційної системи. Генератор секретних ключів використовує crypto.getRandomValues(), і всі три його формати тепер дають повну кількість біт, зокрема й алфавітно-цифровий. Одна деталь, якщо оберете формат Base64: це стандартний Base64, тож 128-бітний ключ має 24 символи, закінчується на == і може містити + та /. У заголовку це не проблема. В URL закодуйте його через percent-encoding або оберіть hex.

Увімкніть автентифікацію, яка вже є на платформі

Перш ніж писати код перевірки, гляньте, що платформа вміє сама. Станом на жовтень 2026 року три найбільші платформи сильно відрізняються.

ПлатформаВбудована автентифікація вхідного вебхукаЩо робити
Make (Custom webhook)Необов'язковий API-ключ у заголовку x-make-apikey; список дозволених IPУвімкніть API-ключ. Make вирізає цей заголовок, тож він не потрапляє в дані сценарію
n8n (Webhook node)Basic auth, Header auth, JWT authВикористайте Header auth зі згенерованим секретом; увімкніть Raw Body, якщо перевіряєте підписи самі
Zapier (Catch Hook)Я такої не знайшовЄдиний секрет — це URL. Використайте Catch Raw Hook, який передає заголовки запиту, і зупиняйте zap кроком Filter, коли секрет не збігається

Опції Make і n8n взято з їхньої документації (застосунок Webhooks у Make, Webhook node в n8n). Для Zapier я не знайшов вбудованої перевірки, а на форумі спільноти Zapier це питання піднімають знову і знову без офіційної відповіді.

URL вебхука — теж секрет, і сканери про це не знають

У Zapier URL catch-hook і є обліковими даними: будь-хто, у кого він є, може запустити zap. У Make і n8n без увімкненої автентифікації так само. Тож я перевірив, чи помітить сканер секретів такий URL, закомічений у репозиторій. Я згенерував значення потрібного формату (випадкові, не справжні) для дев'яти файлів і просканував їх gitleaks 8.30.1 зі стандартними правилами, п'ять разів, щоразу з новими значеннями.

Що було у файліЗнайдено?Правило
OPENAI_API_KEY=sk-proj-… у .envТак, 5 із 5openai-api-key
URL вхідного вебхука SlackТак, 5 із 5slack-webhook-url
const WEBHOOK_SECRET = "<32 hex>"Так, 5 із 5generic-api-key
"X-Webhook-Secret": "<32 hex>" і "x-make-apikey": "<64 hex>" в об'єкті заголовківТак, 5 із 5generic-api-key
https://hooks.zapier.com/hooks/catch/<id>/<id>/Ні, 0 із 5немає
https://hook.eu1.make.com/<id>Ні, 0 із 5немає
URL вебхука n8n cloud з UUID у шляхуНі, 0 із 5немає
Експортований JSON сценарію, де секрет лежить у парі заголовка {"name": …, "value": …}Ні, 0 із 5немає

Стандартні правила знають URL вебхуків Slack і Teams, але не Zapier, Make чи n8n. Пропуск в експортованому JSON важливіший, ніж здається: інструменти записують заголовки парами name/value, тож слово «secret» і саме значення опиняються на різних рядках, і загальне правило ніколи не бачить їх разом. Якщо ви зберігаєте експорти сценаріїв у git, gitleaks вас тут не врятує.

Окреме правило виправило випадки з URL. З ним у .gitleaks.toml URL Zapier, URL Make і URL Make всередині експортованого JSON позначалися щоразу (3 запуски з 3). Значення секрету всередині JSON — досі ні, тож секретам в експортах взагалі не місце.

[extend]
useDefault = true

[[rules]]
id = "automation-webhook-url"
description = "Zapier or Make incoming webhook URL"
regex = '''https://(?:hooks\.zapier\.com/hooks/catch/\d+/[a-z0-9]+|hook\.[a-z0-9]+\.make\.com/[a-z0-9]{20,})'''
keywords = ["hooks.zapier.com", "make.com"]

Якщо такий URL уже побував у публічному репозиторії, на записі екрана чи в чаті з підтримкою, згенеруйте вебхук на платформі заново. Про ключі загалом, префікси і сканування детальніше в статті керування API-ключами, а про те, де їм зберігатися, — у статті як захистити .env файл.

HMAC: підписуйте сире тіло, а не розібраний JSON

Статичний секрет доводить, що відправник знав рядок. HMAC-підпис доводить ще й те, що тіло ніхто не змінив: відправник обчислює HMAC-SHA-256 над тілом зі спільним секретом і надсилає результат у заголовку, а отримувач обчислює його ще раз і порівнює.

Помилка, якої варто уникати, — перевіряти підпис проти JSON.stringify(req.body) після того, як фреймворк уже розібрав запит. Я перевірив, що з цього виходить у Node 22 (v22.22 і v22.23, результати однакові): підписав тіло саме так, як його серіалізував би відправник, а потім перевірив раз проти сирих байтів і раз проти повторно серіалізованого об'єкта.

Тіло, як його надіслалиПеревірка сирих байтівПеревірка JSON.stringify(parsed)
{"title":"Café menu","price":12}збігзбіг
{"title":"Café menu","price":12.0}збігне збігається (12.0 стає 12)
json.dumps у Python за замовчуванням: пробіли після : і ,, é як éзбігне збігається
Відформатований JSON із переносами рядківзбігне збігається

Повторна серіалізація працює лише тоді, коли відправник випадково записав JSON рівно так, як це робить JSON.stringify, тож перевірка проходить у вашому тесті і падає з іншим відправником. Зберігайте сире тіло. Документація Stripe каже те саме про їхні підписи, а в їхній схемі є ще одна річ, яку варто перейняти: підписується рядок timestamp.body, і їхні бібліотеки відхиляють усе, що старше за 5 хвилин. Це блокує повторне відтворення перехопленого запиту.

Друга пастка — у порівнянні. crypto.timingSafeEqual кидає ERR_CRYPTO_TIMING_SAFE_EQUAL_LENGTH, коли буфери різної довжини, тож запит із коротким або порожнім заголовком підпису обвалює обробник у 500 замість акуратного 401. Спершу перевірте довжину. Ось функція, яку я запускав:

const crypto = require('crypto')

function verify(rawBody, signatureHex, secret) {
  const expected = crypto.createHmac('sha256', secret).update(rawBody).digest()
  const received = Buffer.from(signatureHex, 'hex')
  if (received.length !== expected.length) return false
  return crypto.timingSafeEqual(received, expected)
}

На тілі розміром 2 КБ одна перевірка займала приблизно 3–4 мкс (200 000 запусків), тож швидкість — не привід її пропускати. В Express підключіть express.raw({ type: 'application/json' }) на маршруті вебхука, щоб req.body був Buffer, перевірте підпис, а потім самі викличте JSON.parse. Режим HMAC у Генераторі хешів зручний, щоб отримати підпис для тестового пейлоаду і порівняти його з тим, що обчислює ваш код.

Для більшості автоматизацій контенту достатньо спільного секрету в заголовку. Я б додавав підпис тоді, коли вебхук може запустити виплату, масову розсилку або публікацію на продакшн.

Де витікають ключі автоматизацій

No-code платформи додають шляхи витоку, яких у звичайному коді немає, бо секрети проходять через спільні робочі простори, історію запусків і записи екрана.

Шлях витокуЯк це стаєтьсяЩо робити
Історія запусківMake і Zapier зберігають дані, які кожен крок отримав і надіслав; секрет у пейлоаді там і лежитьТримайте секрети в заголовках або підключеннях, а не в полях пейлоаду
Спільний робочий простірФрилансер, якого додали в простір, може відкрити будь-який сценарій і прочитати будь-який ключОбмежуйте доступ за папками чи командами; ротуйте, коли хтось іде
Записи екранаУ Loom-демонстрації ключ видно на екрані три секундиРозмийте його або ротуйте одразу після запису
Фронтенд-кодВиклик API провайдера з вебсторінки віддає ключ кожному відвідувачуВикликайте API із сервера або edge-функції
AI-чатиФлоу разом із ключем вставили в асистента, щоб знайти помилкуСпершу приберіть секрети; вставлений ключ вважайте скомпрометованим
Експортовані сценаріїJSON блупринта чи воркфлоу закомічено в git разом зі значеннями заголовківНе кладіть експорти в публічні репозиторії; сканери не бачать значень (див. вище)

Ключам провайдерів потрібне ще одне налаштування, яке не замінить жоден сканер: місячний ліміт витрат у панелі провайдера. Тоді вкрадений ключ коштуватиме вам цей ліміт, і не більше.

Ротація з вікном перекриття

Жоден стандарт не встановлює інтервал ротації API-ключів. Оберіть такий, якого дотримуватиметеся (раз на кілька місяців — поширений варіант), і ротуйте одразу, коли хтось із команди пішов, ключ з'явився в журналі чи на записі або різко зросло використання.

Ротуйте без простою, якийсь час приймаючи два секрети: згенеруйте новий, додайте його в ендпоінт як друге допустиме значення, оновіть автоматизацію, переконайтеся, що флоу спрацьовують, а тоді приберіть старий і відкличте його в провайдера. Без перекриття кожен живий сценарій ламається в момент заміни. Панель Stripe робить те саме для своїх секретів підпису, залишаючи старий дійсним до 24 годин.

Якщо ключ чи URL вебхука колись потрапляв у запис, чат або спільний сценарій, почніть із нього сьогодні: згенеруйте заміну, проведіть заміну з перекриттям і відкличте старий.

Часті питання

Чи можна використати UUID як секрет вебхука?

Для неважливих флоу так. UUID v4 з Генератора UUID має 122 випадкові біти, і ніхто їх не вгадає. Для всього, що виплачує гроші, розсилає листи по базі чи публікує, беріть окремий секрет на 128 біт і більше та додавайте HMAC-підпис, щоб перевіряти вміст запиту, а не лише відправника.

Чи потрібен секрет вебхука, якщо ендпоінт працює через HTTPS?

Так. HTTPS не дає нікому в мережі прочитати чи змінити запит, але нічого не каже про те, хто його надіслав. Будь-хто, хто знає URL вашого ендпоінта, може надіслати на нього цілком коректний HTTPS-запит. Саме секрет або HMAC-підпис підтверджує вашому коду, що запит прийшов із вашого сценарію.

Спробуйте безкоштовний генератор паролів

Генеруйте надійні, безпечні паролі миттєво. 100% приватно, на стороні клієнта.

Відкрити генератор паролів