Техническое руководство
Защита video API webhook от replay и дублей
Защита video API webhook от replay начинается с простой идеи: HTTP callback нельзя считать новым и настоящим только потому, что он дошёл до вашего адреса. Подпись, схема и временные поля проверяются по текущему документированному контракту, а обработчик сохраняет event ID до запуска побочного эффекта. Так повторная доставка, задержка сети или ошибочный ретрай не создают второе уведомление, списание или выдачу результата.
Короткий ответ: принимайте callback в две фазы
Первая фаза должна быть короткой: принять raw body, проверить допустимый метод и размер, извлечь только документированные заголовки и подтвердить подпись по актуальному контракту. Не меняйте JSON до проверки, если схема подписи требует исходные байты. Алгоритм, имя заголовка и кодировка не должны браться из случайного примера: они могут отличаться у каждого маршрута и обновляться. При неясности остановите интеграцию в staging и подтвердите контракт у поставщика.
Во второй фазе сохраните event ID или собственный ключ дедупликации в устойчивом хранилище, затем быстро верните допустимый ответ и передайте полезную работу очереди. Состояние события меняется транзакционно: «получено», «обрабатывается», «завершено» или «отклонено». Повтор того же ID не должен снова создавать задачу. Такая модель работает и когда callback приходит дважды, и когда ваш worker перезапускается после уже принятого ответа.
Подпись и время события проверяются по одному контракту
Подпись доказывает только то, что проверка совпала с доступным секретом и конкретным набором байтов; она не заменяет валидацию содержания и не гарантирует, что событие ещё актуально. Проверьте формат timestamp, допустимое окно, порядок полей и способ сравнения подписи в документации текущего endpoint. Используйте constant-time сравнение, храните секрет проверки только server-side и не выводите его в exception, лог или тикет.
Окно времени уменьшает риск старого callback, но не даёт универсального числа минут. Слишком короткое окно отбрасывает легитимные задержки, слишком длинное расширяет поверхность replay. Начните с согласованного тестового значения, измеряйте задержки и документируйте исключения. Если у контракта нет timestamp или подписи, не имитируйте защиту произвольным заголовком: ограничьте источник сети, запросите подтверждение схемы и держите действие без побочного эффекта до прояснения.
Idempotency защищает бизнес-действие, а не только HTTP
Даже корректное событие может быть доставлено повторно. Поэтому event ID сопоставляют с тем, что делает приложение: отправкой письма, переводом статуса, открытием ссылки на результат или записью расхода. Хранилище дедупликации должно переживать перезапуск процесса и конкурентные запросы. Уникальное ограничение по ключу операции надёжнее локальной переменной в памяти, а короткая блокировка не заменяет запись о завершённой обработке.
Не создавайте key из всего request body, prompt или URL результата. Это может сохранить лишние данные и сломаться при безопасном изменении полей. Предпочтительнее документированный event ID плюс имя маршрута и tenant, если контракт требует такую область уникальности. Если event ID отсутствует, введите собственную корреляцию в момент создания video-задачи и сверяйте известный task ID. Не объявляйте результат готовым, пока статус и право выдачи не подтверждены вашим сервером.
Очередь, DLQ и replay должны иметь стоп-условия
Успешная HTTP-проверка не означает успешную бизнес-обработку. Worker может временно не получить файл, не пройти schema validation или встретить недоступную зависимость. Поместите работу в очередь с ограниченным числом попыток, backoff и понятной классификацией ошибок. Ошибка подписи, неизвестный event type и несоответствие tenant не исправляются retry; их отмечают как отказ и разбирают без повторной доставки в бизнес-логику.
DLQ нужна для безопасного исследования, а не для бесконечного replay. Храните безопасные метаданные, correlation ID и причину, а не секретные заголовки или исходные материалы. Перед повтором исправьте причину, подтвердите, что событие ещё актуально, и используйте ту же защиту от дублей. Для некоторых типов событий правильным действием будет сверка текущего статуса task ID через разрешённый server-side маршрут, а не повторный запуск генерации.
Тестируйте повторы до production
Минимальный контрактный набор включает корректную подпись, битый body, неправильную подпись, слишком старое событие, неизвестный event type, два одинаковых event ID одновременно и повтор после завершения worker. Для каждого кейса зафиксируйте ожидаемый HTTP-ответ, запись аудита и отсутствие лишнего побочного эффекта. Используйте synthetic task и тестовый проект; не помещайте в fixtures реальные ключи, ссылки на приватный результат или материалы пользователей.
При rollout наблюдайте агрегированное число принятых, отклонённых, дублированных и поставленных в очередь событий. Если резко растут отказы проверки, отключите маршрут или переведите его в режим безопасной регистрации метаданных, не ослабляя подпись. RussiaAPI — независимый API gateway; доступные видео-модели, callback-поля, правила хранения и подписи зависят от текущего каталога и контрактов. Эта схема не обещает, что любой provider использует одинаковые заголовки или сроки доставки.
Server-side пример
Пример показывает локальную проверку на сервере. Ключи берутся только из окружения; до запуска подтвердите маршрут, model ID и параметры в текущем каталоге RussiaAPI.
import { timingSafeEqual, createHmac } from 'node:crypto';
export function verifyWebhook({ rawBody, signature, timestamp, secret, now = Date.now() }) {
if (!Number.isFinite(Number(timestamp)) || Math.abs(now - Number(timestamp) * 1000) > 300_000) return false;
const expected = createHmac('sha256', secret).update(timestamp + '.' + rawBody).digest('hex');
const received = Buffer.from(signature || '', 'hex');
const wanted = Buffer.from(expected, 'hex');
return received.length === wanted.length && timingSafeEqual(received, wanted);
}
// Before queueing work, atomically insert a documented event ID; duplicates must be no-ops.
Проверьте синтаксис через node --check, добавьте аутентификацию своего маршрута и негативные тесты. Не логируйте тело запроса или заголовки только ради отладки.
Границы и безопасный запуск
Это инженерное руководство, а не юридическое заключение и не инструкция по обходу законов, санкций, региональных, платёжных или платформенных ограничений. Не передавайте в тесты персональные данные, коммерческие секреты, upstream-ключи, cookie, пароли или полный заголовок Authorization. Для чувствительных данных подтвердите цель, минимизацию, срок хранения и договорные условия с ответственными специалистами.
Ключ RussiaAPI хранится только в server-side secret store. Разделяйте development, staging и production, ограничивайте доступ и журналируйте лишь безопасные метаданные. Неизвестную функцию, модель, квоту, подпись callback или поле ответа считайте неподтверждёнными, пока не проверите их в текущем разрешённом тестовом контуре.
Проверьте сценарий в RussiaAPI
Создайте собственный тестовый ключ в консоли, сверьте текущий каталог моделей и выполните обезличенный server-side smoke test. Расширяйте нагрузку и доступ только после измеримой проверки.
FAQ
Можно ли считать подпись защитой от дублей?
Нет. Подпись помогает проверить источник и целостность согласно контракту, но одинаковое корректное событие может быть доставлено повторно. Для побочного эффекта нужна устойчивая дедупликация по event ID или согласованной корреляции операции.
Какое окно времени выбрать?
Только после проверки текущего контракта и измерения реальных задержек вашего маршрута. Универсального числа нет: оно зависит от timestamp, очереди и допустимого риска. Документируйте решение и не подменяйте отсутствие timestamp произвольной проверкой.
Нужно ли повторять callback из DLQ?
Только после устранения причины и проверки актуальности события. Неправильная подпись, неизвестный тип или чужой tenant не требуют retry. Используйте ту же дедупликацию, чтобы повтор не создал новое бизнес-действие.