Aller au contenu principal

Cas d’usage

Vérification HMAC-SHA256 des webhooks (timing-safe)

Comparer les hash en temps constant pour éviter le timing attack

Tous les webhooks Scell.io sont signés HMAC-SHA256 avec le secret du webhook (configuré au moment de la création du endpoint). La signature est calculée sur la concaténation `<timestamp>.<raw_body>` (timestamp dans le header `X-Scell-Timestamp`) et envoyée dans `X-Scell-Signature`. Côté receveur, ne JAMAIS comparer les hash avec `===` (vulnérable au timing attack) : utiliser `timingSafeEqual` (Node) ou `hash_equals` (PHP). Vérifier aussi le timestamp : tout message > 5 minutes est rejeté pour empêcher les replay attacks. Sur les webhooks sortants en mTLS optionnel pour les tenants enterprise.

À retenir

  • Signature : `HMAC-SHA256(secret, timestamp + '.' + raw_body)`
  • Header `X-Scell-Signature` (hex) + `X-Scell-Timestamp` (epoch)
  • Tolérance timestamp : 5 minutes (anti-replay)
  • Comparaison timing-safe obligatoire (`hash_equals` / `timingSafeEqual`)
  • Secret rotativable via dashboard sans downtime
  • mTLS optionnel sur sortants pour entreprises

Exemple de code

<?php
// Laravel controller webhook receiver
public function handle(\Illuminate\Http\Request $request)
{
    $signature = $request->header('X-Scell-Signature');
    $timestamp = (int) $request->header('X-Scell-Timestamp');
    $rawBody = $request->getContent();

    // 1. Anti-replay
    if (abs(time() - $timestamp) > 300) {
        abort(401, 'Timestamp too old');
    }

    // 2. Vérification timing-safe
    $expected = hash_hmac('sha256', $timestamp . '.' . $rawBody, config('services.scell.webhook_secret'));
    if (!hash_equals($expected, $signature)) {
        abort(401, 'Invalid signature');
    }

    // 3. Idempotence (table avec UNIQUE sur event_id)
    $event = json_decode($rawBody, true);
    \App\Models\WebhookEvent::firstOrCreate(['event_id' => $event['event_id']]);

    return response()->noContent();
}

Voir aussi

Vos préférences cookies

Nous utilisons des cookies pour améliorer votre expérience. Les cookies essentiels sont toujours actifs. Politique cookies.