Аутентификация вебхуков - Защитите свои эндпоинты
finlight предоставляет несколько методов аутентификации для защиты доставки вебхуков. Каждый вебхук по умолчанию включает проверку подписи с необязательными дополнительными уровнями аутентификации для повышенной безопасности.
finlight поддерживает четыре метода аутентификации, которые можно использовать по отдельности или в сочетании:
Нет
Никакой дополнительной аутентификации, кроме стандартной проверки подписи.
Когда использовать:
- Среды тестирования и разработки
- Внутренние эндпоинты за защищёнными сетями
- Когда проверки подписи достаточно для безопасности
Примечание. Проверка подписи по-прежнему включается в каждый запрос вебхука независимо от этой настройки.
Заголовок X-Finlight-Key
Отправляет ваш ключ API в пользовательском заголовке X-Finlight-Key с каждым запросом вебхука.
Конфигурация:
- Укажите ключ API при настройке вебхука
- Ключ будет включён в заголовок
X-Finlight-Key
Реализация: Ваш эндпоинт должен проверять входящий заголовок:
const finlightKey = req.headers['x-finlight-key']
if (finlightKey !== 'your-expected-api-key') {
return res.status(401).send('Invalid API key')
}
Отправляемые заголовки:
X-Finlight-Key: your-api-key-value
X-Webhook-Signature: sha256=signature
X-Webhook-Timestamp: 2024-01-15T10:30:00.000Z
Базовая аутентификация
HTTP-аутентификация Basic с учётными данными логин/пароль.
Конфигурация:
- Задайте логин и пароль при настройке вебхука
- Учётные данные кодируются в base64 и отправляются в заголовке
Authorization
Реализация: Ваш эндпоинт получает стандартную HTTP Basic Auth:
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')
}
Отправляемые заголовки:
Authorization: Basic dXNlcm5hbWU6cGFzc3dvcmQ=
X-Webhook-Signature: sha256=signature
X-Webhook-Timestamp: 2024-01-15T10:30:00.000Z
Проверка подписи
Автоматическая безопасность: Каждый запрос вебхука включает проверку подписи независимо от выбранного метода аутентификации.
Как это работает:
- finlight генерирует метку времени при отправке вебхука
- Создаёт сообщение путём конкатенации:
timestamp + '.' + payload - Подписывает сообщение с помощью HMAC-SHA256, используя секретный ключ вашего вебхука
- Отправляет в заголовках и подпись, и метку времени
Включаемые заголовки:
X-Webhook-Signature: sha256=computed-signature
X-Webhook-Timestamp: 2024-01-15T10:30:00.000Z
Алгоритм подписи:
message = timestamp + '.' + raw_request_body
signature = HMAC-SHA256(message, webhook_secret)
Если заголовок X-Webhook-Timestamp отсутствует, сообщением является само необработанное тело запроса. Сравнивайте результат за постоянное время и отклоняйте доставки, метка времени которых старше пяти минут.
Телом должны быть необработанные, не разобранные байты запроса. Любое промежуточное ПО, которое декодирует и заново кодирует JSON до того, как вы прочитаете тело, изменяет последовательность байтов и делает подпись недействительной — это самая частая причина неудачной проверки. Читайте тело как необработанную строку или буфер, проверяйте его и только затем разбирайте.
Проверка с помощью официального клиента
Каждая клиентская библиотека содержит помощник, выполняющий всю проверку за вас: он принимает префикс sha256= в подписи, сравнивает за постоянное время, применяет пятиминутное окно защиты от повторов и возвращает разобранную статью. При неудачной проверке он выбрасывает исключение или возвращает ошибку — всегда отвечайте на это кодом 4xx и никогда не обрабатывайте полезную нагрузку.
Проверка вебхука
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)
}
})
Лучшие практики безопасности
Проверка метки времени
Показанные выше помощники constructEvent уже применяют пятиминутное окно защиты от повторов, поэтому это нужно, только если вы проверяете подписи вручную:
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
}
Безопасное хранение учётных данных
- Переменные окружения: Храните все секреты в переменных окружения
- Управление секретами: Используйте AWS Secrets Manager, HashiCorp Vault или аналог
- Никогда не хардкодьте: Никогда не коммитьте секреты в систему контроля версий
- Регулярная ротация: Периодически обновляйте секреты вебхуков
Рекомендации по настройке вебхуков см. в основной документации по вебхукам. Для всестороннего тестирования см. руководство по тестированию вебхуков.