MAATRIX / Блог / Почему шифрование перестало нагружать процессор и что изменилось внутри него

Почему шифрование перестало нагружать процессор и что изменилось внутри него

MAATRIX

Если у вас в голове до сих пор сидит установка «включишь HTTPS на весь сайт — процессор ляжет под нагрузкой», пора её пересмотреть. Лет пятнадцать-двадцать назад это было честной инженерной осторожностью: шифрование действительно откусывало заметный кусок CPU, и админы взвешивали, стоит ли овчинка выделки. Сегодня на обычном VPS TLS-шифрование — почти незаметная строчка в профиле нагрузки на фоне работы приложения и базы данных. Разберём, что именно изменилось внутри процессоров и протоколов, и почему миф «HTTPS = медленно» пережил причину, которая его породила.

Откуда взялся миф «шифрование дорого»

В девяностых и начале двухтысячных, когда SSL и ранний TLS только становились массовыми, все криптографические операции выполнялись целиком в software — общими вычислительными блоками процессора, без какой-либо специализированной поддержки со стороны железа. Раунды блочного шифра AES считались через таблицы подстановок и обычные арифметические инструкции, а асимметричная часть хендшейка (RSA с ключами 1024-2048 бит) требовала модульного возведения в степень — операции, которая по своей математической природе на порядки тяжелее одного раунда симметричного шифра.

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

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

Что изменилось внутри CPU: аппаратные инструкции для AES

Ключевой сдвиг — это AES-NI (AES New Instructions), набор инструкций, который Intel добавила в архитектуру x86 начиная с процессоров поколения Westmere (конец 2010 года), а AMD — чуть позже, начиная с Bulldozer. Смысл в том, что один раунд шифрования AES, который раньше выполнялся десятком обычных инструкций через таблицы подстановок в памяти, теперь выполняется одной специализированной инструкцией процессора — AESENC для раунда шифрования, AESDEC для расшифровки, плюс инструкции для генерации раундовых ключей.

Практический эффект такой аппаратной поддержки — не просто «немного быстрее», а смена самого способа вычисления: вместо цикла с обращениями к памяти и условными переходами процессор делает шифрование фиксированной, предсказуемой последовательностью тактов прямо в исполнительном конвейере. Заодно закрывается и отдельный класс атак — программная реализация через таблицы подстановок исторически была уязвима к атакам по времени выполнения (timing attacks), потому что доступ к разным ячейкам таблицы занимал чуть разное время в зависимости от данных кэша. Аппаратная инструкция выполняется за константное время независимо от ключа и данных.

Сегодня AES-NI есть практически во всех серверных x86-процессорах — и Intel Xeon, и AMD EPYC. У ARM есть свой аналог — Cryptography Extensions в ARMv8, доступные на большинстве современных серверных ARM-чипов (включая то, что вы встретите в облачных ARM-инстансах). То есть если вы арендуете современный VPS практически у любого провайдера, шифрование AES на нём почти наверняка идёт не программным циклом, а выделенной инструкцией процессора.

Проверить это на своём сервере просто:

lscpu | grep -i aes
# или
cat /proc/cpuinfo | grep -m1 aes

Если флаг aes есть в списке — процессор поддерживает аппаратное ускорение, и OpenSSL (а вместе с ним nginx, curl, большинство серверного софта) использует его автоматически, без дополнительной настройки. Отдельный нюанс для виртуалок: гипервизор должен пробросить флаг aes гостевой системе — в редких случаях после миграции между хостами или при определённых профилях виртуализации флаг может не отображаться в госте, даже если физический процессор его поддерживает. Об этом — в разделе про узкие места ниже.

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

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

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

Асимметричная криптография тоже стала легче: ECDHE вместо RSA

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

Классический RSA-хендшейк требует модульного возведения в степень с большими числами — вычислительно тяжёлой операции, которая не ускоряется отдельной инструкцией CPU так же прямолинейно, как AES. Современный TLS (и практически весь TLS 1.3) вместо этого использует ECDHE — обмен ключами на эллиптических кривых. Для эквивалентного уровня криптографической стойкости эллиптическим кривым хватает существенно меньшего размера ключа и меньше вычислений, чем модульной арифметике RSA — это математическое свойство самих кривых, а не следствие оптимизации кода.

TLS 1.3 дополнительно упростил сам процесс: убрал устаревшие и вычислительно тяжёлые режимы совместимости, сократил число обязательных round-trip'ов в хендшейке и оставил только компактный набор современных и безопасных алгоритмов вместо длинного исторического списка, который приходилось поддерживать TLS 1.2 ради обратной совместимости.

Отдельный момент — асимметричная операция выполняется один раз на установление соединения, а не на каждый байт трафика. При активном keep-alive и session resumption повторные запросы того же клиента вообще обходятся без пересчёта асимметрики — про это подробно, с настройкой ssl_session_cache и проверкой через openssl s_client -reconnect, есть отдельный разбор — почему HTTPS «дорогой» только в первый раз. Для темы этой статьи важно другое: даже полный хендшейк с нуля сегодня — на порядок легче того же RSA-хендшейка пятнадцатилетней давности просто потому, что математика под капотом сменилась на менее затратную.

Современные шифры экономят ещё один проход по данным

Третий слой изменений — сам набор используемых режимов шифрования. Старые cipher suite вроде AES-CBC в связке с HMAC-SHA1 требовали двух отдельных проходов по данным: один — зашифровать блок, второй — отдельно посчитать код аутентификации сообщения (MAC), чтобы получатель мог убедиться, что данные не подменили по дороге. Два прохода — это не только вдвое больше операций, но и отдельный класс уязвимостей (padding oracle атаки на CBC-режим), из-за которых такие связки постепенно вывели из современных стандартов.

Сегодняшний стандарт — AEAD-режимы (Authenticated Encryption with Associated Data), из которых на практике вы встретите два: AES-GCM и ChaCha20-Poly1305. Оба делают шифрование и аутентификацию за один проход по данным, без отдельного шага под MAC. Разница между ними — в том, для какого железа они оптимальны:

  • AES-GCM — быстрее там, где есть AES-NI, потому что сам блочный шифр внутри — это AES, и режим GCM спроектирован так, чтобы хорошо параллелиться на современных CPU.
  • ChaCha20-Poly1305 — шифр, спроектированный так, чтобы быть быстрым в чистом software, без аппаратной поддержки. Это осмысленный выбор для устройств без AES-NI — часть мобильных чипов, старые ARM-платформы без Cryptography Extensions, некоторые встраиваемые системы.

Практический вывод для сервера: если у вас современный x86- или ARM-процессор с аппаратной поддержкой AES, разумно отдавать приоритет AES-GCM в списке шифров nginx, а ChaCha20-Poly1305 держать как fallback для клиентов на устройствах без аппаратного ускорения:

ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305;
ssl_prefer_server_ciphers off;

Начиная с TLS 1.3 порядок предпочтений шифров в основном определяет клиент, а не сервер (ssl_prefer_server_ciphers в первую очередь влияет на TLS 1.2), но сам список разрешённых шифров всё равно стоит держать коротким и современным — не ради производительности даже, а чтобы не тащить в конфиге устаревшие связки вроде CBC-режимов или RC4 просто по инерции от старых гайдов.

Где на самом деле уходит время обработки запроса

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

Типичный жизненный цикл динамического HTTP-запроса на сервере выглядит примерно так:

  1. TLS-хендшейк (если соединение новое) или расшифровка входящих байт (если resumed) — сегодня это доли от общего бюджета времени благодаря всему разобранному выше.
  2. Парсинг HTTP-запроса и маршрутизация в приложении.
  3. Логика приложения — вызовы бизнес-кода, сериализация/десериализация данных.
  4. Запросы к базе данных — здесь легко потерять на порядки больше времени, чем на всём TLS-слое, особенно если запрос делает full table scan без нужного индекса, или если соединение с БД идёт через отдельный сетевой прыжок с собственным TLS-контекстом.
  5. Обращения к внешним API, кэшам, файловой системе.
  6. Рендеринг ответа и обратная сериализация в HTTP.
  7. Сетевой RTT до клиента — часто больше, чем всё серверное время обработки вместе взятое, особенно если клиент физически далеко от дата-центра.

Я намеренно не привожу здесь конкретных цифр в миллисекундах или процентах — соотношение сильно зависит от вашего конкретного стека, размера ответа, схемы БД и сети, и любое усреднённое число будет вводить в заблуждение больше, чем помогать. Но качественная картина устойчива: на современном сервере с аппаратным AES доля CPU-времени, которая уходит именно на криптографию TLS, обычно теряется на фоне времени, которое уходит на логику приложения и обращения к БД. Если у вас реально тормозит сервер под HTTPS-нагрузкой, статистически вероятнее искать причину не в шифровании, а в неоптимальном запросе к БД, отсутствующем индексе или синхронном вызове внешнего API в горячем пути.

Как проверить это своими руками

Вместо того чтобы верить мифу или этой статье на слово, разницу можно измерить прямо на своём сервере.

Сравнить пропускную способность разных шифров на конкретном CPU:

openssl speed -evp aes-256-gcm
openssl speed -evp chacha20-poly1305

Команда прогонит шифрование блоков разного размера и выведет пропускную способность в мегабайтах в секунду для каждого размера — конкретные цифры будут своими для вашего процессора, но сам факт, что AES-GCM на CPU с AES-NI ощутимо обгоняет ChaCha20, обычно виден сразу в выводе, без необходимости запоминать эталонные числа откуда-то со стороны.

Сравнить нагрузку на CPU от HTTP и HTTPS версии одного и того же бэкенда под нагрузкой — если у вас есть тестовый стенд, где можно временно поднять один и тот же апстрим на двух портах (обычный и через TLS-терминацию nginx), запустите нагрузочный тест и смотрите на top/htop параллельно:

wrk -t4 -c100 -d30s https://staging.example.com/
wrk -t4 -c100 -d30s http://staging.example.com:8080/

Разница в CPU-потреблении между двумя прогонами и покажет реальную цену TLS именно на вашем стеке и вашем железе — вместо абстрактного «шифрование дорого» или «шифрование бесплатно» из чужой статьи. На большинстве современных VPS разница будет заметно меньше, чем разница между запросом с индексом и без него в той же БД.

Если у вас поверх TLS ещё поднят QUIC для HTTP/3, где шифрование обязательно на транспортном уровне без возможности отключить его вообще — там применимы те же аппаратные ускорители, и настройка описана в отдельном разборе HTTP/3 и QUIC на VPS.

Когда шифрование всё же может стать заметным

Разбор был бы нечестным, если не сказать, где старый миф всё ещё частично верен — таких сценариев немного, но они реальны.

  • Процессор без аппаратной поддержки. Встречается редко на современных серверных платформах, но не исключено на очень старом железе или в отдельных бюджетных конфигурациях. Проверка через lscpu | grep aes снимает вопрос за секунду.
  • Виртуализация не пробрасывает флаг CPU гостю. Иногда после миграции VPS между физическими хостами, смены типа CPU в гипервизоре или использования консервативного профиля виртуализации (qemu64 вместо host-passthrough) гостевая система не видит флаг aes, даже если физический процессор его поддерживает. Стоит проверить lscpu сразу после разворачивания сервера и при подозрении на просадку производительности после миграции — обратиться к провайдеру за уточнением профиля CPU.
  • Устаревшие cipher suite в конфиге по инерции. Если в ssl_ciphers годами тащится список для совместимости со старыми клиентами (CBC-режимы, устаревшие MAC), сервер может договариваться с частью клиентов на менее эффективные шифры, требующие двух проходов вместо одного. Стоит периодически пересматривать список шифров, ориентируясь на актуальные рекомендации (например, Mozilla SSL Configuration Generator в режиме Intermediate или Modern).
  • Огромное число новых TLS-соединений без resumption. Если keep-alive не настроен и балансировщик не даёт resumption работать между инстансами, каждый запрос — это новый хендшейк с нуля, и асимметричная часть начинает суммироваться в заметную нагрузку даже при лёгкой ECDHE. Это не про шифрование данных как таковое, а про архитектуру соединений — лечится настройкой ssl_session_cache/ssl_session_tickets, а не отказом от HTTPS.

Ни один из этих сценариев не повод «выключить HTTPS ради CPU» — правильный шаг почти всегда устранить конкретную причину, а не жертвовать шифрованием как таковым.

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

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

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

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

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

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

Нужно ли что-то специально включать в nginx, чтобы использовался AES-NI?

Нет, если у вас достаточно свежая версия OpenSSL (а вместе с ней и nginx, который её использует) — аппаратное ускорение задействуется автоматически, когда процессор его поддерживает. Проверить факт использования можно косвенно через openssl speed -evp aes-256-gcm — если цифры сильно ниже ожидаемых для современного CPU, стоит проверить версию OpenSSL и флаг aes в lscpu.

ChaCha20 быстрее AES или медленнее?

Зависит от железа. На CPU с AES-NI обычно быстрее AES-GCM. На устройствах без аппаратного ускорения (часть мобильных чипов, старые ARM без Cryptography Extensions) обычно быстрее ChaCha20-Poly1305, потому что он спроектирован для эффективной работы в чистом software. На сервере с современным x86 или ARM разница на практике редко становится узким местом при любом из двух вариантов.

Стоит ли на слабом или бюджетном VPS отключать HTTPS ради экономии CPU?

Практически никогда. Сначала проверьте lscpu | grep aes — если аппаратное ускорение доступно, экономия от отказа от TLS будет незначительной на фоне того, что вы теряете в безопасности и доверии клиентов (браузеры явно помечают HTTP-сайты как небезопасные). Если ускорения действительно нет, разумнее сменить план на VPS с современным CPU, чем убирать шифрование.

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

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

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