блог · 10 сентября 2026 · 8 минут

Пять уровней защиты платежей и ИИ-запросов: кейс клиентского проекта

Попросить ассистента «сделай приём платежей» — и получить код, который работает. Кнопка нажата, оплата прошла, доступ выдан. Тесты зелёные, демо клиенту выглядит убедительно. А дальше кто-то открывает devtools и платит доллар вместо ста. Разбираем на реальном клиентском проекте, какие дыры ИИ-ассистент оставляет в оплате по умолчанию и что мы строим вокруг платежей и ИИ-запросов, чтобы этого не случилось.

Дыра первая: цена приходит с клиента

Вот что генерируется по умолчанию, если просто попросить «сделай приём платежей»:

// так делать нельзя
app.post('/checkout', async (req, res) => {
  const { productId, amount } = req.body;        // ← сумма пришла из браузера
  const session = await psp.createSession({ amount, currency: 'usd' });
  res.json({ url: session.url });
});

Формально логично: фронтенд знает цену, он её и передал. Но фронтенд лежит на машине покупателя и правится в одну строку прямо в консоли браузера. amount: 10000 превращается в amount: 100, платёжка честно списывает доллар, товар уходит.

Правильно — клиент присылает только идентификатор того, что покупает, а цену сервер берёт у себя:

app.post('/checkout', requireAuth, async (req, res) => {
  const product = await products.get(req.body.product_id);
  if (!product) return res.sendStatus(404);
  // заказ фиксируем до похода в платёжку: с ним потом сверяем вебхук
  const order = await orders.create({
    user_id:      req.user.id,
    product_id:   product.id,
    amount_cents: product.price_cents,   // цена только из базы
    currency:     product.currency,
    status:       'pending',
  });
  const session = await psp.createSession({
    amount:   order.amount_cents,
    currency: order.currency,
    metadata: { order_id: order.id },
  });
  res.json({ url: session.url });
});

Разница в три строки. Но пока сумма приходит из req.body, любые проверки выше по коду бессмысленны.

Дыра вторая: оплата подтверждается страницей «Спасибо»

Второй типовой вариант: пользователь возвращается с платёжки на /success, и доступ выдаётся прямо там. Это ломается в обе стороны. Человек закрыл вкладку сразу после списания — деньги ушли, доступа нет, о факте оплаты никто не узнал. Или наоборот: открыл /success напрямую, минуя оплату, и получил доступ бесплатно.

Единственный источник правды об оплате — вебхук от провайдера. И его мало принять, его нужно проверить: подпись, время, идемпотентность и сумму заказа. По умолчанию ассистент в лучшем случае делает первый пункт — проверку подписи. Остальное приходится требовать отдельно и поимённо. Обе дыры не ловятся обычным тестированием: тесты проверяют «оплатил — получил» и «не оплатил — не получил», а атакующего интересует третий путь, которого в голове проверяющего нет.

Как мы ловим дыры до того, как их найдёт клиент

На одном из проектов с интеграцией платёжки код был написан, протестирован и формально работал: деньги приходят, товар выдаётся. На этом можно было сдавать работу. Мы прогнали сценарий за атакующего: хитрый пользователь подделывает ответ от платёжного шлюза — сервер верит, товар уходит бесплатно. Копает глубже — находит способ пополнить баланс в обход списаний, а затем добраться до чужих счетов.

Процесс, который у нас встроен в такие проекты, — три шага. Сначала разбираем документацию интеграции и типовые уязвимости в обычном диалоге, без кода — так проще держать в голове, что вообще искать. Затем отдаём готовый код ИИ-агенту на первый аудит: он проверяет логику по актуальной документации API, а мы разбираем каждый пункт отчёта и закрываем найденное. И последний шаг — стресс-тест в чистой сессии, без контекста прошлых чатов: агентам ставится одна жёсткая задача — сломать код и обойти оплату. В этом проекте они нашли лазейку, которую пропустил первый аудит.

Чистая сессия здесь принципиальна. Агент, который знает, «как задумано», защищает замысел и объясняет, почему всё правильно. Агент, который видит только код, ищет способ его обмануть — и это совсем другая роль.

Пять уровней защиты вокруг платежей и ИИ-запросов

Полностью защититься невозможно. Задача другая — сделать так, чтобы атака стоила дороже, чем то, что можно получить. В проекте, где мы сейчас собираем такую защиту, она строится в пять уровней.

1. Логирование всего

Каждый запрос, каждая операция. Выглядит паранойей ровно до первого разбора инцидента — тогда без логов разбираться нечем.

2. Блокировка подозрительных IP

Боты, спамеры, странные паттерны обращений отсекаются автоматически, без ручного разбора каждого случая.

3. Контроль трат по каждому аккаунту

Скользящее окно на число запросов и на потраченные деньги — отдельно, потому что 10 дорогих запросов к ИИ бьют по бюджету сильнее, чем 1000 дешёвых.

4. Алерты в реальном времени

Не «узнать из логов через неделю», а увидеть аномалию сейчас — пока её ещё можно остановить.

5. Аварийное отключение

Скрипт, который обрубает все внешние соединения: ни данных, ни API — и уведомление нам. На случай атаки, которую не предусмотрели остальные четыре уровня.

Третий уровень — контроль трат — на практике выглядит так:

// Считаем не только запросы, но и деньги: 10 дорогих запросов
// бьют по кошельку сильнее, чем 1000 дешёвых
async function guard(accountId, costCents) {
  const WINDOW = 3600;
  const now = Math.floor(Date.now() / 1000);
  const key = `spend:${accountId}`;
  await redis.zremrangebyscore(key, 0, now - WINDOW);
  await redis.zadd(key, now, `${now}:${crypto.randomUUID()}:${costCents}`);
  await redis.expire(key, WINDOW);
  const entries  = await redis.zrange(key, 0, -1);
  const requests = entries.length;
  const spent    = entries.reduce((s, e) => s + Number(e.split(':')[2]), 0);
  const limit = await limits.get(accountId);
  if (requests > limit.requests_per_hour || spent > limit.cents_per_hour) {
    await accounts.block(accountId, 'anomaly');
    await alerts.send('account_blocked', { accountId, requests, spent });
    return false;
  }
  return true;
}

Срабатывает это в двух случаях: либо клиент вашего клиента нашёл уязвимость, либо кто-то посторонний гоняет запросы к платному ИИ-API за чужой счёт — нашёл эндпоинт, который проксирует запросы в модель в обход интерфейса и лимитов, и пользуется им как бесплатным доступом к нейросети. В обоих случаях реакция одна: сначала стоп, потом разбор.

Отдельная тема — бэкапы. Мы делаем снимок инфраструктуры до начала любых работ на сервере, а не после инцидента. Причина простая: правка мелочи на проде способна уронить рабочий сайт клиента, и тогда единственное, что экономит часы разбирательств, — копия, снятая заранее.

Честная часть

ИИ-агенты не заменяют пентест. Они не видят инфраструктуру целиком, не знают бизнес-контекст и уверенно говорят «уязвимостей нет», когда просто ничего не нашли, — каждую находку приходится перепроверять руками. Это дешёвый первый фильтр, а не аудит безопасности. И описанные выше пять уровней не делают продукт неуязвимым: они закрывают конкретные места, в которых ошибаются чаще всего, — а не гарантируют отсутствие ошибок вообще.

Когда это не нужно

Что спросить у подрядчика про безопасность платежей

Строите продукт с платежами или платным ИИ-функционалом?

Разведка на 3–5 дней за 15 000 ₽ — разберём архитектуру и покажем, где риск, до старта основной разработки. Сумма зачитывается в проект. AI-агенты и защитные механизмы — от 120 000 ₽.

Telegram · @juzubiyyah