Usługa zewnętrzna nie połączy się z localhost. Tunel udostępnia publiczny adres HTTPS i przekazuje prawdziwe nagłówki oraz niezmienione body do lokalnego serwera. Zachowaj surowe body przed parserem JSON. Najpierw sprawdź podpis i dane, trwale zapisz dostawę, a dopiero potem uruchom logikę biznesową.
Jak webhook Linear trafia do aplikacji lokalnej
Usługa zewnętrzna nie połączy się z localhost. Tunel udostępnia publiczny adres HTTPS i przekazuje prawdziwe nagłówki oraz niezmienione body do lokalnego serwera. >oficjalna dokumentacja
1. Zbuduj endpoint zachowujący surowe body
Zachowaj surowe body przed parserem JSON. Najpierw sprawdź podpis i dane, trwale zapisz dostawę, a dopiero potem uruchom logikę biznesową.
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);
Zachowaj surowe body przed parserem JSON. Najpierw sprawdź podpis i dane, trwale zapisz dostawę, a dopiero potem uruchom logikę biznesową. >oficjalna dokumentacja
2. Udostępnij port lokalny przez HTTPS
Uruchom serwer i tunel w drugim terminalu. Odpowiedź 401 na niepodpisany request potwierdza poprawny routing i odrzucenie uwierzytelnienia.
npx portpreview 3000
https://example.portpreview.dev/webhooks/linear
Uruchom serwer i tunel w drugim terminalu. Odpowiedź 401 na niepodpisany request potwierdza poprawny routing i odrzucenie uwierzytelnienia.
3. Skonfiguruj webhook w Linear
Zapisz pełny adres HTTPS, wybierz tylko potrzebne zdarzenia i wywołaj je na koncie testowym. Sekret przechowuj poza repozytorium.
Zapisz pełny adres HTTPS, wybierz tylko potrzebne zdarzenia i wywołaj je na koncie testowym. Sekret przechowuj poza repozytorium.
Zweryfikuj podpis na dokładnych bajtach
Oblicz HMAC z sekretem webhooka i dokładnymi danymi wskazanymi przez dostawcę. Porównuj w stałym czasie i nie zapisuj sekretu w logach.
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);
}
Oblicz HMAC z sekretem webhooka i dokładnymi danymi wskazanymi przez dostawcę. Porównuj w stałym czasie i nie zapisuj sekretu w logach. >poradnik
Zastosuj ochronę przed replay
Po poprawnej weryfikacji podpisu sprawdź znacznik czasu i odrzuć stare wiadomości. Synchronizuj zegar hosta.
Po poprawnej weryfikacji podpisu sprawdź znacznik czasu i odrzuć stare wiadomości. Synchronizuj zegar hosta.
Poznaj payload i identyfikatory dostawy
Obsługuj znane typy, toleruj dodatkowe pola i deduplikuj za pomocą stabilnego identyfikatora dostawy.
Obsługuj znane typy, toleruj dodatkowe pola i deduplikuj za pomocą stabilnego identyfikatora dostawy.
Odpowiadaj szybko i przetwarzaj idempotentnie
Zarezerwuj identyfikator i utwórz zadanie w jednej transakcji przed odpowiedzią 200. Przy awarii trwałego storage zwróć błąd.
Zarezerwuj identyfikator i utwórz zadanie w jednej transakcji przed odpowiedzią 200. Przy awarii trwałego storage zwróć błąd. >poradnik
Przetestuj cały przepływ lokalnie
- Uruchom serwer i tunel w drugim terminalu. Odpowiedź 401 na niepodpisany request potwierdza poprawny routing i odrzucenie uwierzytelnienia.
- Zapisz pełny adres HTTPS, wybierz tylko potrzebne zdarzenia i wywołaj je na koncie testowym. Sekret przechowuj poza repozytorium.
- Oblicz HMAC z sekretem webhooka i dokładnymi danymi wskazanymi przez dostawcę. Porównuj w stałym czasie i nie zapisuj sekretu w logach.
- Zarezerwuj identyfikator i utwórz zadanie w jednej transakcji przed odpowiedzią 200. Przy awarii trwałego storage zwróć błąd.
Sprawdź routing, nagłówki, podpis, czas odpowiedzi i idempotencję. Syntetyczne requesty najlepiej nadają się do testowania odrzuceń.
Rozwiązywanie problemów z webhookiem Linear
- Uruchom serwer i tunel w drugim terminalu. Odpowiedź 401 na niepodpisany request potwierdza poprawny routing i odrzucenie uwierzytelnienia.
- Oblicz HMAC z sekretem webhooka i dokładnymi danymi wskazanymi przez dostawcę. Porównuj w stałym czasie i nie zapisuj sekretu w logach.
- Po poprawnej weryfikacji podpisu sprawdź znacznik czasu i odrzuć stare wiadomości. Synchronizuj zegar hosta.
- Sprawdź ścieżkę POST, port tunelu, surowe body, sekret, zegar i opóźnienia bazy danych.
Lista zabezpieczeń dla środowiska lokalnego i produkcji
- Wymagaj HTTPS, ogranicz rozmiar i metodę, chroń sekrety, maskuj dane w logach i usuwaj nieaktualne adresy testowe.
- Zachowaj surowe body przed parserem JSON. Najpierw sprawdź podpis i dane, trwale zapisz dostawę, a dopiero potem uruchom logikę biznesową.
- Zarezerwuj identyfikator i utwórz zadanie w jednej transakcji przed odpowiedzią 200. Przy awarii trwałego storage zwróć błąd.
Wymagaj HTTPS, ogranicz rozmiar i metodę, chroń sekrety, maskuj dane w logach i usuwaj nieaktualne adresy testowe. >poradnik
