Bezpečnost
casinocrm.io používá HMAC-SHA256 pro webhook autentizaci na obou koncích (inbound + outbound). Stejné schéma jako Stripe / GitHub — pravděpodobně už váš tým zná.
Signature scheme
signature = hex(HMAC_SHA256(secret, "<timestamp>.<body>"))
timestamp— Unix sekundy (string)body— raw request body bytes (NE re-serializované JSON; podepište přesně to, co letí po drátě)secret— sdílený secret, vyměňováno out-of-band
Headers casinocrm.io posílá + očekává:
X-casinocrm.io-Signature: <hex-hmac-sha256>
X-casinocrm.io-Timestamp: <unix-seconds>
casinocrm.io také akceptuje prefix v1=<hex> na signature (Stripe-kompatibilní).
Anti-replay
Odmítejte události s timestamp mimo [now - 5 min, now + 5 min]. casinocrm.io tohle vynucuje na inbound; doporučujeme to samé na vašich endpointech.
Constant-time porovnání
Vždy porovnávejte signatures s constant-time equality (např. Node crypto.timingSafeEqual, Go subtle.ConstantTimeCompare). Naivní == je timing-leakable.
Reference implementace
Node.js / TypeScript
import { createHmac, timingSafeEqual } from "node:crypto";
function verify(rawBody: string, header: string, ts: string, secret: string): boolean {
// Anti-replay
if (Math.abs(Date.now() / 1000 - Number(ts)) > 300) return false;
// Compute expected
const expected = createHmac("sha256", secret)
.update(`${ts}.${rawBody}`, "utf8")
.digest("hex");
// Povolit v1= prefix
const provided = header.replace(/^v1=/, "");
if (provided.length !== expected.length) return false;
try {
return timingSafeEqual(Buffer.from(provided, "hex"), Buffer.from(expected, "hex"));
} catch {
return false;
}
}
Go
import (
"crypto/hmac"
"crypto/sha256"
"encoding/hex"
"strconv"
"strings"
"time"
)
func verify(rawBody []byte, header, ts, secret string) bool {
tsNum, err := strconv.ParseInt(ts, 10, 64)
if err != nil {
return false
}
if abs(time.Now().Unix()-tsNum) > 300 {
return false
}
mac := hmac.New(sha256.New, []byte(secret))
mac.Write([]byte(ts + "." + string(rawBody)))
expected := hex.EncodeToString(mac.Sum(nil))
provided := strings.TrimPrefix(header, "v1=")
return hmac.Equal([]byte(provided), []byte(expected))
}
func abs(x int64) int64 { if x < 0 { return -x }; return x }
Python
import hmac
import hashlib
import time
def verify(raw_body: bytes, header: str, ts: str, secret: str) -> bool:
try:
ts_num = int(ts)
except ValueError:
return False
if abs(time.time() - ts_num) > 300:
return False
expected = hmac.new(
secret.encode("utf-8"),
f"{ts}.{raw_body.decode('utf-8')}".encode("utf-8"),
hashlib.sha256,
).hexdigest()
provided = header.removeprefix("v1=")
return hmac.compare_digest(expected, provided)
Idempotence
Kromě signature verifikace inbound webhook receiver deduplikuje na (adapter_slug, event_id) přes webhook_events_processed ledger. Replays vrací 200 { applied: false, replay: true } okamžitě.
Měli byste deduplikovat i na své straně — casinocrm.io může retry-ovat outbound volání (např. na 504 z vaší strany, které ve skutečnosti uspělo). Použijte grant_id od casinocrm.io (nebo jakékoli correlation id, které jsme předali) jako dedup klíč na vaší straně.
Rotace secretů
Koordinujte se s týmem casinocrm.io přes Slack / email. Rotation flow:
- Vygenerujte nový secret na obou stranách
- casinocrm.io akceptuje OBA staré + nové po 24h overlap okno (per-adapter env var:
casinocrm.io_ADAPTER_<SLUG>_WEBHOOK_SECRET_NEXT) - Po overlapu starý secret zrušte
- Ověřte metriky (žádný spike v
signature mismatcherrorech)
Rotation cadence: minimálně ročně, nebo okamžitě při podezření na leak.
URL allowlist
Outbound webhooks od casinocrm.io (journey call_webhook uzly) jsou HTTPS-only. Odmítáme posílat na plain HTTP URLs i v dev — vždy šifrujte journey context v přenosu.
API key rotation
Project API klíče lze rotovat z adminu: Settings → API keys → Revoke + Create new. casinocrm.io akceptuje oba klíče po konfigurovatelné overlap okno, takže můžete rolovat deploys bez downtime.