Предистория:

Решал я таски на Root-Me и попалась таска XMPP - authentication. Основная цель таски состояла в том, чтобы по захвату пакетов вытащить пароль, который использовался при аутентификации и я начал искать документацию к тому, как работает аутентификация клиента.


Наткнулся на xmpp wiki и там же нашел документацию на аутентификацию клиента SASL_Authentication_and_SCRAM. Из нее я выяснил то, как происходит распознавания того, что клиент знает правильный пароль и что просто так из пакетов пароль я не вытащу. И тут я заинтересовался тем, как вообще происходит подтверждение правильного пароля. Именно это и побудило меня написать эту статью.

Повествование о том, что такое этот ваш XMPP

XMPP(ранее jabber) - протокол обмена сообщениями с открытым исходным кодом, публикуемый под Apache License 2.0, основан на XML. Позволяет мгновенно передавать сообщения и информацию о присутствии, также с помощью расширений позволяет передавать файлы(XEP-0363 HTTP File Upload; XEP-0096 SI File Transfer; XEP-0065 SOCKS5 Bytestreams; XEP-0166: Jingle и т.д.), создавать комнаты(XEP-0045: Multi-User Chat), осуществлять голосовые и видео звонки(XEP-0166: Jingle), хранить произвольные данные на сервере(XEP-0049 Private XML Storage) и т.д.

А теперь о том, что я хотел рассказать в этой статье

У XMPP есть 2 способа аутентификации клиента SASL и OAuth 2.0. Начнем по порядку - с механизмов аутентификации SASL, используемые в XMPP.

SASL (Simple Authentication and Security Layer) — унифицированный фреймворк для реализации механизмов аутентификации и опционального шифрования при передаче данных по сети.

SASL:

Механизмы SASL, которые используются в XMPP:

  1. External

  2. SCRAM (Salted Challenge Response Authentication Mechanism)

  3. PLAIN

  4. DIGEST-MD5

    (расположены в порядке убывания безопасности)

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

SCRAM(На нем я и заострю свое внимание) - Наиболее распространенный и обязательный для современного ПО XMPP механизм. Обеспечивает надежную защиту пароля. Что меня зацепило именно в этом механизме, это то, что клиент не передает пароль в открытом виде, а подтверждает свое знание пароля.

Как оно работает?

  1. Клиент отправляет имя пользователя, под которым он хочет аунтефицироваться

  2. Сервер отправляет обратно соль для этого пользователя и количество итераций

  3. Клиент хэширует пароль с заданной солью для заданного количества итераций

  4. Клиент отправляет результат обратно

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

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

Какие вариации SCRAM использует XMPP?

  • SCRAM-SHA-256-PLUS

  • SCRAM-SHA-1-PLUS

  • SCRAM-SHA-256

  • SCRAM-SHA-1

    (расположены в порядке убывания безопасности, также есть SCRAM-SHA512(-PLUS) и SCRAM-SHA3-512(-PLUS), но на официальной вики и документе, на который она ссылается, нет информации о том, насколько они безопасны)

А теперь о том, как оно работает под капотом

  1. Сначала нормализуется пароль(используя SASLprep)

  2. Берется случайная строка(для примера будет 32 байта в Hex кодировке). Это будет clientNonce

  3. Клиент отправляет первоначальное сообщение(initialMessage) "n=" .. username .. ",r=" .. clientNonce

  4. Клиент добавляет GS2 заголовок к initialMessage и кодирует полученное в base64 и отправляет это в качестве своего первого сообщения


    biwsbj1yb21lbyxyPTZkNDQyYjVkOWU1MWE3NDBmMzY5ZTNkY2VjZjMxNzhl

  1. Сервер отвечает вызовом. Данные закодированы в base64


    cj02ZDQ0MmI1ZDllNTFhNzQwZjM2OWUzZGNlY2YzMTc4ZWMxMmIzOTg1YmJkNGE4ZTZmODE0YjQyMmFiNzY2NTczLHM9UVNYQ1IrUTZzZWs4YmY5MixpPTQwOTY=

  1. Клиент декодирует этоr=6d442b5d9e51a740f369e3dcecf3178ec12b3985bbd4a8e6f814b422ab766573,s=QSXCR+Q6sek8bf92,i=4096

  2. Клиент парсит отсюда:

    • r = Это serverNonce, клиент должен убедиться в том, что он начинается с clientNonce, который он отправил в своем первом сообщении

    • s = Это соль, закодированная в base64

    • i = Это количество итераций

  3. Клиент вычисляет:

clientFinalMessageBare = "c=biws,r=" .. serverNonce
saltedPassword = PBKDF2-SHA-1(normalizedPassword, salt, i)
clientKey = HMAC-SHA-1(saltedPassword, "Client Key")
storedKey = SHA-1(clientKey)
authMessage = initialMessage .. "," .. serverFirstMessage .. "," .. clientFinalMessageBare
clientSignature = HMAC-SHA-1(storedKey, authMessage)
clientProof = clientKey XOR clientSignature
serverKey = HMAC-SHA-1(saltedPassword, "Server Key")
serverSignature = HMAC-SHA-1(serverKey, authMessage)
clientFinalMessage = clientFinalMessageBare .. ",p=" .. base64(clientProof)
  1. Клиент кодирует в base64 clientFinalMessage и отправляет это как ответ



Yz1iaXdzLHI9NmQ0NDJiNWQ5ZTUxYTc0MGYzNjllM2RjZWNmMzE3OGVjMTJiMzk4NWJiZDRhOGU2ZjgxNGI0MjJhYjc2NjU3MyxwPXlxbTcyWWxmc2hFTmpQUjFYeGFucG5IUVA4bz0=

  1. Если все прошло успешно, то вы получите от сервера ответ ``:


	dj1wTk5ERlZFUXh1WHhDb1NFaVc4R0VaKzFSU289

Если декодировать из base64 это сообщение, то получим:
v=pNNDFVEQxuXxCoSEiW8GEZ+1RSo=
11. Клиент должен убедиться, что значение v - это serverSignature закодированный в base64.
И вот и все, дальше можно добавить привязку канала(channel binding), для улучшения защиты(предотвращения проведения MitM атак).

GS2‑заголовок — короткий префикс, добавляемый клиентом к своему первому сообщению в процессе аутентификации. Является частью фреймворка GS2, обеспечивающего совместимость между механизмами SASL (Simple Authentication and Security Layer) и GSS‑API (Generic Security Service Application Program Interface). Может содержать флаги gs2-cb-flag и gs2-authzid.

PLAIN - наиболее простой механизм аутентификации, все данные для аутентификации передаются закодированными в base64, по факту в открытом виде.
[authzid]\x00[username]\x00[password]
и при отправке выглядит примерно так

AGhhYnJfcmVhZGVyAGhlbGxvX3dvcmxk

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

DIGEST-MD5 - самые небезопасный механизм из списка, считается давно устаревшим и не рекомендуется к использованию. Данные для аутентификации передаются уже в закрытом виде, но камнем преткновения стало то, что для использования этого механизма требуется хранить пароль клиента в открытом виде, либо хранить результат первого хеширования MD5(user:realm:password).

И вот небольшое пояснение для тех, кто еще не вкатился, почему PLAIN безопасней DIGEST-MD5, если проблему PLAIN с передачей в открытом виде можно решить внедрением TLS, то проблему DIGEST-MD5 с небезопасным хранением пароля на сервере никак не решить, кроме как сменой механизма аутентификации. Но можно возразить, что PLAIN тоже хранит пароль в открытом виде, но в отличие от DIGEST-MD5, механизм PLAIN не навязывает серверу способ хранения паролей. Сервер получает пароль и волен хранить его с использованием современных стойких хеш-функций, таких, как bcrypt или Argon2, что является стандартом безопасности

OAuth 2.0

Шагнем назад, OAuth 2.0 - это протокол авторизации, а не аутентификации. Его задача - позволить одному приложению получить ограниченный доступ к данным пользователя в другом сервисе, не спрашивая у пользователя его пароль от этого сервиса.

Как оно реализовано в XMPP?
Чтобы встроить этот веб-ориентированный подход в XMPP, был создан специальный механизм SASL — OAUTHBEARER. Его логика кардинально отличается от PLAIN или SCRAM.

Этап 1:

  1. Пользователь в нашем приложении нажимает кнопку "Войти через Google".

  2. Приложение перенаправляет его на страницу входа Google в браузере.

  3. Пользователь вводит свой логин/пароль от Google, проходит 2FA (если настроено).

  4. Google показывает экран согласия: "Приложение 'XXXXX' запрашивает доступ к вашему имени и адресу электронной почты. Разрешить?"

  5. Пользователь нажимает "Разрешить".

  6. Google перенаправляет пользователя обратно в наше приложение, прикрепив к редиректу authorization_code.

  7. Наше приложение (его бэкенд) обменивает этот код на access_token и refresh_token, сделав прямой запрос к API Google.

Этап 2:

  1. Клиент выбирает OAUTHBEARER и отправляет auth-станзу. Внутри, в base64, он передает access_token.

    n,a=user@example.com,^Aauth=Bearer a_very_long_token^A^A

<auth xmlns='urn:ietf:params:xml:ns:xmpp-sasl'
      mechanism='OAUTHBEARER'>
bixhPWhhYnJfcmVhZGVyQGhhYnIuY29tLAFhdXRoPUJlYXJlciB0aGVyZSBzaG91bGQgaGF2ZSBiZWVuIHRva2VuLCBidXQgaXQgd291bGQgbm90IGJlOiBEAQA=
</auth>
  1. Сервер получает auth-станзу, декодирует строку и извлекает access_token.

  2. Сервер делает запрос к API Google и прикрепляет токен клиента

  3. Сервер получает ответ от API и понимает, что этот токен валиден и был выпущен для нужного пользователя

  4. Сервер отправляет клиенту <success/>

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

Итог

Что в итоге? Провели ресерч, выяснили, что устаревшие технологии использовать небезопасно, что технологии, который на первый взгляд небезопасны(PLAIN) можно усовершенствовать и сделать их безопасными. А можно взять лучшее из лучших и использовать лучшие способы защиты пароля. Вот так начиная с CTF-задачки, перешли к современной криптографии и механизмам защиты. Если эта статья кому-то помогла лучше понять механизмы аутентификации SASL или то, как оно реализовано в XMPP, буду безмерно счастлив, ведь это моя первая статья :D

P.S. Если есть какие-то предложения по улучшению этой статьи или будущих статей, пишите, на все постараюсь ответить

Комментарии (4)


  1. David_Osipov
    23.06.2025 10:03

    Поздравляю с первой статьёй. Как фидбек - попробуйте объяснения работы алгоритмов делать более понятными для не криптографов и киберсек. спецов.

    Например, чтобы понять механизм работы, мне пришлось RFC скормить Gemini и попросить пошагово объяснить. Вот что вышло для SCRAM-SHA-256-PLUS (луддитам не читать спойлер - там богомерзкое объяснение от ЛЛМ):

    Скрытый текст

    Исходные данные: Состояние перед аутентификацией

    • Клиент: Знает username (имя пользователя) и password (пароль). Больше у него ничего нет.

    • Сервер: НЕ знает пароль пользователя. В его базе данных для каждого username хранится запись с четырьмя ключевыми, заранее вычисленными элементами:

      1. Salt (Соль): Уникальная случайная строка, сгенерированная при создании учётной записи.

      2. IterationCount (Число итераций): Число (например, 4096, но для защиты от офлайн-атак рекомендуется 600 000+). Это "фактор сложности".

      3. StoredKey: Хеш ключа клиента. Конкретно, StoredKey = H(ClientKey).

      4. ServerKey: Ключ, который сервер будет использовать для создания собственных подписей.

      Ключи StoredKey и ServerKey вычисляются из SaltedPassword (пароль + соль) только один раз — при регистрации пользователя или смене пароля. Сервер хранит эти готовые ключи, чтобы избежать дорогостоящих вычислений при каждом входе в систему.

    Криптографические функции, которые мы будем использовать:

    • H(): Хеш-функция SHA-256.

    • HMAC(key, data): Функция HMAC-SHA-256.

    • Hi(): Функция PBKDF2-HMAC-SHA-256, которая представляет собой HMAC, повторённый IterationCount раз. Это "медленная" часть.

    • XOR: Операция "побитовое исключающее ИЛИ".

    Полный "танец" аутентификации: Шаг за шагом

    Вот весь процесс, сообщение за сообщением.

    Шаг 1: Первое сообщение клиента (Начало диалога)

    Клиент инициирует диалог, не раскрывая никакой конфиденциальной информации.

    Клиент -> Сервер: n,,n=user,r=rOprNGfwEbeRWgbNEkqO

    Что происходит "под капотом":

    1. Генерация Nonce клиента (c-nonce): Клиент создаёт криптографически случайную строку (r=...), уникальную для каждой попытки аутентификации.

    2. Форматирование сообщения:

      • n=user: Имя пользователя.

      • n,,: Заголовок gs2-header. В примере из RFC n означает, что клиент не поддерживает привязку к каналу, но для -PLUS обмена здесь было бы y,,. Первая запятая означает, что клиент поддерживает привязку к каналу, вторая — что данные привязки будут отправлены позже.

      • r=...: Уникальное nonce-значение клиента.

    Чего достигает этот шаг: Сигнализирует о начале аутентификации, идентифицирует пользователя и предоставляет уникальное значение для предотвращения атак повторного воспроизведения со стороны сервера.

    Шаг 2: Первое сообщение сервера (Вызов)

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

    Сервер -> Клиент: r=rOprNGfwEbeRWgbNEkqO%hvYDpWUa2RaTCAfuxFIlj)hNlF$k0,s=W22ZaJ0SNY7soEsUEjb6gQ==,i=4096

    Что происходит "под капотом":

    1. Поиск пользователя: Сервер находит запись для user. Если пользователь не найден, сервер может сгенерировать поддельные данные, чтобы не раскрывать факт существования или отсутствия пользователя.

    2. Генерация и объединение Nonce: Сервер генерирует собственное nonce-значение (s-nonce) и добавляет его в конец nonce-значения клиента. Полученный r=... теперь содержит случайность от обеих сторон.

    3. Извлечение публичных данных: Сервер извлекает из своей базы данных Salt (s=...) и IterationCount (i=...) для данного пользователя.

    4. Сборка и отправка: Сервер отправляет объединённое nonce-значение, соль и число итераций.

    Чего достигает этот шаг: Бросает клиенту вызов (challenge) доказать знание пароля, используя предоставленные параметры, и гарантирует уникальность сессии.

    Шаг 3: Финальное сообщение клиента (Тяжёлая работа и доказательство)

    Это самый сложный шаг, на котором клиент выполняет всю основную криптографическую работу.

    Клиент -> Сервер: c=biws,r=rOprNGfwEbeRWgbNEkqO%hvYDpWUa2RaTCAfuxFIlj)hNlF$k0,p=dHzbZapWIk4jUhN+Ute9ytag9zjfMHgsqmmiz7AndVQ=

    Что происходит "под капотом" (по порядку):

    1. Вычисление SaltedPassword ("медленная" часть): Клиент выполняет ресурсоёмкую функцию PBKDF2:

      • SaltedPassword = Hi(password, salt, iterationCount)

      • Это тот самый шаг, который делает офлайн-атаки на украденную базу данных очень медленными и дорогими.

    2. Вывод специализированных ключей (Криптографическая гигиена): Клиент использует SaltedPassword как мастер-ключ для создания двух разных ключей для разных целей. Для этого используются публичные, стандартные строки-константы, определённые в спецификации SCRAM.

      • ClientKey = HMAC(SaltedPassword, "Client Key")

      • ServerKey = HMAC(SaltedPassword, "Server Key")

      • Теперь у клиента есть два ключа: ClientKey для создания своего доказательства и ServerKey для последующей проверки сервера.

    3. Получение данных привязки к каналу (часть -PLUS): Клиент запрашивает у своего TLS-уровня уникальный "отпечаток" соединения (channel-binding-data) и форматирует его в часть сообщения c=biws.

    4. Формирование Auth-Message: Клиент создаёт единую строку, представляющую всю историю диалога:

      • Auth-Message = client-first-message-bare + "," + server-first-message + "," + client-final-message-without-proof

      • Эта строка уникальна благодаря nonce и привязана к TLS-каналу благодаря данным привязки.

    5. Создание ClientProof (Доказательство клиента):

      • StoredKey = H(ClientKey) (Клиент вычисляет это значение "на лету"; ему не нужно его хранить)

      • ClientSignature = HMAC(StoredKey, Auth-Message)

      • ClientProof = ClientKey XOR ClientSignature

    6. Сборка и отправка: Клиент кодирует ClientProof в base64 и отправляет его как p=....

    Чего достигает этот шаг: Клиент доказывает, что он вычислил правильный ClientKey (и, следовательно, знает пароль), не отправляя его. Доказательство криптографически привязано к данной сессии (через nonce) и к данному TLS-соединению (через привязку к каналу).

    Шаг 4: Финальное сообщение сервера (Проверка и взаимное доказательство)

    Сервер проверяет доказательство клиента и, если оно верно, доказывает собственную подлинность.

    Сервер -> Клиент: v=6rriTRBi23WpRR/wtup+mMhUZUn/dB5nLTJRsjl95G4=

    Что происходит "под капотом":

    1. Разбор и проверка: Сервер проверяет nonce и получает данные привязки к каналу со своей стороны TLS-соединения. Это момент истины для -PLUS: если есть "человек посередине", отпечатки каналов не совпадут.

    2. Воссоздание Auth-Message: Он реконструирует в точности ту же строку Auth-Message, что и клиент.

    3. Восстановление ClientKey и проверка доказательства:

      • Сервер извлекает из базы данных StoredKey для этого пользователя.

      • Он вычисляет, какой должна быть подпись клиента: ClientSignature = HMAC(StoredKey, Auth-Message).

      • Он восстанавливает ключ клиента из доказательства: ClientKey_from_client = ClientProof XOR ClientSignature.

      • Он проверяет ключ: H(ClientKey_from_client) ДОЛЖЕН быть равен сохранённому StoredKey. Если совпадает, клиент аутентифицирован.

    4. Создание ServerSignature ("эффективная" часть):

      • Сервер извлекает из своей базы данных заранее вычисленный ServerKey для этого пользователя. (Никаких дорогостоящих вычислений здесь не требуется).

      • Он подписывает диалог: ServerSignature = HMAC(ServerKey, Auth-Message).

    5. Сборка и отправка: Сервер кодирует ServerSignature в base64 и отправляет как верификатор v=....

    Чего достигает этот шаг: Эффективно проверяет подлинность клиента и предоставляет доказательство собственной подлинности, завершая взаимную аутентификацию.

    Шаг 5: Финальная проверка клиента

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

    Что происходит "под капотом":

    1. Вычисление ожидаемой ServerSignature: Клиент использует ServerKey, вычисленный на Шаге 3, чтобы рассчитать, какой должна быть подпись сервера: ExpectedServerSignature = HMAC(ServerKey, Auth-Message).

    2. Сравнение: Он сравнивает свою вычисленную подпись со значением v=..., полученным от сервера.

    3. Успех или провал: Если они совпадают, сервер подлинный. Если нет, клиент ОБЯЗАН немедленно разорвать соединение.

    Чего достигает этот шаг: Защищает клиента от атаки "человек посередине" (Man-in-the-Middle), выполняя обещание взаимной аутентификации.


    А тут объяснение (тоже от ЛЛМ) как защищает от MiTM -PLUS:

    Скрытый текст

    Сценарий атаки MITM (без -PLUS)

    Представим классическую атаку MITM:

    1. Клиент -> Злоумышленник: Клиент думает, что подключается к Серверу, но на самом деле устанавливает идеальное, зашифрованное TLS-соединение со Злоумышленником. Назовём это Канал А.

    2. Злоумышленник -> Сервер: Злоумышленник, притворяясь Клиентом, устанавливает второе, такое же идеальное TLS-соединение с настоящим Сервером. Назовём это Канал Б.

    Теперь Злоумышленник сидит "посередине" и просто пересылает сообщения SCRAM туда-сюда:

    • Клиент отправляет client-first-message по Каналу А.

    • Злоумышленник расшифровывает, видит его и пересылает по Каналу Б на Сервер.

    • Сервер отвечает server-first-message по Каналу Б.

    • Злоумышленник пересылает его по Каналу А Клиенту.

    • Клиент вычисляет ClientProof и отправляет его.

    • Злоумышленник пересылает его на Сервер.

    Результат: Сервер успешно проверяет ClientProof. Он считает, что у него установлено защищённое соединение с Клиентом. Но на самом деле он общается со Злоумышленником. Атака удалась.

    Как -PLUS (Привязка к каналу) ломает эту атаку

    Вот где собака зарыта. Привязка к каналу заставляет аутентификацию SCRAM "знать" о том TLS-канале, внутри которого она происходит.

    Давайте пройдём по тому же сценарию, но теперь с SCRAM-SHA-256-PLUS.

    1. У нас та же ситуация: Клиент <-> Канал А <-> Злоумышленник <-> Канал Б <-> Сервер.

    2. Шаг 3 (Клиент): Когда Клиент готовится создать своё доказательство (ClientProof), он делает дополнительное действие:

      • Он обращается к своему TLS-уровню и просит: "Дай мне уникальный криптографический отпечаток именно этого TLS-соединения". Этот отпечаток (channel-binding-data) вычисляется на основе секретов, которыми обменялись стороны во время TLS-рукопожатия.

      • Для Клиента это будет отпечаток Канала А.

      • Клиент включает этот отпечаток Канала А в строку Auth-Message, которую он подписывает.

    3. Теперь ClientProof криптографически зависит не только от пароля и nonce, но и от уникальной идентичности Канала А. Клиент отправляет это доказательство.

    4. Злоумышленник, как и раньше, перехватывает это сообщение и пересылает его по Каналу Б на Сервер.

    5. Шаг 4 (Сервер): Момент истины.

      • Сервер получает ClientProof. Чтобы его проверить, он должен сам вычислить, каким это доказательство должно быть.

      • Он, так же как и клиент, обращается к своему TLS-уровню и просит: "Дай мне уникальный отпечаток этого TLS-соединения".

      • Но для Сервера это соединение — Канал Б!

      • Поскольку TLS-рукопожатия для Канала А (Клиент <-> Злоумышленник) и Канала Б (Злоумышленник <-> Сервер) были двумя совершенно разными, независимыми событиями, их криптографические отпечатки гарантированно будут разными.

      • Сервер вычисляет ожидаемое доказательство, используя отпечаток Канала Б.

    6. Провал проверки:

      • Доказательство, полученное от Клиента, было подписано с использованием отпечатка Канала А.

      • Доказательство, которое ожидает увидеть Сервер, должно быть подписано с использованием отпечатка Канала Б.

      • Эти два значения не совпадают. Проверка проваливается. Сервер отклоняет попытку входа.

    Результат: Атака полностью провалилась.

    Аналогия

    Представьте, что вы и ваш друг договорились о секретном рукопожатии (это SCRAM).

    • Без -PLUS: Вы делаете рукопожатие с мошенником. Мошенник в точности повторяет его с вашим другом. Друг отвечает, мошенник повторяет ответ вам. Всё выглядит нормально.

    • С -PLUS: Ваше новое рукопожатие включает дополнительный шаг: вы должны вместе указать на уникальную трещину на полу в той комнате, где вы находитесь (это отпечаток канала).

      • Вы находитесь в Комнате А с мошенником и указываете на трещину в ней.

      • Мошенник пытается передать это вашему другу, который находится в Комнате Б.

      • Ваш друг смотрит на пол в своей комнате и видит другую трещину (или её отсутствие). Он мгновенно понимает, что вы не в одной комнате, и что-то не так.

    Итог: Механизм -PLUS не просто защищает данные внутри канала. Он криптографически "приваривает" саму аутентификацию к конечным точкам этого конкретного канала, делая невозможной атаку с "прозрачной" ретрансляцией. Он защищает именно от MITM-атаки, запущенной с самого начала.


    1. gamebun Автор
      23.06.2025 10:03

      Спасибо. Насчет объяснения алгоритмов нужно подумать над тем, как бы не переборщить и не превратить объяснение в отдельную статью. А так, подумаю, как можно реализовать объяснение непогруженным людям.


  1. Knkplua
    23.06.2025 10:03

    А есть еще OTR (который уже признан устаревшим, хотя вполне эффективен) и OMEMO.


    1. MikalaiR
      23.06.2025 10:03

      Это алгоритмы шифрования сообщений, а не аутентификации.