InquirerBARMM chief forms CabinetThe Jerusalem PostPolice demolish Beit Hanina compound used to house Palestinians without permitsDaily MaverickNew wave of bomb hoaxes hits dozens of flights across EuropePunchNJC suspends three judges, disciplines six others한겨레여권 사법부 압박에 판사들 대응 나서나…전국법관대표회의 조기 개최VarietyTaiwan Creative Content Fest Opens as Chair Sue Wang Says ‘Culture Is Power’ Amid AI ShiftZDF heuteAktuelle Pressemitteilungen des ZDFSDP Espectáculos¿De qué murió el hermano de Mónica Castañeda? Ventaneando confirma su fallecimientoالنهارخاص "النهار": د.غبيشة الكتبي... من شغف الصقور إلى ريادة طب الصقور في الإماراتDaily MailBondi Beach massacre death toll rises to 16: Survivor dies from complications resulting from their injuries almost eleven months laterSözcüEşinden 2 yıl önce boşanmış! 8 kişinin katili bakın ne iş yapıyormuşESPNJaguars dominate Ravens in Baltimore
The Daily Newsstand · Free, Always
Friday, November 6, 2026

Пожалуйста, зашифруй мой пароль

Translate

Признай, разработчик, ты же любишь отправлять пароль в теле POST‑запроса из формы входа открытым текстом, ведь «там же TLS, всё зашифровано»? Судя по тому что я вижу в интернете, эту традицию соблюдают многие, и ИИ — не исключение. Одни продолжают запрашивать по сети пароли, прикрытые звёздочками, другие — используют одинаковый пароль в десятке различных сервисов. Даже Госуслуги не стали исключением. Открытый пароль гуляет неизвестно где, но все спокойны. «Задача неприоритетна» [cудя по всему] уже много лет.

"Спасение и сохранение пароля Василия Пупкина Госуслугами" (Фотонный оттиск, 2026)

«Спасение и сохранение пароля Василия Пупкина Госуслугами» (Фотонный оттиск, 2026)

Что не так?

В подавляющем большинстве продакшн‑инфраструктур TLS терминируется на балансировщике, reverse‑proxy (nginx, Envoy, HAProxy) или на CDN/edge‑узле (Cloudflare, Fastly, Yandex Cloud). Дальше, внутри периметра — до контейнера с приложением, до соседнего микросервиса, до очереди сообщений — трафик идёт по обычным HTTP, gRPC и так далее, потому что «мы же внутри, тут все свои». Пароль в этот момент — обычная строка в теле запроса, читаемая любым, кто оказался на этом внутреннем участке пути. Утечка может произойти через банальный log.info(req.body), забытый в middleware, или через APM/error‑tracking системы типа Sentry, которые могут сохранить полное тело HTTP‑запроса при ошибке, а аутентификация рано или поздно отпятисотит.

Это происходило с крупными компаниями:

В 2018 году GitHub обнаружил у себя баг, из‑за которого пароли пользователей в открытом виде записывались во внутренние логи компании, и эти незашифрованные пароли были доступны небольшому числу сотрудников GitHub. При этом в обычном режиме пароли надёжно хранились через bcrypt — проблему создал именно баг, из‑за которого plaintext‑версия попадала в лог.

Практически одновременно похожая история случилась у Twitter: компания сообщила о баге, из‑за которого пароли пользователей сохранялись во внутреннем логе в незамаскированном виде. По словам техдиректора компании, из‑за ошибки пароли записывались в лог до того, как полностью проходили процесс хеширования, хотя в норме система использовала bcrypt для маскирования паролей.

В обоих случаях TLS работал штатно. Проблема была в том, что где‑то на пути между приёмом запроса и вызовом функции хеширования пароль на короткое время существовал в виде обычной строки, и эта строка попала не туда.

Пароль — это тайна

Ты наверное с оголтелым снобизмом заявишь что пароли от разных сервисов должны быть разные, сложные, храниться в менеджере паролей, и так далее. Но в реальном мире это не работает. Клиент всегда будет пытаться задать пароль «111». (часто просто потому, что ему вообще не нужна была регистрация). И только ограничения разработчика заставляют его уйти придумать правильный пароль. Можно ограничить количество символов и их разнообразие, но вот чего разработчик никогда не сможет — это заставить придумать уникальный пароль. Значит мы живём в мире где один пароль пользователя подходит к разным сервисам. Это факт. А коль скоро это факт, утечка его пароля из твоей системы вызывает последствия неопределённых масштабов, выходящие далеко за пределы тех рисков и ответственности, на которые ты мог рассчитывать.

Если тебе плевать на судьбу своих пользователей, предлагаю рассмотреть немного абсурдный, но занимательный пример.
Сами по себе логин и пароль не являются персональными данными, но ровно до тех пор, пока я не запишу туда свои паспортные данные и отправлю на сервер. Ну а что? Такой вот у меня пароль. Так уж вышло, что по нему можно однозначно идентифицировать мою личность. Как только сервер принял паспортные данные в виде пароля и пытается их захешировать, у владельца сервера тут же возникает статус оператора персональных данных со всеми вытекающими. Чувствуешь градус абсурда?

Можно ещё повысить этот градус, если передать в качестве пароля запрещённую информацию. Представь, твой сервер ночами напролёт хеширует цопэ, которое ему присылают в качестве пароля, а особо понравившееся nginx сохраняет в access‑log, а Sentry ещё и «распространяет».
Здорово, правда?

Я это к чему — сам пароль серверу для аутентификации вовсе и не нужен, и знать его — лишняя головная боль. Пароль — это «чужое». Нельзя трогать. Ай‑яй‑яй. Надеюсь, что мне удалось донести эту простую мысль.

Что делать

Главное правило — Пароль не должен покидать клиента.

Хеширование пароля на клиенте

Самый ленивый способ. Клиент отправляет hash(password + salt). Исходный пароль не светится больше нигде и никогда, но сам хеш становится эквивалентом пароля, который однако, при утечке больше нигде использовать не получится. Необходимо использовать современные алгоритмы хеширования, например Argon2. В соль нужно включить домен сервиса или уникальный идентификатор: иначе два сайта, внедрившие одинаковую схему с одинаковой солью, выдадут одинаковые хеши, и перехват на одном откроет вход на другой. Удобный вариант — соль из домена и логина, вычисляемая на клиенте.

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

Хеширование уже закрывает большую часть проблем. Для большинства пет‑проектов этого вполне достаточно. Хотелось бы чтобы хотя бы этот метод стал базовой базой вместо отправки исходного пароля.

На этом я изначально хотел и закончить. Но если уж начал обмазываться криптографией, нужно идти до конца. (если кто‑нибудь сделает и скинет в комменты мемас на тему «Страха и ненависти в криптографии», буду благодарен и добавлю его сюда)

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

Сначала немного теории, потом будет пара конкретных алгоритмов.

ZKPP

Zero‑knowledge password proof (доказательство пароля с нулевым разглашением, ZKPP). Общее название механизма, который позволяет одной стороне (доказывающей) доказать другой стороне (проверяющей), знание пароля, не раскрывая его.

Это идея обычного доказательства с нулевым разглашением (ZK), адаптированная под низкоэнтропийный секрет. В отличие от длинного ключа, пароль имеет относительно мало символов, и его можно перебрать, поэтому протокол должен быть устроен так, чтобы перехваченный обмен не позволял перебирать пароли по словарю оффлайн: каждая догадка должна требовать отдельного обращения к тому, кто может его проверить. Полностью перебор не исключить: теоретически можно стучаться на сервер и подбирать пароль бесконечно долго. Rate limit и другие технические ухищрения это ограничивают, но не исключают.

Но сам ZKPP отвечает только на вопрос «знает ли собеседник пароль». Аутентификация без выработки общего ключа почти бесполезна: она не привязана к конкретному обмену, поэтому её можно воспроизвести или перехватить с помощью man‑in‑the‑middle. Значит, нужен следующий шаг — превратить доказательство в ключ.

PAKE

Password‑Authenticated Key Exchange (PAKE) — общее название класса криптографических протоколов, в которых стороны на основе пароля вырабатывают общий высокоэнтропийный сессионный ключ. Не любой PAKE формально является ZKPP. Делятся на два типа:

  • Balanced PAKE — обе стороны знают один и тот же пароль (или одну и ту же его производную). В строгом смысле это не ZKPP, хотя применение у таких протоколов огромно: например, WPA3-SAE в твоём роутере. Но это не наш случай.

  • Augmented PAKE (aPAKE) — более подходящий для клиент‑серверных сценариев, в которых сервер хранит верификатор — уникальное производное, из которого нельзя легко восстановить пароль и которое нельзя переотправить для входа.

Любой приличный aPAKE даёт:

  • взаимную аутентификацию

  • установление сессионного ключа

  • защиту от офлайн‑перебора по перехваченному обмену (онлайн‑перебор остаётся и ограничивается только rate limit'ами).

Получаемый сессионный ключ нужен прежде всего самому протоколу: через него стороны подтверждают, что обмен прошёл с одним и тем же паролем именно в этой сессии, поэтому записанный обмен нельзя воспроизвести. Дальше уже сессия выдаётся как обычно (cookie с идентификатором, JWT или что ты там любишь). От кражи этой сессии PAKE не защищает. Его задача вполне конкретна: пароль и его эквивалент никогда не попадают на сервер, утечка базы не позволяет зайти, а перехваченный обмен не позволяет перебирать пароли офлайн. То что нам и было нужно.
Ниже пара современных представителей aPAKE на выбор — попроще и посложнее.

SPAKE2+

RFC 9383, сентябрь 2023. Один из самых простых и актуальных aPAKE. Не путай со SPAKE2.

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

Модель угроз:

  • Перехват трафика — бесполезен. Атакующий получает одну онлайн‑попытку за сессию.

  • Утечка верификатора из базы — даёт офлайн‑перебор. Верификатор математически связан с паролем, поэтому кандидата можно проверить без участия сервера.*

  • Компрометация сервера в момент регистрации — тоже даёт офлайн‑перебор: сервер получает верификатор.*

Единственный тормоз для такого перебора — стоимость вычисления производного от пароля: Argon2 с нормальными параметрами и уникальная соль для каждого пользователя. Для длинного пароля этого достаточно.

OPAQUE

Золотой стандарт. RFC 9807 от 2025 г. Рекомендован IETF CFRG. Имеет строгое криптографическое доказательство безопасности.

Клиент отправляет «ослеплённое» значение (математическая маскировка пароля случайным множителем), сервер применяет к нему свою секретную функцию и возвращает результат, не видя, что именно обработал. Секрет этой функции — OPRF‑ключ: случайный скаляр, который сервер хранит вне базы (HSM/KMS/Vault), и без которого украденные верификаторы бесполезны для перебора. Сервер участвует в вычислении производного от пароля, не получая никакой информации о пароле, даже если Sentry в порыве энтузиазма запишет весь request body со стеком. Сверху OPAQUE добавляет обмен ключами на основе тройного Диффи‑Хеллмана: клиент и сервер взаимно проверяют, что общаются друг с другом, и договариваются об общем сессионном ключе.

Модель угроз:

  • Перехват трафика — бесполезен.

  • Утечка базы верификаторов — не даёт офлайн‑перебора: в базе нет серверного секрета, а без него прогнать кандидата через OPRF нельзя.

  • Компрометация сервера в момент регистрации: пароль не раскрывается, сервер видит только ослеплённые значения.

  • Полная компрометация сервера (ключ + база): офлайн‑перебор возможен, но по одному пользователю за раз и без заранее посчитанных таблиц. Под угрозой только совсем слабые пароли.

  • Компрометация TLS‑сертификата сервера (привет ТСПУ+НУЦ): вход и пароль это не раскрывает, взаимная аутентификация делает MITM бесполезным. Остальной трафик — уже не в его компетенции.

Минусы этого добряка в основном связаны с серверным ключом:

  • Серверный секрет придётся беречь. Он должен жить в HSM/KMS/Vault, а не в базе и не в репозитории.

  • при деплое ключ нужно безопасно доставить в приложение (через менеджер секретов, а не «как‑нибудь через env»);

  • при масштабировании на несколько серверов ключ должен быть одинаковым у всех;

  • при ротации нужно перерегистрировать всех пользователей или поддерживать сложную схему с двумя ключами одновременно;

  • при компрометации ключ приходится отзывать, перерегистрировать всех пользователей и принудительно менять пароли.

  • Стоимость. OPRF добавляет несколько операций на эллиптической кривой, а основная нагрузка обычно приходится на Argon2 на стороне клиента. Но на очень нагруженном сервисе лишние операции на каждый вход всё же заметны.

Аутентификация без пароля

Всякие Passkeys / WebAuthn — это отдельный дивный мир. Рассматривать его в рамках этой статьи не вижу смысла.

Ещё раз

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

Ничего придумывать не нужно. Всё стандартизировано, существуют библиотеки. Если не хочешь вникать в «эти ваши непонятные протоколы», делай хотя бы хеширование на фронте. Это всё равно будет сильно лучше чем отправлять голый пароль.
Да и в конце концов, не руками же тебе код писать.

Спасибо за внимание к этому вопросу. Распространите среди жильцов вашего ЖЭКа.

Только зарегистрированные пользователи могут участвовать в опросе. Войдите, пожалуйста.

View the original on Хабр →

KioskNews shows a cleaned-up reading view extracted from the publisher’s page — the original always lives on their site, not ours.