Autenticación de webhooks - Protege tus endpoints
finlight ofrece varios métodos de autenticación para proteger tus entregas de webhook. Cada webhook incluye validación de firma de forma predeterminada, con capas de autenticación adicionales opcionales para una mayor seguridad.
finlight admite cuatro métodos de autenticación que pueden usarse de forma individual o combinada:
Ninguno
Sin autenticación adicional más allá de la validación de firma predeterminada.
Cuándo usarlo:
- Entornos de prueba y desarrollo
- Endpoints internos detrás de redes seguras
- Cuando la validación de firma proporciona seguridad suficiente
Nota: La validación de firma sigue incluyéndose en cada solicitud de webhook independientemente de este ajuste.
Encabezado X-Finlight-Key
Envía tu clave de API en un encabezado personalizado X-Finlight-Key con cada solicitud de webhook.
Configuración:
- Proporciona tu clave de API durante la configuración del webhook
- La clave se incluirá en el encabezado
X-Finlight-Key
Implementación: Tu endpoint debe validar el encabezado entrante:
const finlightKey = req.headers['x-finlight-key']
if (finlightKey !== 'your-expected-api-key') {
return res.status(401).send('Invalid API key')
}
Encabezados enviados:
X-Finlight-Key: your-api-key-value
X-Webhook-Signature: sha256=signature
X-Webhook-Timestamp: 2024-01-15T10:30:00.000Z
Autenticación básica
Autenticación básica HTTP con credenciales de usuario/contraseña.
Configuración:
- Establece el usuario y la contraseña durante la configuración del webhook
- Las credenciales se codifican en base64 y se envían en el encabezado
Authorization
Implementación: Tu endpoint recibe la autenticación básica HTTP estándar:
const auth = req.headers.authorization
if (!auth || !auth.startsWith('Basic ')) {
return res.status(401).send('Missing Basic Auth')
}
const credentials = Buffer.from(auth.slice(6), 'base64').toString()
const [username, password] = credentials.split(':')
if (username !== 'expected-user' || password !== 'expected-pass') {
return res.status(401).send('Invalid credentials')
}
Encabezados enviados:
Authorization: Basic dXNlcm5hbWU6cGFzc3dvcmQ=
X-Webhook-Signature: sha256=signature
X-Webhook-Timestamp: 2024-01-15T10:30:00.000Z
Validación de firma
Seguridad automática: Cada solicitud de webhook incluye validación de firma independientemente del método de autenticación que elijas.
Cómo funciona:
- finlight genera una marca de tiempo al enviar el webhook
- Crea un mensaje concatenando:
timestamp + '.' + payload - Firma el mensaje usando HMAC-SHA256 con la clave secreta de tu webhook
- Envía tanto la firma como la marca de tiempo en los encabezados
Encabezados incluidos:
X-Webhook-Signature: sha256=computed-signature
X-Webhook-Timestamp: 2024-01-15T10:30:00.000Z
Algoritmo de firma:
message = timestamp + '.' + raw_request_body
signature = HMAC-SHA256(message, webhook_secret)
Cuando no hay ninguna cabecera X-Webhook-Timestamp, el mensaje es únicamente el cuerpo en bruto. Compara el resultado en tiempo constante y rechaza las entregas cuya marca de tiempo tenga más de cinco minutos de antigüedad.
El cuerpo debe ser los bytes de la solicitud en bruto, sin analizar. Cualquier middleware que decodifique y vuelva a codificar el JSON antes de que lo leas cambia la secuencia de bytes e invalida la firma — esta es la causa más común de fallos de verificación. Lee el cuerpo como una cadena o un búfer en bruto, verifícalo y solo entonces analízalo.
Verificación con un cliente oficial
Cada biblioteca cliente incluye un helper que realiza toda la comprobación por ti: acepta el prefijo sha256= en la firma, compara en tiempo constante, aplica la ventana de repetición de cinco minutos y devuelve el artículo analizado. Lanza una excepción o devuelve un error cuando la verificación falla — responde siempre a eso con un 4xx y nunca proceses el payload.
Verificar un webhook
import express from 'express'
import { WebhookService } from 'finlight-client'
const app = express()
app.post('/webhook', express.raw({ type: 'application/json' }), (req, res) => {
try {
const article = WebhookService.constructEvent(
req.body.toString(),
req.headers['x-webhook-signature'] as string,
process.env.WEBHOOK_SECRET!,
req.headers['x-webhook-timestamp'] as string,
)
console.log('New article:', article.title)
res.sendStatus(200)
} catch (err) {
console.error('Webhook verification failed:', err)
res.sendStatus(400)
}
})
Mejores prácticas de seguridad
Validación de marca de tiempo
Los helpers constructEvent anteriores ya aplican una ventana de repetición de cinco minutos, así que esto solo es necesario si verificas las firmas manualmente:
function isTimestampValid(timestamp, toleranceSeconds = 300) {
const now = Date.now()
const requestTime = new Date(timestamp).getTime()
const difference = Math.abs(now - requestTime) / 1000
return difference <= toleranceSeconds
}
Almacenamiento seguro de credenciales
- Variables de entorno: Almacena todos los secretos en variables de entorno
- Gestión de secretos: Usa AWS Secrets Manager, HashiCorp Vault o similar
- Nunca los incrustes en el código: Nunca confirmes secretos al control de versiones
- Rotación regular: Actualiza los secretos de webhook periódicamente
Para orientación sobre la configuración de webhooks, consulta la documentación principal de webhooks. Para pruebas exhaustivas, consulta la guía de pruebas de webhooks.