webhook-id: 7c1e2f0a-3b5d-4e8f-9a61-2d4c8b0e5f13
webhook-timestamp: 1790490600
webhook-signature: v1,s0RzLJ1d7Cxd9nwe8pqER9DWy/stEIB+uT9MlGVLlM0=
| Antraštė |
Kas tai yra |
| webhook-id |
Pristatymas, ta pati reikšmė kaip delivery_id turinyje. Įsiminkite jau apdorotus pristatymus ir į pakartotinį atsakykite 200, darbo nekartodami. |
| webhook-timestamp |
Kada mes tai pasirašėme, pilnomis sekundėmis nuo 1970 metų. Atmeskite viską, kas nuo jūsų laikrodžio skiriasi daugiau nei penkiomis minutėmis, kad senas pristatymas negalėtų būti jums atsiųstas dar kartą. |
| webhook-signature |
v1, tada id, laiko žymos ir neapdoroto turinio base64 HMAC-SHA256, sujungti taškais ir sudaryti naudojant jūsų pasirašymo raktą. Jų gali būti daugiau nei vienas, atskirti tarpais. Priimkite pristatymą, jei sutampa bent vienas iš jų. |
Pasirašymo raktas
Jis prasideda whsec_. Pašalinkite tai ir likusią dalį iškoduokite iš base64: tie baitai yra HMAC raktas, o ne tekstas, kurį nukopijavote. Jis parodomas vieną kartą, kai prijungiate webhook arba Publishing puslapyje paspaudžiate "New signing key", ir mes jo niekada nesiunčiame.
Patikrinkite neapdorotą turinį
Apskaičiuokite parašą iš baitų tiksliai tokių, kokie jie atėjo, prieš bet kam juos išnagrinėjant. JSON, kuris buvo iškoduotas ir vėl užkoduotas, nebėra tie patys baitai, todėl jo parašas nesutaps.
PHP
<?php
$raw = getenv('SEONIS_SIGNING_KEY'); // whsec_...
// No key, no deliveries. A missing or empty variable would otherwise become
// an empty HMAC key, and anybody can sign with that.
$secret = is_string($raw) && str_starts_with($raw, 'whsec_')
? base64_decode(substr($raw, strlen('whsec_')), true)
: false;
if ($secret === false || strlen($secret) < 24) {
http_response_code(500);
exit('Signing key not configured.');
}
$id = $_SERVER['HTTP_WEBHOOK_ID'] ?? '';
$timestamp = $_SERVER['HTTP_WEBHOOK_TIMESTAMP'] ?? '';
$header = $_SERVER['HTTP_WEBHOOK_SIGNATURE'] ?? '';
$body = file_get_contents('php://input');
if (! ctype_digit($timestamp) || abs(time() - (int) $timestamp) > 300) {
http_response_code(401);
exit('Too old.');
}
$expected = base64_encode(hash_hmac('sha256', "$id.$timestamp.$body", $secret, true));
$valid = false;
foreach (explode(' ', $header) as $signature) {
[$version, $value] = array_pad(explode(',', $signature, 2), 2, '');
$valid = $valid || ($version === 'v1' && hash_equals($expected, $value));
}
if (! $valid) {
http_response_code(401);
exit('Bad signature.');
}
$delivery = json_decode($body, true);
Node.js
import crypto from 'node:crypto';
import express from 'express';
const app = express();
// No key, no server. An empty variable would otherwise become an empty HMAC
// key, and anybody can sign with that.
const raw = process.env.SEONIS_SIGNING_KEY ?? '';
if (!raw.startsWith('whsec_')) throw new Error('SEONIS_SIGNING_KEY is not set.');
const key = Buffer.from(raw.slice('whsec_'.length), 'base64');
if (key.length < 24) throw new Error('SEONIS_SIGNING_KEY is not a valid key.');
// express.raw keeps the body as the bytes we signed.
app.post('/hooks/seonis', express.raw({ type: 'application/json' }), (req, res) => {
const id = req.get('webhook-id') ?? '';
const timestamp = req.get('webhook-timestamp') ?? '';
const header = req.get('webhook-signature') ?? '';
if (!/^\d+$/.test(timestamp) || Math.abs(Date.now() / 1000 - Number(timestamp)) > 300) {
return res.status(401).send('Too old.');
}
const expected = crypto.createHmac('sha256', key)
.update(`${id}.${timestamp}.`)
.update(req.body)
.digest();
const valid = header.split(' ').some((signature) => {
const [version, value = ''] = signature.split(',');
const given = Buffer.from(value, 'base64');
return version === 'v1' && given.length === expected.length && crypto.timingSafeEqual(given, expected);
});
if (!valid) return res.status(401).send('Bad signature.');
const delivery = JSON.parse(req.body);
res.json({ ok: true });
});
Arba naudokite biblioteką
Oficialios Standard Webhooks bibliotekos, skirtos PHP, JavaScript, Python, Go, Ruby ir kitoms kalboms, visa tai atlieka vienu iškvietimu. Pateikite joms pasirašymo raktą tokį, koks jis buvo parodytas jums, įskaitant whsec_.
github.com/standard-webhooks
Senesnės antraštės
Pristatymuose taip pat siunčiama pasirašymo paslaptis kaip bearer prieigos raktas ir X-AutoSEO-LT-Signature, tik turinio HMAC, sudarytas su ta paslaptimi. Jie vis dar siunčiami, kad jais paremti galiniai taškai toliau veiktų. Naujas galinis taškas vietoj to turėtų tikrinti webhook-signature: senasis parašas neapima nei laiko, nei id, o jo raktas keliauja kiekvienoje užklausoje.
AutoSEO, Outrank ir BabyLoveGrowth formatai siunčia tiksliai tai, ką atsiuntė tos paslaugos, todėl šių antraščių jie neturi.