Documentación
Monitorización y alertas de cambios
AppSafe compara cada escaneo de un sitio verificado con el anterior y te dice qué cambió. Con los escaneos programados o el reescaneo al desplegar, eso se convierte en una monitorización continua con alertas por email, Slack, Discord o un webhook firmado.
Las alertas de cambios están incluidas en los planes Pro, Agency y Team. Los escaneos programados se ejecutan a diario o cada semana en Pro y Agency, y cada hora en Team.
Qué compara AppSafe
- Hallazgos nuevos y resueltos.
- Encabezados de seguridad añadidos, eliminados o debilitados (por ejemplo, un Content-Security-Policy eliminado o un max-age de HSTS más corto).
- Reemplazos del certificado TLS y avisos de caducidad 14 y 3 días antes de que caduque el certificado.
- Nuevos dominios de scripts de terceros y scripts externos sin Subresource Integrity.
- Nuevos subdominios de tu dominio en los registros de Certificate Transparency (crt.sh) y subdominios cuyo CNAME apunta a un recurso eliminado en un servicio de hosting. Solo se usan consultas DNS pasivas.
- Cambios en los registros SPF, DMARC, CAA y MX.
Las alertas se envían en los escaneos programados y al desplegar, una vez por problema: un problema que sigue igual no volverá a generar una alerta en el siguiente escaneo. Si desaparece y vuelve, recibirás una nueva alerta.
Configura las alertas
Abre un sitio verificado en tu panel y añade canales en Monitorización y alertas. Cada canal tiene una severidad mínima y un botón Enviar alerta de prueba.
- Email: Cualquier dirección de email. Las alertas se escriben en el idioma de tu cuenta.
- Slack: Una URL de webhook entrante de una app de Slack (https://hooks.slack.com/services/…).
- Discord: Una URL de webhook del canal (Ajustes del servidor → Integraciones → Webhooks).
- Webhook: Cualquier endpoint https. AppSafe envía por POST el payload JSON de abajo, firmado con un secreto que se muestra una sola vez al crear el canal.
Payload del webhook
Los webhooks genéricos reciben este JSON (Content-Type: application/json). Las alertas de prueba usan el evento monitoring.test y tienen scan con valor null.
{
"event": "monitoring.changes",
"deliveryId": "5f2c9a0e41b7d3a8c6e1f0b2",
"createdAt": "2026-10-26T12:00:00.000Z",
"site": {
"domain": "myapp.com"
},
"scan": {
"id": "3f1c2b9e-1111-4222-8333-944455556666",
"grade": "C",
"score": 67,
"reportUrl": "https://appsafe.dev/report/…"
},
"summary": "2 changes on myapp.com",
"changes": [
{
"type": "header_removed",
"severity": "high",
"subject": "content-security-policy",
"message": "content-security-policy header removed"
},
{
"type": "finding_new",
"severity": "critical",
"subject": "baas-supabase-table-readable",
"title": "Supabase tables readable without logging in",
"message": "New issue: Supabase tables readable without logging in"
}
]
}Tipos de cambio: finding_new, finding_resolved, header_added, header_removed, header_weakened, header_changed, tls_changed, tls_expiring (con days), script_domain_new, sri_missing, subdomain_new, subdomain_dangling, dns_changed. Severidades: low, medium, high, critical. Las alertas solo incluyen regresiones, así que finding_resolved y header_added aparecen en el panel y en el informe, pero no generan alertas por sí solos.
Verifica la firma
Cada solicitud de webhook lleva un encabezado X-AppSafe-Signature. Calcula un HMAC-SHA256 con el secreto de tu canal sobre el timestamp, un punto y el cuerpo sin procesar de la solicitud, y compáralo con v1 en tiempo constante. Rechaza las solicitudes cuyo timestamp tenga más de 5 minutos.
X-AppSafe-Signature: t=<unix seconds>,v1=<hex HMAC-SHA256 of "<t>.<raw body>">
X-AppSafe-Event: monitoring.changes | monitoring.testimport { createHmac, timingSafeEqual } from "node:crypto";
// header: the X-AppSafe-Signature value, e.g. "t=1761480000,v1=5d41…"
// body: the raw request body (string), before JSON.parse
export function verifyAppSafeSignature(secret, header, body) {
const parts = Object.fromEntries(header.split(",").map((p) => p.split("=")));
const timestamp = Number(parts.t);
if (!parts.v1 || Math.abs(Date.now() / 1000 - timestamp) > 300) return false;
const expected = createHmac("sha256", secret).update(`${timestamp}.${body}`).digest();
const given = Buffer.from(parts.v1, "hex");
return given.length === expected.length && timingSafeEqual(given, expected);
}AppSafe envía cada alerta una sola vez y no reintenta. Responde con un estado 2xx en menos de 8 segundos. El panel muestra el último error de envío del canal.
Lo que las alertas nunca contienen
Los payloads de las alertas contienen tipos de cambio, severidades, títulos de hallazgos, nombres de encabezados, hostnames de scripts y subdominios, y un enlace al informe. Nunca contienen detalles de los hallazgos, secretos (ni siquiera ocultos) ni datos personales.