Усі статті
Безпечне тестування webhook Linear на localhost
Linearwebhookslocalhostdeveloper integrations

Безпечне тестування webhook Linear на localhost

Зовнішній сервіс не може напряму звернутися до localhost. Тунель надає публічний HTTPS URL і передає локальному серверу справжні headers та незмінений body. Збережіть raw body до JSON parser. Спочатку перевірте підпис і дані, надійно запишіть доставку й лише потім запускайте бізнес-логіку.

Як webhook Linear потрапляє до локального застосунку

Зовнішній сервіс не може напряму звернутися до localhost. Тунель надає публічний HTTPS URL і передає локальному серверу справжні headers та незмінений body. >офіційна документація

1. Створіть endpoint зі збереженням raw body

Збережіть raw body до JSON parser. Спочатку перевірте підпис і дані, надійно запишіть доставку й лише потім запускайте бізнес-логіку.

import express from "express";
import crypto from "node:crypto";

const app = express();
const secret = process.env.LINEAR_WEBHOOK_SECRET;

app.post(
  "/webhooks/linear",
  express.raw({ type: "application/json", limit: "1mb" }),
  async (req, res) => {
    const rawBody = req.body;
    const signature = req.get("linear-signature");

    if (!verifyLinearSignature(signature, rawBody, secret)) {
      return res.sendStatus(401);
    }

    let payload;
    try {
      payload = JSON.parse(rawBody.toString("utf8"));
    } catch {
      return res.sendStatus(400);
    }

    if (!Number.isFinite(payload.webhookTimestamp) ||
        Math.abs(Date.now() - payload.webhookTimestamp) > 60_000) {
      return res.sendStatus(401);
    }

    const deliveryId = req.get("linear-delivery") || payload.webhookId;
    if (!deliveryId) return res.sendStatus(400);

    try {
      await recordAndEnqueueOnce(deliveryId, payload);
      return res.sendStatus(200);
    } catch (error) {
      console.error("Linear webhook persistence failed", error);
      return res.sendStatus(500);
    }
  }
);

app.use(express.json());
app.listen(3000);

Збережіть raw body до JSON parser. Спочатку перевірте підпис і дані, надійно запишіть доставку й лише потім запускайте бізнес-логіку. >офіційна документація

2. Відкрийте локальний порт через HTTPS

Запустіть сервер і тримайте тунель відкритим у другому терміналі. Відповідь 401 на запит без підпису підтверджує правильний routing.

npx portpreview 3000
https://example.portpreview.dev/webhooks/linear

Запустіть сервер і тримайте тунель відкритим у другому терміналі. Відповідь 401 на запит без підпису підтверджує правильний routing.

3. Налаштуйте webhook у Linear

Укажіть повний HTTPS URL, підпишіться лише на потрібні події та виконайте реальний сценарій у тестовому середовищі. Зберігайте secret поза репозиторієм.

Укажіть повний HTTPS URL, підпишіться лише на потрібні події та виконайте реальний сценарій у тестовому середовищі. Зберігайте secret поза репозиторієм.

Перевірте підпис за точними байтами

Обчисліть HMAC із webhook secret і точними даними зі специфікації. Порівнюйте за сталий час і не записуйте secret у логи.

function verifyLinearSignature(signature, rawBody, secret) {
  if (!secret || typeof signature !== "string" ||
      !/^[0-9a-f]{64}$/i.test(signature)) {
    return false;
  }

  const expected = crypto
    .createHmac("sha256", secret)
    .update(rawBody)
    .digest();
  const actual = Buffer.from(signature, "hex");

  return actual.length === expected.length &&
    crypto.timingSafeEqual(actual, expected);
}

Обчисліть HMAC із webhook secret і точними даними зі специфікації. Порівнюйте за сталий час і не записуйте secret у логи. >практичний посібник

Захистіться від replay за допомогою timestamp

Після перевірки підпису контролюйте свіжість timestamp і синхронізуйте годинник сервера.

Після перевірки підпису контролюйте свіжість timestamp і синхронізуйте годинник сервера.

Розберіться з payload та ID доставки

Обробляйте відомі type, приймайте додаткові поля та усувайте дублікати за стабільним ID доставки.

Обробляйте відомі type, приймайте додаткові поля та усувайте дублікати за стабільним ID доставки.

Відповідайте швидко й обробляйте ідемпотентно

Резервуйте ID доставки та створюйте job в одній transaction до відповіді 200. Якщо storage недоступний, поверніть помилку для retry.

Резервуйте ID доставки та створюйте job в одній transaction до відповіді 200. Якщо storage недоступний, поверніть помилку для retry. >практичний посібник

Протестуйте весь локальний потік

  1. Запустіть сервер і тримайте тунель відкритим у другому терміналі. Відповідь 401 на запит без підпису підтверджує правильний routing.
  2. Укажіть повний HTTPS URL, підпишіться лише на потрібні події та виконайте реальний сценарій у тестовому середовищі. Зберігайте secret поза репозиторієм.
  3. Обчисліть HMAC із webhook secret і точними даними зі специфікації. Порівнюйте за сталий час і не записуйте secret у логи.
  4. Резервуйте ID доставки та створюйте job в одній transaction до відповіді 200. Якщо storage недоступний, поверніть помилку для retry.

Перевірте routing, headers, підпис, час відповіді та ідемпотентність. Синтетичні запити корисні для сценаріїв відмови.

Діагностика помилок webhook Linear

  • Запустіть сервер і тримайте тунель відкритим у другому терміналі. Відповідь 401 на запит без підпису підтверджує правильний routing.
  • Обчисліть HMAC із webhook secret і точними даними зі специфікації. Порівнюйте за сталий час і не записуйте secret у логи.
  • Після перевірки підпису контролюйте свіжість timestamp і синхронізуйте годинник сервера.
  • Перевірте POST path, port тунелю, raw body, secret, годинник і затримку бази даних.

>практичний посібник

Перевірка безпеки для розробки та production

  • Вимагайте HTTPS, обмежте розмір і method, захищайте secrets, очищуйте логи та видаляйте застарілі тестові URL.
  • Збережіть raw body до JSON parser. Спочатку перевірте підпис і дані, надійно запишіть доставку й лише потім запускайте бізнес-логіку.
  • Резервуйте ID доставки та створюйте job в одній transaction до відповіді 200. Якщо storage недоступний, поверніть помилку для retry.

Вимагайте HTTPS, обмежте розмір і method, захищайте secrets, очищуйте логи та видаляйте застарілі тестові URL. >практичний посібник

Поширені запитання

Як протестувати webhook Linear на localhost?
Запустіть сервер і тримайте тунель відкритим у другому терміналі. Відповідь 401 на запит без підпису підтверджує правильний routing.
Як перевірити підпис webhook Linear?
Обчисліть HMAC із webhook secret і точними даними зі специфікації. Порівнюйте за сталий час і не записуйте secret у логи.
Чому Linear повторює доставку webhook?
Резервуйте ID доставки та створюйте job в одній transaction до відповіді 200. Якщо storage недоступний, поверніть помилку для retry.
Який ключ використовувати для ідемпотентності?
Обробляйте відомі type, приймайте додаткові поля та усувайте дублікати за стабільним ID доставки.