Sari la conținut

Signature Verification

Cum să verificați semnăturile platformei webhook cu HMAC-SHA256 peste corpul brut și timestamp, cu exemple de lucru în Go, Python și JavaScript.

Fiecare platformă webhook include o semnătură HMAC. Verificați-o înainte de a analiza corpul sau de a efectua efecte secundare. Verificarea semnăturilor dovedește că sarcina de utilizare și timestamp au fost produse cu secretul de semnătură al abonamentului; un timestamp verifică prospețimea limitează atacurile de replay.

the platform signs each webhook payload using HMAC-SHA256 cu secretul de semnătură din abonamentul dvs. webhook. Semnătura este trimisă în antetul X-Probo-Webhook-Signature.

Mesajul semnat este concatenarea timestamp-ului și a corpului de cerere brut, separat de un colon:

{timestamp}:{body}

Where:

  • timestamp este valoarea din antetul X-Probo-Webhook-Timestamp (secunde Unix)
  • body este corpul de solicitare JSON brută

Use the full signing secret string (inclusiv prefixul whsec_) ca cheie HMAC.

  1. Extract the headers

    Citiți X-Probo-Webhook-Timestamp și X-Probo-Webhook-Signature din cerere.

  2. Build the signed message

    Conectați timestamp-ul, un colon (:) și corpul de solicitare brută.

  3. Compute the expected signature

    Calculați HMAC-SHA256 folosind secretul complet de semnătură (inclusiv prefixul whsec_) ca cheie și mesajul semnat ca intrare.

  4. Compare signatures

    Utilizați o comparație constantă a timpului pentru a verifica dacă semnătura calculată se potrivește cu antetul X-Probo-Webhook-Signature.

  5. Check timestamp freshness

    După ce semnătura se potrivește, respingeți solicitarea dacă timestamp-ul său este mai mare de 5 minute în trecut sau în viitor.

package main
import (
"crypto/hmac"
"crypto/sha256"
"encoding/hex"
"fmt"
"io"
"net/http"
"strconv"
"time"
)
func verifyWebhook(r *http.Request, signingSecret string) ([]byte, error) {
body, err := io.ReadAll(r.Body)
if err != nil {
return nil, err
}
timestamp := r.Header.Get("X-Probo-Webhook-Timestamp")
signature := r.Header.Get("X-Probo-Webhook-Signature")
if timestamp == "" || signature == "" {
return nil, fmt.Errorf("missing signature headers")
}
mac := hmac.New(sha256.New, []byte(signingSecret))
mac.Write([]byte(timestamp))
mac.Write([]byte(":"))
mac.Write(body)
received, err := hex.DecodeString(signature)
if err != nil || !hmac.Equal(mac.Sum(nil), received) {
return nil, fmt.Errorf("invalid signature")
}
signedAt, err := strconv.ParseInt(timestamp, 10, 64)
if err != nil {
return nil, fmt.Errorf("invalid timestamp")
}
delta := time.Now().Unix() - signedAt
if delta > 300 || delta < -300 {
return nil, fmt.Errorf("stale timestamp")
}
return body, nil
}
  • Verificați semnătura înainte de a analiza JSON, de a autoriza organizația sau de a coada lucrările.
  • Refuzați timestamp-urile lipsă, deformate, învechite și datate în viitor. Exemplele utilizează o toleranță de 5 minute.
  • Verificați mai întâi lungimea lor, unde comparațiaAPInecesită intrări de lungime egală.
  • Păstrați un secret separat pentru fiecare abonament și stocați-l într-un manager secret.
  • Returnați un răspuns generic 400 sau 403. Nu dezvăluiți care verificare a eșuat.
  • Înregistrează eventId după verificare și o procesează o singură dată. validarea timestamp limitează timpul de redare; idempotency previne efectele secundare duplicate.
Symptom Likely cause
Every signature fails Cadrul parsează sau modifică corpul înainte de verificare
Only non-ASCII payloads fail Receptorul a decodificat și re-codificat corpul în loc să hasheze byte brute
timingSafeEqual throws Semnătura primită nu a fost validată mai întâi ca 32-byte hexadecimal
Livrările valabile sunt aproape ca stale Ceasul receptorului nu este sincronizat sau timestamp-ul a fost tratat ca milisecunde
Verificarea funcționează cu o singură abonare Punctul final este selectarea secretului de abonament greșit

Ultima actualizare: