Skip to main content
Cómo funcionan los webhooks
Language

Webhooks

Configura destinos de webhook para recibir alertas de seguridad de cside mediante solicitudes HTTP POST con verificación de firma HMAC.

Cómo funcionan los webhooks

Los webhooks te permiten recibir notificaciones de cside como solicitudes HTTP POST a una URL que especifiques. Cuando se activa un disparador, cside envía un payload JSON a tu endpoint de webhook con detalles sobre el evento.

Puedes configurar webhooks en tres formatos:

Agregar un destino de webhook

Para agregar un webhook como destino en una configuración de notificación:

  1. Abre el panel de control y navega a Team Settings > Notifications
  2. Crea una nueva configuración de notificación o edita una existente
  3. En Send To, haz clic en Add destination y selecciona Webhook
  4. En el panel de Configuration, completa:
    • URL: la URL donde quieres recibir las notificaciones de webhook (por ejemplo, https://example.com/cside/notify)
    • Secret: una clave secreta usada para firmar las solicitudes de webhook (usada para la verificación HMAC)
    • Format: elige JSON, Slack o Discord
  5. Haz clic en Save o Save & Test

El payload del webhook

cside envía una solicitud HTTP POST por evento, con Content-Type: application/json y un encabezado x-cside-signature (consulta la verificación HMAC). Cada payload incluye un campo event de nivel superior que identifica el tipo de evento, una marca de tiempo, el equipo o dominio al que pertenece el evento y un objeto data específico del evento.

El campo que contiene la marca de tiempo varía según el evento: alert.created y los eventos de resumen usan timestamp, mientras que security_headers.changed usa sent_at. Lee primero event y después analiza el resto del payload según las tablas siguientes. Trata los campos desconocidos como opcionales para que los campos nuevos no rompan tu integración.

IDs de eventos

IDEnviado porCampo de marca de tiempoObjeto data
alert.createdDisparadores inmediatos, y el disparador Vulnerable Script Detected en su programación inmediatatimestampObjeto de alerta
security_headers.changedSecurity Headers Changedsent_atCambio de cabeceras de seguridad
pci_report.availablePCI Report (diario o semanal)timestampInforme PCI
csp_block_digestScript Blocked by CSP (diario o semanal)timestampResumen de bloqueos CSP
csp_policy_digestCSP Policy Digest (diario o semanal)timestampResumen de política CSP
vulnerable_script_digestVulnerable Script Detected (diario o semanal)timestampResumen de vulnerabilidades
outbound_connections_pending_reviewOutbound Connections Pending Review (diario o semanal)timestampConexiones salientes pendientes

Solo alert.created y security_headers.changed respetan los formatos Slack y Discord. Los eventos de resumen se envían siempre como JSON, sea cual sea el formato configurado en el destino.

Save & Test envía un payload alert.created de ejemplo con el trigger_type del disparador de la regla, incluso para los disparadores que normalmente entregan un resumen.

Todas las marcas de tiempo son cadenas ISO 8601 en UTC. Los IDs son cadenas decimales.

alert.created

Se envía por cada alerta que genera un disparador inmediato: Script Threat Detected, High-risk Vulnerability, Managed Flagged, Malicious Tag Manager, Malicious Outbound Connection y Vulnerable Script Detected cuando la regla está configurada para entregar de inmediato.

CampoTipoDescripción
event"alert.created"El ID del evento
timestampstringCuándo se envió el webhook
domain_idstringID del dominio en el que se produjo la alerta
domainstringHostname del dominio en el que se produjo la alerta
trigger_typestringEl disparador que produjo la alerta, consulta la tabla siguiente
dataobjectEl objeto de alerta
destination_idstringID del destino de webhook que recibió el evento

Valores de trigger_type:

trigger_typeDisparador
script_threat_detected_by_rules_engineScript Threat Detected
high_risk_vulnerability_detectedHigh-risk Vulnerability
vulnerable_script_detectedVulnerable Script Detected (programación inmediata)
cside_managed_list_flaggedManaged Flagged
malicious_tag_managerMalicious Tag Manager
malicious_outbound_connectionMalicious Outbound Connection

Objeto de alerta

CampoTipoDescripción
target_type"URL" | "HASH" | "IP" | "DOMAIN" | "HOSTNAME" | "VENDOR"Qué contiene target
targetstringEl objeto de la alerta: la URL de un script, el hash de un script, o la IP, el dominio o el hostname marcados
action"alert"La acción registrada para la alerta. Actualmente siempre es "alert"; trátala como una cadena para seguir siendo compatible con acciones futuras
ruleset_idstringID del ruleset que coincidió
ruleset_namestringNombre del ruleset que coincidió
ruleset_descriptionstringDescripción del ruleset que coincidió
script_urlsstring[]Las URL de los scripts que activaron la alerta; puede estar vacío
vuln_cve_idstring | nullID de la vulnerabilidad (CVE o GHSA); null en alertas que no son de vulnerabilidad
vuln_packagestring | nullPaquete afectado; null en alertas que no son de vulnerabilidad
vuln_severitystring | nullSeveridad reportada, como HIGH; null en alertas que no son de vulnerabilidad
vuln_reference_urlstring | nullEnlace a la vulnerabilidad en osv.dev; null cuando no hay vuln_cve_id

Los cuatro campos vuln_* solo se rellenan para vulnerable_script_detected y high_risk_vulnerability_detected. Para cualquier otro trigger_type son 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

Lo envía Security Headers Changed cuando las cabeceras de seguridad observadas en las páginas de checkout de un dominio difieren entre dos ventanas de observación consecutivas.

CampoTipoDescripción
event"security_headers.changed"El ID del evento
sent_atstringCuándo se envió el webhook
team_idstringID del equipo propietario del dominio
data.domain_idstringID del dominio
data.domainstringHostname del dominio
data.checkout_pathsstring[]Los selectores de páginas de checkout que se observaron
data.changesobject[]Una entrada por cada cabecera que cambió
data.changes[].header_namestringLa cabecera, por ejemplo content-security-policy
data.changes[].from_valuestringEl valor anterior; vacío cuando la cabecera se añadió
data.changes[].to_valuestringEl valor nuevo; vacío cuando la cabecera desapareció
data.changes[].from_datestringInicio de la ventana de observación en la que se vio el valor anterior
data.changes[].to_datestringInicio de la ventana de observación en la que se vio el valor nuevo

pci_report.available

Lo envía PCI Report cuando se ha puesto en cola una ejecución diaria o semanal del informe para los dominios en alcance que tienen rutas de checkout configuradas.

CampoTipoDescripción
event"pci_report.available"El ID del evento
timestampstringCuándo se envió el webhook
organization_idstringID de la organización
team_idstringID del equipo
data.domain_idsstring[]IDs de los dominios que cubre el informe
data.domainsstring[]Hostnames de los dominios que cubre el informe, en el mismo orden
data.starting_datestringInicio del periodo del informe
data.ending_datestringFin del periodo del informe
data.period"daily" | "weekly"La programación de la regla

csp_block_digest

Lo envía Script Blocked by CSP con los informes de violación de Content Security Policy recopilados en el periodo. No se envía nada si no hubo violaciones.

CampoTipoDescripción
event"csp_block_digest"El ID del evento
timestampstringCuándo se envió el webhook
organization_idstringID de la organización
team_idstringID del equipo
data.period"daily" | "weekly"La programación de la regla
data.start_datestringInicio del periodo del resumen
data.end_datestringFin del periodo del resumen
data.domainsobject[]Recuentos por dominio, ordenados por número de violaciones
data.domains[].domain_idstringID del dominio
data.domains[].domainstringHostname del dominio
data.domains[].violation_countnumberViolaciones registradas para el dominio
data.top_blocked_urisstring[]Las URI bloqueadas con más frecuencia
data.top_violated_directivesstring[]Las directivas violadas con más frecuencia
data.total_violationsnumberTotal de violaciones en todos los dominios en alcance

csp_policy_digest

Lo envía CSP Policy Digest por cada dominio en alcance cuya Content Security Policy recomendada cambió en el periodo. No se envía nada si ningún dominio cambió.

CampoTipoDescripción
event"csp_policy_digest"El ID del evento
timestampstringCuándo se envió el webhook
organization_idstringID de la organización
team_idstringID del equipo
data.period"daily" | "weekly"La programación de la regla
data.domainsobject[]Una entrada por cada dominio cuya política cambió
data.domains[].domainstringHostname del dominio
data.domains[].domain_idstringID del dominio
data.domains[].addedstring[]Hosts añadidos a la política recomendada
data.domains[].removedstring[]Hosts eliminados de la política recomendada
data.domains[].flaggedstring[]Hosts eliminados que están en la lista de amenazas gestionada por cside
data.domains[].host_countnumberNúmero de hosts en la política recomendada
data.domains[].policy_headerstringEl valor completo recomendado de la cabecera Content-Security-Policy

vulnerable_script_digest

Lo envía Vulnerable Script Detected en una programación diaria o semanal con los hallazgos de vulnerabilidades rutinarias (no de alto riesgo) del periodo. No se envía nada si no hubo hallazgos.

CampoTipoDescripción
event"vulnerable_script_digest"El ID del evento
timestampstringCuándo se envió el webhook
organization_idstringID de la organización
team_idstringID del equipo
data.period"daily" | "weekly"La programación de la regla
data.start_datestringInicio del periodo del resumen
data.end_datestringFin del periodo del resumen
data.domainsobject[]Recuentos por dominio
data.domains[].domain_idstringID del dominio
data.domains[].domainstringHostname del dominio
data.domains[].vulnerability_countnumberHallazgos registrados para el dominio
data.top_vulnerabilitiesobject[]Los hallazgos más frecuentes
data.top_vulnerabilities[].vulnerability_idstringID de la vulnerabilidad (CVE o GHSA)
data.top_vulnerabilities[].packagestringPaquete afectado
data.top_vulnerabilities[].severitystringSeveridad reportada
data.top_vulnerabilities[].occurrence_countnumberCuántas veces se produjo el hallazgo
data.total_vulnerabilitiesnumberTotal de hallazgos en todos los dominios en alcance

outbound_connections_pending_review

Lo envía Outbound Connections Pending Review con el número de hostnames salientes que tu equipo todavía no ha aprobado en Resources inventory. No se envía nada si no hay nada pendiente.

CampoTipoDescripción
event"outbound_connections_pending_review"El ID del evento
timestampstringCuándo se envió el webhook
organization_idstringID de la organización
team_idstringID del equipo
data.period"daily" | "weekly"La programación de la regla
data.pending_hostname_countnumberHostnames salientes distintos pendientes de revisión
data.domain_countnumberNúmero de dominios en alcance en los que se observaron

Verificación HMAC

Cada solicitud de webhook incluye un encabezado x-cside-signature que contiene un hash HMAC SHA256 del cuerpo de la solicitud, firmado con el secret que proporcionaste al crear el destino de webhook.

Siempre debes verificar esta firma para confirmar que la solicitud proviene de cside y prevenir ataques de repetición.

Cómo verificar la firma

Si estás usando JavaScript, usa nuestro paquete JavaScript para verificación integrada.

Para otros lenguajes, calcula el hash HMAC SHA256 del cuerpo crudo de la solicitud usando tu secret de webhook, y luego compáralo con el valor del encabezado 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?