Skip to main content
Comment fonctionnent les webhooks
Language

Webhooks

Configurez des destinations webhook pour recevoir les alertes de sécurité cside via des requêtes HTTP POST avec vérification de signature HMAC.

Comment fonctionnent les webhooks

Les webhooks vous permettent de recevoir les notifications cside sous forme de requêtes HTTP POST vers une URL que vous spécifiez. Lorsqu’un déclencheur s’active, cside envoie un payload JSON à votre endpoint webhook contenant les détails de l’événement.

Vous pouvez configurer les webhooks dans trois formats :

  • JSON - payload JSON standard pour les intégrations personnalisées
  • Slack - pré-formaté pour les canaux Slack
  • Discord - pré-formaté pour les canaux Discord

Ajouter une destination webhook

Pour ajouter un webhook comme destination dans une notification config :

  1. Ouvrez le tableau de bord et naviguez vers Team Settings > Notifications
  2. Créez une nouvelle notification config ou modifiez-en une existante
  3. Sous Send To, cliquez sur Add destination et sélectionnez Webhook
  4. Dans le panneau Configuration, remplissez :
    • URL : l’URL où vous souhaitez recevoir les notifications webhook (par ex., https://example.com/cside/notify)
    • Secret : une clé secrète utilisée pour signer les requêtes webhook (utilisée pour la vérification HMAC)
    • Format : choisissez JSON, Slack ou Discord
  5. Cliquez sur Save ou Save & Test

Le payload webhook

cside envoie une requête HTTP POST par événement, avec Content-Type: application/json et un en-tête x-cside-signature (voir la vérification HMAC). Chaque payload contient un champ event de premier niveau qui identifie le type d’événement, un horodatage, l’équipe ou le domaine auquel l’événement appartient et un objet data propre à l’événement.

Le champ qui contient l’horodatage varie selon l’événement : alert.created et les événements de digest utilisent timestamp, tandis que security_headers.changed utilise sent_at. Lisez d’abord event, puis analysez le reste du payload selon les tableaux ci-dessous. Traitez les champs inconnus comme facultatifs afin que de nouveaux champs ne cassent pas votre intégration.

IDs d’événements

IDEnvoyé parChamp d’horodatageObjet data
alert.createdLes déclencheurs immédiats, et le déclencheur Vulnerable Script Detected en planification immédiatetimestampObjet d’alerte
security_headers.changedSecurity Headers Changedsent_atChangement d’en-têtes de sécurité
pci_report.availablePCI Report (quotidien ou hebdomadaire)timestampRapport PCI
csp_block_digestScript Blocked by CSP (quotidien ou hebdomadaire)timestampDigest des blocages CSP
csp_policy_digestCSP Policy Digest (quotidien ou hebdomadaire)timestampDigest de politique CSP
vulnerable_script_digestVulnerable Script Detected (quotidien ou hebdomadaire)timestampDigest des vulnérabilités
outbound_connections_pending_reviewOutbound Connections Pending Review (quotidien ou hebdomadaire)timestampConnexions sortantes en attente

Seuls alert.created et security_headers.changed respectent les formats Slack et Discord. Les événements de digest sont toujours envoyés en JSON, quel que soit le format configuré sur la destination.

Save & Test envoie un exemple de payload alert.created avec le trigger_type du déclencheur de la règle, y compris pour les déclencheurs qui livrent normalement un digest.

Tous les horodatages sont des chaînes ISO 8601 en UTC. Les IDs sont des chaînes décimales.

alert.created

Envoyé pour chaque alerte levée par un déclencheur immédiat : Script Threat Detected, High-risk Vulnerability, Managed Flagged, Malicious Tag Manager, Malicious Outbound Connection et Vulnerable Script Detected lorsque la règle est configurée pour une livraison immédiate.

ChampTypeDescription
event"alert.created"L’ID de l’événement
timestampstringDate d’envoi du webhook
domain_idstringID du domaine sur lequel l’alerte s’est déclenchée
domainstringNom d’hôte du domaine sur lequel l’alerte s’est déclenchée
trigger_typestringLe déclencheur qui a produit l’alerte, voir le tableau ci-dessous
dataobjectL’objet d’alerte
destination_idstringID de la destination webhook qui a reçu l’événement

Valeurs de trigger_type :

trigger_typeDéclencheur
script_threat_detected_by_rules_engineScript Threat Detected
high_risk_vulnerability_detectedHigh-risk Vulnerability
vulnerable_script_detectedVulnerable Script Detected (planification immédiate)
cside_managed_list_flaggedManaged Flagged
malicious_tag_managerMalicious Tag Manager
malicious_outbound_connectionMalicious Outbound Connection

Objet d’alerte

ChampTypeDescription
target_type"URL" | "HASH" | "IP" | "DOMAIN" | "HOSTNAME" | "VENDOR"Ce que contient target
targetstringLe sujet de l’alerte : une URL de script, un hash de script, ou l’IP, le domaine ou le nom d’hôte signalé
action"alert"L’action enregistrée pour l’alerte. Actuellement toujours "alert" ; traitez-la comme une chaîne pour rester compatible avec de futures actions
ruleset_idstringID du ruleset qui a correspondu
ruleset_namestringNom du ruleset qui a correspondu
ruleset_descriptionstringDescription du ruleset qui a correspondu
script_urlsstring[]Les URLs de script qui ont déclenché l’alerte ; peut être vide
vuln_cve_idstring | nullID de la vulnérabilité (CVE ou GHSA) ; null pour les alertes sans lien avec une vulnérabilité
vuln_packagestring | nullPackage affecté ; null pour les alertes sans lien avec une vulnérabilité
vuln_severitystring | nullSévérité rapportée, par exemple HIGH ; null pour les alertes sans lien avec une vulnérabilité
vuln_reference_urlstring | nullLien vers la vulnérabilité sur osv.dev ; null en l’absence de vuln_cve_id

Les quatre champs vuln_* ne sont renseignés que pour vulnerable_script_detected et high_risk_vulnerability_detected. Pour tout autre trigger_type, ils valent null.

{
  "event": "alert.created",
  "timestamp": "2026-09-17T10:15:30.123456Z",
  "domain_id": "1897425263682514945",
  "domain": "checkout.example.com",
  "trigger_type": "high_risk_vulnerability_detected",
  "data": {
    "target_type": "URL",
    "target": "https://cdn.example.com/lodash.js",
    "action": "alert",
    "ruleset_id": "ruleset_vulnerability_detected",
    "ruleset_name": "Vulnerability Detected",
    "ruleset_description": "A script with a known vulnerability was loaded",
    "script_urls": ["https://cdn.example.com/lodash.js"],
    "vuln_cve_id": "CVE-2024-1234",
    "vuln_package": "lodash",
    "vuln_severity": "HIGH",
    "vuln_reference_url": "https://osv.dev/vulnerability/CVE-2024-1234"
  },
  "destination_id": "1897425263682514950"
}

security_headers.changed

Envoyé par Security Headers Changed lorsque les en-têtes de sécurité observés sur les pages de paiement d’un domaine diffèrent entre deux fenêtres d’observation consécutives.

ChampTypeDescription
event"security_headers.changed"L’ID de l’événement
sent_atstringDate d’envoi du webhook
team_idstringID de l’équipe propriétaire du domaine
data.domain_idstringID du domaine
data.domainstringNom d’hôte du domaine
data.checkout_pathsstring[]Les sélecteurs de pages de paiement observés
data.changesobject[]Une entrée par en-tête modifié
data.changes[].header_namestringL’en-tête, par exemple content-security-policy
data.changes[].from_valuestringLa valeur précédente ; vide lorsque l’en-tête a été ajouté
data.changes[].to_valuestringLa nouvelle valeur ; vide lorsque l’en-tête a disparu
data.changes[].from_datestringDébut de la fenêtre d’observation où la valeur précédente a été vue
data.changes[].to_datestringDébut de la fenêtre d’observation où la nouvelle valeur a été vue

pci_report.available

Envoyé par PCI Report lorsqu’une génération de rapport quotidienne ou hebdomadaire a été mise en file d’attente pour les domaines concernés qui ont des chemins de paiement configurés.

ChampTypeDescription
event"pci_report.available"L’ID de l’événement
timestampstringDate d’envoi du webhook
organization_idstringID de l’organisation
team_idstringID de l’équipe
data.domain_idsstring[]IDs des domaines couverts par le rapport
data.domainsstring[]Noms d’hôte des domaines couverts par le rapport, dans le même ordre
data.starting_datestringDébut de la période de rapport
data.ending_datestringFin de la période de rapport
data.period"daily" | "weekly"La planification de la règle

csp_block_digest

Envoyé par Script Blocked by CSP avec les rapports de violation de Content Security Policy collectés sur la période. Rien n’est envoyé s’il n’y a eu aucune violation.

ChampTypeDescription
event"csp_block_digest"L’ID de l’événement
timestampstringDate d’envoi du webhook
organization_idstringID de l’organisation
team_idstringID de l’équipe
data.period"daily" | "weekly"La planification de la règle
data.start_datestringDébut de la période du digest
data.end_datestringFin de la période du digest
data.domainsobject[]Compteurs par domaine, triés par nombre de violations
data.domains[].domain_idstringID du domaine
data.domains[].domainstringNom d’hôte du domaine
data.domains[].violation_countnumberViolations enregistrées pour le domaine
data.top_blocked_urisstring[]Les URIs les plus fréquemment bloquées
data.top_violated_directivesstring[]Les directives les plus fréquemment violées
data.total_violationsnumberTotal des violations sur tous les domaines concernés

csp_policy_digest

Envoyé par CSP Policy Digest pour chaque domaine concerné dont la Content Security Policy recommandée a changé sur la période. Rien n’est envoyé si aucun domaine n’a changé.

ChampTypeDescription
event"csp_policy_digest"L’ID de l’événement
timestampstringDate d’envoi du webhook
organization_idstringID de l’organisation
team_idstringID de l’équipe
data.period"daily" | "weekly"La planification de la règle
data.domainsobject[]Une entrée par domaine dont la politique a changé
data.domains[].domainstringNom d’hôte du domaine
data.domains[].domain_idstringID du domaine
data.domains[].addedstring[]Hôtes ajoutés à la politique recommandée
data.domains[].removedstring[]Hôtes retirés de la politique recommandée
data.domains[].flaggedstring[]Hôtes retirés qui figurent sur la liste de menaces gérée par cside
data.domains[].host_countnumberNombre d’hôtes dans la politique recommandée
data.domains[].policy_headerstringLa valeur complète de l’en-tête Content-Security-Policy recommandé

vulnerable_script_digest

Envoyé par Vulnerable Script Detected en planification quotidienne ou hebdomadaire avec les vulnérabilités courantes (hors haut risque) détectées sur la période. Rien n’est envoyé s’il n’y a eu aucune détection.

ChampTypeDescription
event"vulnerable_script_digest"L’ID de l’événement
timestampstringDate d’envoi du webhook
organization_idstringID de l’organisation
team_idstringID de l’équipe
data.period"daily" | "weekly"La planification de la règle
data.start_datestringDébut de la période du digest
data.end_datestringFin de la période du digest
data.domainsobject[]Compteurs par domaine
data.domains[].domain_idstringID du domaine
data.domains[].domainstringNom d’hôte du domaine
data.domains[].vulnerability_countnumberDétections enregistrées pour le domaine
data.top_vulnerabilitiesobject[]Les détections les plus fréquentes
data.top_vulnerabilities[].vulnerability_idstringID de la vulnérabilité (CVE ou GHSA)
data.top_vulnerabilities[].packagestringPackage affecté
data.top_vulnerabilities[].severitystringSévérité rapportée
data.top_vulnerabilities[].occurrence_countnumberNombre d’occurrences de la détection
data.total_vulnerabilitiesnumberTotal des détections sur tous les domaines concernés

outbound_connections_pending_review

Envoyé par Outbound Connections Pending Review avec le nombre de noms d’hôte sortants que votre équipe n’a pas encore approuvés dans l’inventaire des ressources. Rien n’est envoyé si rien n’est en attente.

ChampTypeDescription
event"outbound_connections_pending_review"L’ID de l’événement
timestampstringDate d’envoi du webhook
organization_idstringID de l’organisation
team_idstringID de l’équipe
data.period"daily" | "weekly"La planification de la règle
data.pending_hostname_countnumberNoms d’hôte sortants distincts en attente de revue
data.domain_countnumberNombre de domaines concernés sur lesquels ils ont été observés

Vérification HMAC

Chaque requête webhook inclut un en-tête x-cside-signature contenant un hash HMAC SHA256 du corps de la requête, signé avec le secret que vous avez fourni lors de la création de la destination webhook.

Vous devriez toujours vérifier cette signature pour confirmer que la requête provient de cside et prévenir les attaques par rejeu.

Comment vérifier la signature

Si vous utilisez JavaScript, utilisez notre package JavaScript pour une vérification intégrée.

Pour les autres langages, calculez le hash HMAC SHA256 du corps brut de la requête en utilisant votre secret webhook, puis comparez-le à la valeur de l’en-tête x-cside-signature.

async function verifyHmac(body: string, signature: string, secret: string) {
	const encoder = new TextEncoder();
	const encodedBody = encoder.encode(body);

	const key = await crypto.subtle.importKey(
		"raw",
		encoder.encode(secret),
		{ name: "HMAC", hash: "SHA-256" },
		false,
		["sign"]
	);

	const signatureBuffer = await crypto.subtle.sign("HMAC", key, encodedBody);

	const finalSig = Array.from(new Uint8Array(signatureBuffer))
		.map((byte) => byte.toString(16).padStart(2, "0"))
		.join("");

	return signature.toLowerCase() === finalSig;
}
fn verify_hmac(body: &str, signature: &str, secret: &str) -> Result<(), &'static str> {
      let mut mac = HmacSha256::new_from_slice(secret.as_bytes()).map_err(|_| "Invalid secret")?;
      mac.update(body.as_bytes());
      let result = mac.finalize();
      let code_bytes = result.into_bytes();
      let final_sig = hex::encode(code_bytes);

      if signature.to_lowercase() == final_sig {
          Ok(())
      } else {
          Err("Invalid signature")
      }
  }

  // Add to Cargo.toml:
  // [dependencies]
  // hmac = "0.12"
  // sha2 = "0.10"
  // hex = "0.4"
package main

import (
  "crypto/hmac"
  "crypto/sha256"
  "encoding/hex"
  "strings"
)

func verifyHmac(body, signature, secret string) bool {
	h := hmac.New(sha256.New, []byte(secret))
	h.Write([]byte(body))
	finalSig := hex.EncodeToString(h.Sum(nil))

	return strings.ToLower(signature) == finalSig
}
defmodule HmacVerifier do
	def verify_hmac(body, signature, secret) do
		final_sig =
			:crypto.mac(:hmac, :sha256, secret, body)
			|> Base.encode16(case: :lower)

			String.downcase(signature) == final_sig
	end
end
import hmac
import hashlib
import secrets

def verify_hmac(body: str, signature: str, secret: str) -> bool:
	final_sig = hmac.new(
		secret.encode('utf-8'),
		body.encode('utf-8'),
		hashlib.sha256
	).hexdigest()

	return signature.lower() == final_sig
Was this page helpful?