La seguridad de los webhooks es fundamental para garantizar que las solicitudes a tu punto de conexión realmente provengan de Firecrawl y no hayan sido manipuladas. Esta página explica cómo verificar la autenticidad de los webhooks e implementar prácticas de seguridad recomendadas.
Por qué es importante la seguridad de los webhooks
Sin una verificación adecuada, los atacantes podrían:
- Enviar solicitudes de webhook falsas para activar acciones no deseadas
- Modificar la carga útil para manipular tu aplicación
- Sobrecargar tu endpoint de webhook con solicitudes
Cómo Firecrawl firma los webhooks
Firecrawl firma cada solicitud de webhook usando HMAC-SHA256 con la clave secreta de tu cuenta. Esto genera una firma única para cada solicitud que garantiza:
- Que la solicitud proviene de Firecrawl
- Que el contenido no ha sido modificado
Encontrar tu clave secreta
Tu secreto de webhook está disponible en la pestaña Advanced de la configuración de tu cuenta. Cada cuenta tiene un secreto único que se utiliza para firmar todas las solicitudes de webhook.
Mantén tu secreto de webhook seguro y nunca lo expongas públicamente. Si crees
que tu secreto se ha visto comprometido, regénéralo de inmediato desde la
configuración de tu cuenta.
Cómo funcionan las firmas
Cada solicitud de webhook incluye un encabezado X-Firecrawl-Signature
con el siguiente formato:
X-Firecrawl-Signature: sha256=abc123def456...
La firma se calcula de la siguiente manera:
- Toma el cuerpo sin procesar de la solicitud (cadena JSON)
- Crea un hash HMAC-SHA256 con tu clave secreta
- Convierte el resultado a una cadena hexadecimal
- Anteponle
sha256=
Ejemplos de implementación
import crypto from 'crypto';
import express from 'express';
const app = express();
// Usa el analizador de cuerpo en bruto para verificar la firma
app.use('/webhook/firecrawl', express.raw({ type: 'application/json' }));
app.post('/webhook/firecrawl', (req, res) => {
const signature = req.get('X-Firecrawl-Signature');
const webhookSecret = process.env.FIRECRAWL_WEBHOOK_SECRET;
if (!signature || !webhookSecret) {
return res.status(401).send('No autorizado');
}
// Extrae el hash del encabezado de la firma
const [algorithm, hash] = signature.split('=');
if (algorithm !== 'sha256') {
return res.status(401).send('Algoritmo de firma no válido');
}
// Calcula la firma esperada
const expectedSignature = crypto
.createHmac('sha256', webhookSecret)
.update(req.body)
.digest('hex');
// Verifica la firma usando una comparación segura contra ataques de temporización
if (!crypto.timingSafeEqual(Buffer.from(hash, 'hex'), Buffer.from(expectedSignature, 'hex'))) {
return res.status(401).send('Firma no válida');
}
// Analiza y procesa el webhook verificado
const event = JSON.parse(req.body);
console.log('Webhook de Firecrawl verificado:', event);
res.status(200).send('ok');
});
app.listen(3000, () => console.log('Escuchando en el puerto 3000'));
- Extrae la firma del encabezado
X-Firecrawl-Signature
- Obtén el cuerpo sin procesar de la solicitud tal como se recibió (no lo analices primero)
- Calcula el HMAC-SHA256 usando tu clave secreta y el cuerpo sin procesar
- Compara las firmas usando una función de comparación segura frente al tiempo
- Procesa el webhook solo si las firmas coinciden
Prácticas recomendadas de seguridad
Valida siempre las firmas
Nunca confíes en una solicitud de webhook sin verificar la firma:
// ❌ MALO: sin verificación
app.post('/webhook', (req, res) => {
processWebhook(req.body); // ¡Peligroso!
res.status(200).send('OK');
});
// ✅ BUENO: verificado primero
app.post('/webhook', (req, res) => {
if (!verifySignature(req)) {
return res.status(401).send('No autorizado');
}
processWebhook(req.body);
res.status(200).send('OK');
});
Usa comparaciones seguras en tiempo constante
Las comparaciones de cadenas estándar pueden filtrar información por temporización. Usa funciones específicas:
- Node.js:
crypto.timingSafeEqual()
- Python:
hmac.compare_digest()
- Otros lenguajes: Busca funciones de comparación en “tiempo constante” o “seguras en el tiempo”
Usa siempre puntos de conexión HTTPS para los webhooks:
{
"url": "https://your-app.com/webhook" // ✅ Seguro
}
{
"url": "http://your-app.com/webhook" // ❌ No seguro
}