MAATRIX / Блог / Отключили TLS 1.0 и вместе с ботами потеряли платёжный шлюз

Отключили TLS 1.0 и вместе с ботами потеряли платёжный шлюз

MAATRIX

В конце августа 2026 года мы закрывали пункт из чеклиста аудита безопасности — отключали устаревшие TLS 1.0 и TLS 1.1 на всех продакшн-серверах. Формально всё прошло гладко: сканеры отчитались об улучшении рейтинга, сайт открывался как обычно. А через несколько часов начали копиться заказы в статусе «ожидает оплаты», хотя деньги с карт списывались. Это разбор того, как безобидное на вид security-обновление тихо отрезало платёжный шлюз, какие гипотезы мы отбросили по пути и что в итоге оказалось причиной.

Что мы увидели: заказы зависли в статусе «ожидание оплаты»

Первый сигнал пришёл не от мониторинга инфраструктуры, а от службы поддержки: покупатели писали, что деньги списались, а заказ в личном кабинете так и висит неоплаченным. Дальше подключился Prometheus-алерт, который мы обычно игнорировали как шумный — очередь заказов в статусе pending_payment начала расти линейно, без признаков остановки.

Логика магазина простая: покупатель уходит на страницу эквайера, платит, эквайер выполняет server-to-server запрос (webhook/колбэк) на наш эндпоинт /api/payments/callback, наш бэкенд помечает заказ оплаченным. Если колбэк не приходит, заказ так и остаётся висеть — покупатель формально заплатил, но у нас об этом нет записи. Это ровно то, что мы наблюдали: сама оплата на стороне эквайера проходила без единой жалобы, а вот статус на нашей стороне не менялся никогда.

Проверили дашборд nginx: RPS на основной домен в норме, 5xx на витрине не растут, воркеры очереди сообщений живы и ничего не логируют — просто ждут задач, которых не поступает. То есть проблема была не в коде обработки колбэка, а раньше — где-то на пути запроса от эквайера до нашего сервера.

Хронология: что мы меняли в тот день

Чтобы понять, где искать, восстановили таймлайн. В субботу утром, в рамках планового окна на обслуживание, мы прошлись по всем nginx-виртуалхостам и привели ssl_protocols к единому современному стандарту — убрали TLSv1 и TLSv1.1, оставили только TLSv1.2 и TLSv1.3, синхронизировали список шифров с рекомендованным профилем Mozilla intermediate. Причина понятная: несколько серверов достались нам в наследство от предыдущей команды и годами работали с конфигом, который никто не трогал — там ещё жил TLS 1.0, хотя браузеры отказались от него ещё в районе 2020 года.

После деплоя прогнали SSL Labs и внутренний сканер — оценка выросла, предупреждений об устаревших протоколах не осталось. Витрина открывалась нормально в Chrome, Safari, Firefox. Мы закрыли тикет как выполненный. Первые жалобы от поддержки пришли примерно через три часа — то есть не сразу после деплоя, а с задержкой, из-за которой мы не сразу связали одно с другим.

Нужен сервер под эту задачу?

Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.

Арендовать сервер

Первая гипотеза: сломался обработчик колбэка

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

Проверили логи сервиса оплат — пусто. Не «запрос отклонён», не «подпись невалидна», а вообще ничего: сервис не получал ни одного запроса на /api/payments/callback с момента начала инцидента. Откатили деплой валидации подписи на всякий случай — ничего не изменилось, запросы всё равно не доходили. Это отбросило гипотезу «сломался код»: раз до обработчика запрос вообще не долетает, дело не в бизнес-логике.

Вторая гипотеза: firewall или WAF режет трафик эквайера

Следующая версия — сетевая фильтрация. У нас на входе стоит fail2ban с правилами на nginx и security group у хостера с allowlist по некоторым путям. Возникло подозрение, что IP-диапазон эквайера случайно попал под бан — например, из-за всплеска запросов сработал rate-limit и IP улетел в бан-лист.

Проверили fail2ban-client status nginx-limit-req — искомых адресов эквайера в списке забаненных нет. Проверили правила security group и iptables — там explicit allow на нужные подсети, ничего не изменилось за последнюю неделю. На всякий случай прогнали tcpdump -i eth0 port 443 and host <ip-эквайера> прямо во время инцидента — и вот тут стало интереснее: пакеты от эквайера реально приходят, TCP SYN/ACK устанавливается, дальше виден Client Hello. То есть до сервера трафик долетает, firewall тут ни при чём — проблема где-то в самом TLS-рукопожатии.

Что на самом деле показали логи nginx и tcpdump

Дальше включили подробный error_log на уровне debug для нужного vhost и подождали следующую попытку колбэка. В логе нашлась строка:

2026/08/29 14:12:07 [crit] 18422#18422: *58231 SSL_do_handshake() failed
(SSL: error:1417D18C:SSL routines:tls_process_client_hello:version too low)
while SSL handshaking, client: <ip-эквайера>, server: 0.0.0.0:443

«version too low» — сервер эквайера пытается договориться по протоколу, который наш nginx больше не поддерживает. Проверили вручную:

openssl s_client -connect payments.example.com:443 -tls1
...
140054921463104:error:1409442E:SSL routines:ssl3_read_bytes:tlsv1 alert protocol version

Сервер действительно отказывает в рукопожатии по TLS 1.0. Сопоставили время появления первых таких ошибок с временем деплоя — они начались буквально в течение той же минуты, что и рестарт nginx с новым конфигом. Совпадение по времени было полным, а не «примерно похожим».

Написали в техподдержку эквайера с вопросом, какой протокол использует их сервер для исходящих server-to-server запросов. Ответ подтвердил догадку: их callback-сервис работает на отдельном легаси-стеке, который они не трогали с момента интеграции, и по умолчанию использует TLS 1.0 для исходящих соединений — обновление до TLS 1.2 в их системе запланировано, но не выполнено. Учитывая, что подобная интеграция была одной из старейших у нас в проде, никто из нашей команды не подозревал, что где-то на другом конце всё ещё жив TLS 1.0.

Если хочется разобраться, что именно происходит на этих этапах рукопожатия и почему версия протокола вообще может «не сойтись» между клиентом и сервером — у нас есть отдельный разбор: что происходит до первого байта при TLS-рукопожатии.

Как исправили: не откатывать хардненинг, а сделать точечное исключение

Самый простой путь — вернуть TLSv1 TLSv1.1 обратно в общий конфиг — нас не устраивал. Это была бы не починка, а откат security-хардненинга для всех посетителей витрины ради одного контрагента с устаревшим стеком. Вместо этого сделали изоляцию: колбэк-эндпоинт эквайера вынесли на отдельный виртуалхост со своим набором протоколов, доступный только с IP-диапазона самого эквайера.

# Отдельный server-блок только для колбэков эквайера.
# Обычная витрина по-прежнему обслуживается на TLSv1.2/1.3.
server {
    listen 443 ssl;
    server_name payments-callback.example.com;

    ssl_certificate     /etc/letsencrypt/live/payments-callback.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/payments-callback.example.com/privkey.pem;

    # Временная компенсирующая мера: разрешаем TLS 1.0 только здесь
    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
    ssl_ciphers HIGH:!aNULL:!MD5;

    # Пускаем только опубликованные IP эквайера, остальным — 403
    allow 203.0.113.0/24;
    allow 198.51.100.0/24;
    deny all;

    location /api/payments/callback {
        proxy_pass http://payments_backend;
        proxy_set_header X-Real-IP $remote_addr;
    }
}

Ключевые решения здесь:

  • Разделение по доменам/vhost, а не по location в общем конфиге — так политика TLS для витрины и для колбэков не пересекаются, и случайно не «протечёт» друг в друга при следующей правке.
  • Allowlist по IP — старый протокол разрешён не всем подряд, а только опубликованным адресам эквайера. Это снижает риск, что кто-то посторонний воспользуется ослабленным TLS как точкой входа.
  • Явная временная метка в комментарии конфига и в трекере задач — компенсирующая мера отмечена как временная, с датой ревью через квартал и ссылкой на переписку с эквайером о планах их миграции.
  • Отдельный алерт на паттерн version too low в error_log — если эквайер обновится и перестанет слать TLS 1.0, мы это увидим и сможем закрыть исключение сразу, а не через полгода случайно.

Отдельно завели тикет с эквайером на закрытие TLS 1.0 с их стороны и зафиксировали компенсирующий контроль как временное исключение из политики безопасности — это стандартная практика при работе с PCI DSS: если полностью убрать устаревший протокол нельзя из-за внешней зависимости, риск документируется и ограничивается по площади, а не игнорируется.

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

Ещё один урок: если у вас на сервере что-то не применяет ожидаемые SSL-настройки после правки конфига — это отдельный класс проблем, не связанный с версией протокола напрямую, и для него у нас есть отдельный разбор: nginx не применяет SSL — причины и решение.

Нужен сервер под эту задачу?

Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.

Арендовать сервер

Нужны сами нейросети для контента?

Генерируйте изображения, видео и озвучку нейросетями на falapi.io — десятки моделей в одном окне. Оплата картой РФ и по СБП.

Частые вопросы

Почему нельзя было просто не трогать TLS 1.0 везде и не создавать себе проблему?

TLS 1.0 и 1.1 официально считаются устаревшими и небезопасными: в них есть известные уязвимости (например, класс атак вроде BEAST), и крупные платёжные организации требуют их отключения как часть PCI DSS. Держать их включёнными «на всякий случай» на всей витрине — это компромисс безопасности ради удобства ради одного контрагента, а не разумная стратегия.

Как быстро понять, что дело именно в TLS, а не в приложении?

Самый быстрый диагностический шаг — openssl s_client -connect host:443 -tls1 (или -tls1_1) с той стороны, где предположительно проблема, и проверка error_log nginx на строки SSL_do_handshake() failed или version too low. Если TCP-соединение устанавливается (виден SYN/ACK и Client Hello в tcpdump), а данные до бэкенда не доходят — почти всегда причина в согласовании протокола или шифра, а не в фильтрации трафика.

Можно ли было предугадать это заранее, до отключения TLS 1.0?

Частично да: перед отключением стоит хотя бы неделю логировать, какие клиенты вообще подключаются по устаревшим протоколам, прежде чем их выключать. Директива ssl_protocols не даёт такой статистики сама по себе, но можно временно логировать $ssl_protocol в access_log отдельной переменной и посмотреть, кто в текущем трафике ещё пользуется TLS 1.0/1.1, прежде чем резать их окончательно.

Нужно ли держать это исключение навсегда?

Нет, и в этом смысл компенсирующей меры, а не постоянного решения: как только эквайер подтвердит переход на TLS 1.2+ на своей стороне, исключение убирается, а виртуалхост для колбэков либо удаляется, либо приводится к тем же протоколам, что и остальная инфраструктура.

Стоит ли просто попросить у эквайера альтернативный способ доставки статуса оплаты, минуя вебхук?

Это разумный запасной вариант — многие эквайеры поддерживают polling API для сверки статусов транзакций, который можно использовать как подстраховку на случай сбоя колбэков, независимо от того, в чём причина. Но это не отменяет необходимости починить сам механизм доставки, потому что polling обычно даёт бóльшую задержку в обновлении статуса заказа.

Обсудить статью, задать вопрос или начать новую тему

Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.

Перейти в сообщество →