Webhooks
Stel webhook-bestemmingen in om cside-beveiligingswaarschuwingen te ontvangen via HTTP POST-verzoeken met HMAC-handtekeningverificatie.
Hoe webhooks werken
Met webhooks kunt u cside-notificaties ontvangen als HTTP POST-verzoeken naar een URL die u opgeeft. Wanneer een trigger wordt geactiveerd, stuurt cside een JSON-payload naar uw webhook-endpoint met details over de gebeurtenis.
U kunt webhooks in drie formaten configureren:
- JSON - standaard JSON-payload voor aangepaste integraties
- Slack - voorgeformatteerd voor Slack-kanalen
- Discord - voorgeformatteerd voor Discord-kanalen
Zie Jira Automation om op elk plan Jira-werkitems aan te maken vanuit webhooks.
Een webhook-bestemming toevoegen
Om een webhook als bestemming toe te voegen aan een notificatieconfiguratie:
- Open het dashboard en navigeer naar Team Settings > Notifications
- Maak een nieuwe notificatieconfiguratie aan of bewerk een bestaande
- Klik onder Send To op Add destination en selecteer Webhook
- Vul in het configuratiepaneel het volgende in:
- URL: de URL waar u webhook-notificaties wilt ontvangen (bijv.
https://example.com/cside/notify) - Secret: een geheime sleutel die wordt gebruikt om webhook-verzoeken te ondertekenen (gebruikt voor HMAC-verificatie)
- Format: kies JSON, Slack of Discord
- URL: de URL waar u webhook-notificaties wilt ontvangen (bijv.
- Klik op Save of Save & Test
cside ondertekent elk verzoek met de x-cside-signature-header, maar kan geen aangepaste headers toevoegen. Als uw ontvanger authenticeert met een eigen header, controleer dan of deze het token ook in de URL accepteert, zoals Jira Automation doet.
De webhook-payload
cside stuurt één HTTP POST per gebeurtenis, met Content-Type: application/json
en een x-cside-signature-header (zie HMAC-verificatie).
Elke payload bevat een event-veld op het hoogste niveau dat het type
gebeurtenis aanduidt, een tijdstempel, het team of domein waar de gebeurtenis
bij hoort en een gebeurtenisspecifiek data-object.
Het veld dat de tijdstempel bevat verschilt per gebeurtenis: alert.created en
de samenvattingsgebeurtenissen gebruiken timestamp, terwijl
security_headers.changed sent_at gebruikt. Lees eerst event en verwerk
daarna de rest van de payload volgens de tabellen hieronder. Behandel onbekende
velden als optioneel, zodat nieuwe velden uw integratie niet breken.
Elke JSON-payload bevat ook twee voor mensen leesbare velden, summary (een
titel van één regel) en description (details over meerdere regels), zowel op
het hoogste niveau als herhaald binnen data. Gebruik ze om een ticket of
chatbericht in te vullen zonder per gebeurtenis uw eigen tekst op te bouwen. Ze
worden binnen data herhaald omdat
Jira Automation een
data-sleutel op het hoogste niveau speciaal behandelt.
Event-ID’s
| ID | Verstuurd door | Tijdstempelveld | data-object |
|---|---|---|---|
alert.created | Directe triggers, en de trigger Vulnerable Script Detected met het directe schema | timestamp | Alert-object |
security_headers.changed | Security Headers Changed | sent_at | Wijziging van security headers |
pci_report.available | PCI Report (dagelijks of wekelijks) | timestamp | PCI-rapport |
csp_block_digest | Script Blocked by CSP (dagelijks of wekelijks) | timestamp | Samenvatting van CSP-blokkeringen |
csp_policy_digest | CSP Policy Digest (dagelijks of wekelijks) | timestamp | Samenvatting van CSP-beleid |
vulnerable_script_digest | Vulnerable Script Detected (dagelijks of wekelijks) | timestamp | Samenvatting van kwetsbaarheden |
outbound_connections_pending_review | Outbound Connections Pending Review (dagelijks of wekelijks) | timestamp | Openstaande uitgaande verbindingen |
Alleen alert.created, security_headers.changed en pci_report.available
respecteren de formaten Slack en Discord. De andere
samenvattingsgebeurtenissen worden altijd als JSON verstuurd, ongeacht het
formaat dat voor de bestemming is ingesteld.
Save & Test stuurt een voorbeeldpayload van alert.created met het
trigger_type van de trigger van de regel, ook voor triggers die normaal een
samenvatting afleveren. De summary daarvan begint met [Test].
Security Headers Changed stuurt in plaats daarvan een voorbeeldpayload van
security_headers.changed. Als uw endpoint met een fout antwoordt, toont het
testresultaat de HTTP-statuscode die het teruggaf.
Alle tijdstempels zijn ISO 8601-tekenreeksen in UTC. ID’s zijn decimale tekenreeksen.
alert.created
Wordt verstuurd voor elke waarschuwing die een directe trigger genereert: Script Threat Detected, High-risk Vulnerability, Managed Flagged, Malicious Tag Manager, Malicious Outbound Connection en Vulnerable Script Detected wanneer de regel is ingesteld op directe aflevering.
| Veld | Type | Beschrijving |
|---|---|---|
event | "alert.created" | Het event-ID |
timestamp | string | Wanneer de webhook is verstuurd |
domain_id | string | ID van het domein waarop de waarschuwing is geactiveerd |
domain | string | Hostname van het domein waarop de waarschuwing is geactiveerd |
trigger_type | string | De trigger die de waarschuwing heeft opgeleverd, zie de tabel hieronder |
summary | string | Titel van één regel, bijvoorbeeld [cside] Malicious Script: {ruleset} on {domain}. Dezelfde tekst die de Jira-integratie als summary van de issue gebruikt |
description | string | Uitleg van de waarschuwing in platte tekst |
data | object | Het alert-object |
destination_id | string | ID van de webhook-bestemming die de gebeurtenis heeft ontvangen |
Waarden van trigger_type:
trigger_type | Trigger |
|---|---|
script_threat_detected_by_rules_engine | Script Threat Detected |
high_risk_vulnerability_detected | High-risk Vulnerability |
vulnerable_script_detected | Vulnerable Script Detected (direct schema) |
cside_managed_list_flagged | Managed Flagged |
malicious_tag_manager | Malicious Tag Manager |
malicious_outbound_connection | Malicious Outbound Connection |
Alert-object
| Veld | Type | Beschrijving |
|---|---|---|
summary | string | Gelijk aan summary op het hoogste niveau |
description | string | Gelijk aan description op het hoogste niveau |
domain | string | Gelijk aan domain op het hoogste niveau |
trigger_type | string | Gelijk aan trigger_type op het hoogste niveau |
target_type | "URL" | "HASH" | "IP" | "DOMAIN" | "HOSTNAME" | "VENDOR" | Wat target bevat |
target | string | Het onderwerp van de waarschuwing: een script-URL, een scripthash, of het gemarkeerde IP-adres, domein of de gemarkeerde hostname |
action | "alert" | De actie die voor de waarschuwing is vastgelegd. Momenteel altijd "alert"; behandel het als een tekenreeks om compatibel te blijven met toekomstige acties |
ruleset_id | string | ID van de ruleset die overeenkwam |
ruleset_name | string | Naam van de ruleset die overeenkwam |
ruleset_description | string | Beschrijving van de ruleset die overeenkwam |
script_urls | string[] | De script-URL’s die de waarschuwing hebben geactiveerd; kan leeg zijn |
vuln_cve_id | string | null | Kwetsbaarheids-ID (CVE of GHSA); null bij waarschuwingen die geen kwetsbaarheid betreffen |
vuln_package | string | null | Getroffen package; null bij waarschuwingen die geen kwetsbaarheid betreffen |
vuln_severity | string | null | Gerapporteerde ernst, zoals HIGH; null bij waarschuwingen die geen kwetsbaarheid betreffen |
vuln_reference_url | string | null | Link naar de kwetsbaarheid op osv.dev; null als er geen vuln_cve_id is |
De vier vuln_*-velden worden alleen gevuld voor vulnerable_script_detected
en high_risk_vulnerability_detected. Voor elk ander trigger_type zijn ze
null.
{
"event": "alert.created",
"timestamp": "2026-09-17T10:15:30.123456Z",
"domain_id": "1897425263682514945",
"domain": "checkout.example.com",
"trigger_type": "high_risk_vulnerability_detected",
"summary": "[cside] High-risk Vulnerability: CVE-2024-1234 on checkout.example.com",
"description": "cside detected a high-risk vulnerability on checkout.example.com: lodash. Severity: High.\n\nThis is a high-risk finding (malware or a high-severity vulnerability). Investigate it promptly: if it is malware, remove the script immediately and rotate any credentials it may have accessed; otherwise prioritize updating or removing the affected dependency.",
"data": {
"summary": "[cside] High-risk Vulnerability: CVE-2024-1234 on checkout.example.com",
"description": "cside detected a high-risk vulnerability on checkout.example.com: lodash. Severity: High.\n\nThis is a high-risk finding (malware or a high-severity vulnerability). Investigate it promptly: if it is malware, remove the script immediately and rotate any credentials it may have accessed; otherwise prioritize updating or removing the affected dependency.",
"domain": "checkout.example.com",
"trigger_type": "high_risk_vulnerability_detected",
"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
Wordt verstuurd door Security Headers Changed wanneer de security headers die op de checkout-pagina’s van een domein worden waargenomen verschillen tussen twee opeenvolgende observatievensters.
| Veld | Type | Beschrijving |
|---|---|---|
event | "security_headers.changed" | Het event-ID |
sent_at | string | Wanneer de webhook is verstuurd |
team_id | string | ID van het team dat eigenaar is van het domein |
summary | string | [cside] Security headers changed on {domain} |
description | string | Eén regel per gewijzigde header: - {header}: {from} -> {to} |
data.summary | string | Gelijk aan summary |
data.description | string | Gelijk aan description |
data.domain_id | string | ID van het domein |
data.domain | string | Hostname van het domein |
data.checkout_paths | string[] | De checkout-pagina-selectors die zijn waargenomen |
data.changes | object[] | Eén item per gewijzigde header |
data.changes[].header_name | string | De header, bijvoorbeeld content-security-policy |
data.changes[].from_value | string | De vorige waarde; leeg als de header is toegevoegd |
data.changes[].to_value | string | De nieuwe waarde; leeg als de header is verdwenen |
data.changes[].from_date | string | Begin van het observatievenster waarin de vorige waarde is gezien |
data.changes[].to_date | string | Begin van het observatievenster waarin de nieuwe waarde is gezien |
pci_report.available
Wordt verstuurd door PCI Report zodra het rapport voor de periode klaar is,
voor de domeinen binnen bereik waarvoor checkout-paden zijn ingesteld. Het
wordt één keer per rapport verstuurd; een geweigerde aflevering wordt niet
opnieuw geprobeerd. Elk verzoek bevat ook een x-cside-delivery-id-header die
gelijk blijft als hetzelfde rapport opnieuw wordt afgeleverd, zodat u daarop
kunt dedupliceren.
| Veld | Type | Beschrijving |
|---|---|---|
event | "pci_report.available" | Het event-ID |
timestamp | string | Wanneer de webhook is verstuurd |
organization_id | string | ID van de organisatie |
team_id | string | ID van het team |
summary | string | [cside / PCI DSS]: New PCI Compliance Report for {domains} (de eerste drie domeinen, gevolgd door “and N more”) |
description | string | Een zin met de domeinen en datums, gevolgd door de link naar het rapport |
data.summary | string | Gelijk aan summary |
data.description | string | Gelijk aan description |
data.report_id | string | ID van het rapport |
data.report_url | string | Link om het rapport te downloaden vanuit het cside-dashboard |
data.domain_ids | string[] | ID’s van de domeinen die het rapport beslaat |
data.domains | string[] | Hostnames van de domeinen die het rapport beslaat, in dezelfde volgorde |
data.starting_date | string | Begin van de rapportageperiode |
data.ending_date | string | Einde van de rapportageperiode |
data.period | "daily" | "weekly" | "custom" | De lengte van de rapportageperiode. custom wordt gebruikt voor een rapport dat een ander bereik beslaat |
csp_block_digest
Wordt verstuurd door Script Blocked by CSP met de Content Security Policy-overtredingsrapporten die in de periode zijn verzameld. Er wordt niets verstuurd als er geen overtredingen waren.
| Veld | Type | Beschrijving |
|---|---|---|
event | "csp_block_digest" | Het event-ID |
timestamp | string | Wanneer de webhook is verstuurd |
organization_id | string | ID van de organisatie |
team_id | string | ID van het team |
summary | string | Titel van één regel voor de samenvatting |
description | string | Uitsplitsing van de samenvatting over meerdere regels |
data.summary | string | Gelijk aan summary |
data.description | string | Gelijk aan description |
data.period | "daily" | "weekly" | Het schema van de regel |
data.start_date | string | Begin van de samenvattingsperiode |
data.end_date | string | Einde van de samenvattingsperiode |
data.domains | object[] | Aantallen per domein, gesorteerd op aantal overtredingen |
data.domains[].domain_id | string | ID van het domein |
data.domains[].domain | string | Hostname van het domein |
data.domains[].violation_count | number | Overtredingen die voor het domein zijn vastgelegd |
data.top_blocked_uris | string[] | De meest geblokkeerde URI’s |
data.top_violated_directives | string[] | De meest overtreden directives |
data.total_violations | number | Totaal aantal overtredingen over alle domeinen binnen bereik |
csp_policy_digest
Wordt verstuurd door CSP Policy Digest voor elk domein binnen bereik waarvan het aanbevolen Content Security Policy in de periode is gewijzigd. Er wordt niets verstuurd als er geen domein is gewijzigd.
| Veld | Type | Beschrijving |
|---|---|---|
event | "csp_policy_digest" | Het event-ID |
timestamp | string | Wanneer de webhook is verstuurd |
organization_id | string | ID van de organisatie |
team_id | string | ID van het team |
summary | string | Titel van één regel voor de samenvatting |
description | string | Uitsplitsing van de samenvatting over meerdere regels |
data.summary | string | Gelijk aan summary |
data.description | string | Gelijk aan description |
data.period | "daily" | "weekly" | Het schema van de regel |
data.domains | object[] | Eén item per domein waarvan het beleid is gewijzigd |
data.domains[].domain | string | Hostname van het domein |
data.domains[].domain_id | string | ID van het domein |
data.domains[].added | string[] | Hosts die aan het aanbevolen beleid zijn toegevoegd |
data.domains[].removed | string[] | Hosts die uit het aanbevolen beleid zijn verwijderd |
data.domains[].flagged | string[] | Verwijderde hosts die op de door cside beheerde dreigingslijst staan |
data.domains[].host_count | number | Aantal hosts in het aanbevolen beleid |
data.domains[].policy_header | string | De volledige aanbevolen waarde van de Content-Security-Policy-header |
vulnerable_script_digest
Wordt verstuurd door Vulnerable Script Detected met een dagelijks of wekelijks schema, met de routinematige (niet high-risk) kwetsbaarheidsbevindingen uit de periode. Er wordt niets verstuurd als er geen bevindingen waren.
| Veld | Type | Beschrijving |
|---|---|---|
event | "vulnerable_script_digest" | Het event-ID |
timestamp | string | Wanneer de webhook is verstuurd |
organization_id | string | ID van de organisatie |
team_id | string | ID van het team |
summary | string | Titel van één regel voor de samenvatting |
description | string | Uitsplitsing van de samenvatting over meerdere regels |
data.summary | string | Gelijk aan summary |
data.description | string | Gelijk aan description |
data.period | "daily" | "weekly" | Het schema van de regel |
data.start_date | string | Begin van de samenvattingsperiode |
data.end_date | string | Einde van de samenvattingsperiode |
data.domains | object[] | Aantallen per domein |
data.domains[].domain_id | string | ID van het domein |
data.domains[].domain | string | Hostname van het domein |
data.domains[].vulnerability_count | number | Bevindingen die voor het domein zijn vastgelegd |
data.top_vulnerabilities | object[] | De meest voorkomende bevindingen |
data.top_vulnerabilities[].vulnerability_id | string | Kwetsbaarheids-ID (CVE of GHSA) |
data.top_vulnerabilities[].package | string | Getroffen package |
data.top_vulnerabilities[].severity | string | Gerapporteerde ernst |
data.top_vulnerabilities[].occurrence_count | number | Hoe vaak de bevinding is voorgekomen |
data.total_vulnerabilities | number | Totaal aantal bevindingen over alle domeinen binnen bereik |
outbound_connections_pending_review
Wordt verstuurd door Outbound Connections Pending Review met het aantal uitgaande hostnames dat uw team nog niet heeft goedgekeurd in Resources inventory. Er wordt niets verstuurd als er niets openstaat.
| Veld | Type | Beschrijving |
|---|---|---|
event | "outbound_connections_pending_review" | Het event-ID |
timestamp | string | Wanneer de webhook is verstuurd |
organization_id | string | ID van de organisatie |
team_id | string | ID van het team |
summary | string | Titel van één regel voor de samenvatting |
description | string | Uitsplitsing van de samenvatting over meerdere regels |
data.summary | string | Gelijk aan summary |
data.description | string | Gelijk aan description |
data.period | "daily" | "weekly" | Het schema van de regel |
data.pending_hostname_count | number | Unieke uitgaande hostnames die op beoordeling wachten |
data.domain_count | number | Aantal domeinen binnen bereik waarop ze zijn waargenomen |
HMAC-verificatie
Elk webhook-verzoek bevat een x-cside-signature-header met een HMAC SHA256-hash van de verzoekbody, ondertekend met het secret dat u heeft opgegeven bij het aanmaken van de webhook-bestemming.
U dient deze handtekening altijd te verifiëren om te bevestigen dat het verzoek van cside afkomstig is en replay-aanvallen te voorkomen.
Hoe de handtekening te verifiëren
Als u JavaScript gebruikt, gebruik dan ons JavaScript-pakket voor ingebouwde verificatie.
Voor andere talen berekent u de HMAC SHA256-hash van de ruwe verzoekbody met uw webhook-secret en vergelijkt u deze met de waarde van de x-cside-signature-header.
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
endimport 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_sigThanks for your feedback!