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:

Para crear elementos de trabajo de Jira a partir de webhooks en cualquier plan, consulta Jira Automation.

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

cside firma cada solicitud con el encabezado x-cside-signature, pero no puede agregar encabezados personalizados. Si tu receptor se autentica con su propio encabezado, comprueba si también acepta el token en la URL, como hace Jira Automation.

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.

Cada payload JSON incluye además dos campos legibles para personas, summary (un título de una línea) y description (detalles en varias líneas), tanto en el nivel superior como repetidos dentro de data. Úsalos para rellenar un ticket o un mensaje de chat sin tener que construir tu propio texto para cada evento. Se repiten dentro de data porque Jira Automation trata de forma especial una clave data de nivel superior.

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, security_headers.changed y pci_report.available respetan los formatos Slack y Discord. Los demás 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. Su summary empieza por [Test]. Security Headers Changed envía en su lugar un payload security_headers.changed de ejemplo. Si tu endpoint responde con un error, el resultado de la prueba muestra el código de estado HTTP que devolvió.

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
summarystringTítulo de una línea, por ejemplo [cside] Malicious Script: {ruleset} on {domain}. Es el mismo texto que la integración de Jira usa como título del issue
descriptionstringExplicación de la alerta en texto plano
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
summarystringIgual que el summary de nivel superior
descriptionstringIgual que el description de nivel superior
domainstringIgual que el domain de nivel superior
trigger_typestringIgual que el trigger_type de nivel superior
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",
  "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

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
summarystring[cside] Security headers changed on {domain}
descriptionstringUna línea por cada cabecera que cambió: - {header}: {from} -> {to}
data.summarystringIgual que summary
data.descriptionstringIgual que description
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 el informe del periodo está listo, para los dominios en alcance que tienen rutas de checkout configuradas. Se envía una vez por informe; una entrega rechazada no se reintenta. Cada solicitud incluye además un encabezado x-cside-delivery-id que no cambia si el mismo informe se entrega de nuevo, así que puedes usarlo para eliminar duplicados.

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
summarystring[cside / PCI DSS]: New PCI Compliance Report for {domains} (los tres primeros dominios y después “and N more”)
descriptionstringUna frase que indica los dominios y las fechas, seguida del enlace al informe
data.summarystringIgual que summary
data.descriptionstringIgual que description
data.report_idstringID del informe
data.report_urlstringEnlace para descargar el informe desde el panel de control de cside
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" | "custom"La duración del periodo del informe. custom se usa para un informe que cubre cualquier otro rango

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
summarystringTítulo de una línea del resumen
descriptionstringDesglose del resumen en varias líneas
data.summarystringIgual que summary
data.descriptionstringIgual que description
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
summarystringTítulo de una línea del resumen
descriptionstringDesglose del resumen en varias líneas
data.summarystringIgual que summary
data.descriptionstringIgual que description
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
summarystringTítulo de una línea del resumen
descriptionstringDesglose del resumen en varias líneas
data.summarystringIgual que summary
data.descriptionstringIgual que description
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
summarystringTítulo de una línea del resumen
descriptionstringDesglose del resumen en varias líneas
data.summarystringIgual que summary
data.descriptionstringIgual que description
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?