Orbtrace

Webhook doğrulama

Bir alarm webhook'unun gerçekten Orbtrace örneğinizden geldiğini doğrulayın — HMAC-SHA256 imzası, zaman-damgası kayma kontrolü ve idempotency-key tekilleştirmesi. Node, Python, Go ve Java için alıcı kod parçaları.

Bir Webhook alarm kanalı eklediğinizde, Orbtrace her teslimi imzalar; böylece alıcınız bunun gerçekten sizin örneğinizden geldiğini kanıtlayabilir (genel uç noktanıza yapılan sahte bir istek değil). Şema, sektör standardı Stripe tarzı HMAC'tir — alıcınız bunu standart kütüphaneyle doğrular, Orbtrace SDK'sı gerekmez.

İmza

Her istek üç başlık taşır:

BaşlıkAnlamı
X-Orbtrace-Signaturet=<unix-saniye>,v1=<hex> — zaman damgası ve HMAC.
X-Orbtrace-Idempotency-KeyAlarm örneği başına sabit — yeniden denemeleri buna göre tekilleştirin.
X-Orbtrace-Delivery-AttemptTeslim deneme numarası (1 = ilk deneme).

İmzalanan dize; zaman damgası, bir . karakteri, ardından ham istek gövdesidir:

imzalanan = "<unix-saniye>" + "." + <ham_gövde>
v1        = HMAC_SHA256(imzalama_sırrı, imzalanan)   // küçük harf hex

Alıcınız sırasıyla ŞUNLARI yapmalıdır:

  1. t'yi ayrıştırın ve saatinizden 5 dakikadan fazla saparsa reddedin — yakalanmış bir gövdenin tekrarını (replay) bu öldürür.
  2. HMAC-SHA256(sır, t + "." + ham_gövde) değerini yeniden hesaplayın ve v1 ile sabit zamanlı karşılaştırın.
  3. X-Orbtrace-Idempotency-Key üzerinden tekilleştirin ki yeniden denenen bir teslim iki kez işlenmesin.

Gövdenin ham baytlarına karşı doğrulayın, herhangi bir JSON ayrıştır/yeniden-serileştir işleminden önce — yeniden kodlama boşlukları değiştirir ve HMAC'ı bozar.

Alıcı kod parçaları

Node.js

import crypto from 'node:crypto';
 
function verify(rawBody, headers, secret) {
  const [tPart, v1Part] = headers['x-orbtrace-signature'].split(',');
  const t = tPart.slice(2);            // "t=" kırp
  const sig = v1Part.slice(3);         // "v1=" kırp
  if (Math.abs(Date.now() / 1000 - Number(t)) > 300) throw new Error('stale');
  const expected = crypto.createHmac('sha256', secret)
    .update(`${t}.${rawBody}`).digest('hex');
  const ok = crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(expected));
  if (!ok) throw new Error('bad signature');
}

Python

import hashlib, hmac, time
 
def verify(raw_body: bytes, headers, secret: str):
    t, v1 = (p.split("=", 1)[1] for p in headers["X-Orbtrace-Signature"].split(","))
    if abs(time.time() - int(t)) > 300:
        raise ValueError("stale")
    signed = f"{t}.".encode() + raw_body
    expected = hmac.new(secret.encode(), signed, hashlib.sha256).hexdigest()
    if not hmac.compare_digest(v1, expected):
        raise ValueError("bad signature")

Go

func Verify(rawBody []byte, header, secret string) error {
    var t, v1 string
    for _, p := range strings.Split(header, ",") {
        kv := strings.SplitN(p, "=", 2)
        switch kv[0] {
        case "t":
            t = kv[1]
        case "v1":
            v1 = kv[1]
        }
    }
    ts, _ := strconv.ParseInt(t, 10, 64)
    if math.Abs(float64(time.Now().Unix()-ts)) > 300 {
        return errors.New("stale")
    }
    mac := hmac.New(sha256.New, []byte(secret))
    mac.Write([]byte(t + "."))
    mac.Write(rawBody)
    expected := hex.EncodeToString(mac.Sum(nil))
    if !hmac.Equal([]byte(v1), []byte(expected)) {
        return errors.New("bad signature")
    }
    return nil
}

Java

String[] parts = header.split(",");
String t  = parts[0].substring(2);   // "t="
String v1 = parts[1].substring(3);   // "v1="
if (Math.abs(Instant.now().getEpochSecond() - Long.parseLong(t)) > 300)
    throw new SecurityException("stale");
 
Mac mac = Mac.getInstance("HmacSHA256");
mac.init(new SecretKeySpec(secret.getBytes(UTF_8), "HmacSHA256"));
mac.update((t + ".").getBytes(UTF_8));
byte[] expected = mac.doFinal(rawBody);                 // rawBody = ham baytlar
if (!MessageDigest.isEqual(fromHex(v1), expected))
    throw new SecurityException("bad signature");

Idempotency (tekillik)

Orbtrace başarısız teslimleri yeniden dener, dolayısıyla aynı alarm birden fazla gelebilir. X-Orbtrace-Idempotency-Key bir alarm örneği için yeniden denemeler boyunca sabittir — işlediğiniz anahtarları saklayın ve tekrarları atın. X-Orbtrace-Delivery-Attempt başlığı bilgilendiricidir (hangi yeniden denemenin sonunda ulaştığını loglamak için kullanışlı).