Verifikimi i një dërgese
`OpenEmail::verifyWebhookSignature`: në kohë konstante, me një dritare kundër riluajtjes, dhe me ngjarjen e analizuar si rezultat.
Në një handler kërkese
Një URL webhook-u është publike. Çdo gjë në internet mund të bëjë POST me JSON të formës së duhur drejt saj, ndaj një handler që lexon type e ngjarjes pa kontrolluar nënshkrimin është një API shkrimi e hapur.
use OpenEmail\Exception\WebhookSignatureException;use OpenEmail\OpenEmail; $payload = (string) file_get_contents('php://input'); try { $event = OpenEmail::verifyWebhookSignature( $payload, $_SERVER, (string) getenv('OPENEMAIL_WEBHOOK_SECRET'), toleranceSeconds: 300, );} catch (WebhookSignatureException) { http_response_code(400); return;} error_log($event['type'] . ' ' . $event['id']);http_response_code(204);Jepni trupin E PAPËRPUNUAR, si string. Dekodimi dhe rikodimi i tij ndryshojnë renditjen e çelësave dhe hapësirat, dhe nënshkrimi nuk do të përputhet, prandaj payload-i ka tipin string: një array, si rezultati i json_decode() ose input-i i analizuar nga një framework, jep një TypeError në vend që të kontrollohet. Header-at mund të jenë një array me emrin e header-it me çfarëdo shkronjash, siç e kthen getallheaders(), $_SERVER, ku header-i mbërrin si HTTP_X_OPENEMAIL_SIGNATURE, çdo iterable si një HeaderBag i Symfony ose Laravel, ose një kërkesë PSR-7. Një vlerë që është array lexohet nga elementi i saj i parë.
Dy gjëra që kjo i trajton, e që një kontroll i bërë me dorë zakonisht nuk i trajton: e krahason MAC-un në kohë konstante me hash_equals(), që parashtesa e saktë të mos nxirret duke matur kohën, dhe e refuzon një dërgesë më të vjetër se toleranceSeconds: në cilindo drejtim, pesë minuta, veç nëse thoni ndryshe, që një kërkesë e kapur të mos jetë e riluajtshme përgjithmonë. toleranceSeconds: 0 e çaktivizon kontrollin e riluajtjes. Të dy këta defekte janë të heshtur. Një handler me cilindo prej tyre i kalon të gjitha testet që do t’ju shkonte mendja të shkruanit.
Hedh OpenEmail\Exception\WebhookSignatureException në çdo dështim: një header X-OpenEmail-Signature që mungon, një që nuk është në formën t=<seconds>,v1=<hex>, një vulë kohore jashtë dritares, një nënshkrim që nuk përputhet, ose një trup i nënshkruar që nuk është objekt JSON. Në sukses, kthen trupin e dekoduar në një array me çelësa në camelCase, me id, type, createdAt dhe data, ndaj nuk ka një json_decode() të dytë për ta gabuar.
Një sekret bosh hedh në vend të kësaj OpenEmail\Exception\InvalidArgumentException, sepse ai është një gabim në konfigurimin tuaj dhe jo një dërgesë e keqe, dhe (string) para getenv() e kthen një variabël të pacaktuar pikërisht në këtë. Kapni vetëm WebhookSignatureException dhe përgjigjuni me 400. Atëherë një sekret që mungon e mbyll skriptin me një përjashtim të pakapur, të cilit PHP në prodhim i përgjigjet me 500, dhe dërgesa riprovohet sapo ta rregulloni, në vend që çdo ngjarje të refuzohet si e falsifikuar.
Kontrolli mbështetet te hash_hmac() dhe hash_equals(), që i ka çdo build i PHP-së, ndaj nuk ka nevojë për asnjë zgjerim. Shembulli bën return në vend që të thërrasë exit, ndaj të njëjtat rreshta funksionojnë edhe brenda një funksioni ose një front controller-i.
Në një aplikacion PSR-7
Me një kërkesë PSR-7, siç jua japin Slim dhe Mezzio, jepni vetë kërkesën si header-at dhe trupin e saj si string.
use OpenEmail\Exception\WebhookSignatureException;use OpenEmail\OpenEmail;use Psr\Http\Message\ResponseFactoryInterface;use Psr\Http\Message\ResponseInterface;use Psr\Http\Message\ServerRequestInterface; function receiveOpenEmail(ServerRequestInterface $request, ResponseFactoryInterface $responses): ResponseInterface{ try { $event = OpenEmail::verifyWebhookSignature( (string) $request->getBody(), $request, (string) getenv('OPENEMAIL_WEBHOOK_SECRET'), ); } catch (WebhookSignatureException) { return $responses->createResponse(400); } error_log($event['type'] . ' ' . $event['id']); return $responses->createResponse(204);}Përgjigjuni shpejt: një dërgesë që nuk merr përgjigje brenda 5 sekondave llogaritet si e dështuar dhe dërgohet sërish më vonë, ndaj ia kaloni punën një radhe dhe përgjigjuni me një 2xx. Kontrolluesit e Laravel dhe Symfony, bashkë me kalimin te radha, janë në faqen e framework-eve.
OpenEmail\Constants\WebhookSignatureHeaders emërton tre header-at që mbart një dërgesë: X-OpenEmail-Signature, X-OpenEmail-Event me llojin e ngjarjes, dhe X-OpenEmail-Delivery me id-në e saj, të njëjtin id si në trup. Kjo id mbetet e njëjtë në çdo riprovim dhe riluajtje të ngjarjes, ndaj është ajo që duhet ruajtur kur anashkaloni ngjarjet që i keni trajtuar tashmë.
Ndërsa sekreti rrotullohet
webhooks->rotateSecret nuk ka dritare mbivendosjeje: dërgesat nënshkruhen me sekretin e ri që nga çasti kur ai kthehet. Vendosni fillimisht në prodhim një marrës që pranon cilindo nga dy sekretet, rrotulloni, ruajeni sekretin e ri aty ku e lexon marrësi, pastaj hiqni të vjetrin.
use OpenEmail\Exception\WebhookSignatureException;use OpenEmail\OpenEmail; function verifyDelivery(string $payload, mixed $headers): array{ $next = getenv('OPENEMAIL_WEBHOOK_SECRET_NEXT'); try { return OpenEmail::verifyWebhookSignature($payload, $headers, (string) getenv('OPENEMAIL_WEBHOOK_SECRET')); } catch (WebhookSignatureException $error) { if (!is_string($next) || $next === '') { throw $error; } return OpenEmail::verifyWebhookSignature($payload, $headers, $next); }}webhooks->test dërgon një ngjarje sintetike të nënshkruar, ndaj thirreni pas rrotullimit për të provuar se sekreti i ri verifikohet para se të hiqni të vjetrin.