[Перевод] Проверяем невероятные заявления разработчиков приложения-мессенджера с шифрованием
Недавно я услышал в подкасте следующее:
Я использую приложение Converso, потому что забочусь о безопасности, а все другие приложения, которые утверждают, что только о безопасности и думают, на его фоне выглядят как АНБ. С Converso можно рассчитывать на сквозное шифрование, на то, что сообщения не будут храниться на сервере, на отсутствие пользовательских данных и метаданных…
Отсутствие метаданных? Это крайне смелое и любопытное обещание. Даже у Signal, который считается золотым стандартом шифрования сообщений со своими компонентами Double Ratchet и X3DH, всё равно утекает куча метаданных.
На вебсайте компании и в заказных интервью (фрагмент одного из них можно послушать здесь) есть и другие грандиозные заявления, так что мне стало интересно, как же разработчики претворяют в жизнь всё, что обещают.
Мне казалось само собой разумеющимся, что приложения со сквозным шифрованием никак не могут устранить потребность в неком посреднике, который примет зашифрованное сообщение от одной стороны и передаст его другой –, а этот процесс неизбежно сопровождается появлением метаданных, например информации о том, с кем ты говорил и когда это произошло. Однако, по словам создателей Converso, у них сообщения идут «в обход» серверов и не оставляют следов.
Насколько я знаю, единственный способ вывести из сценария посредника — перейти с модели 'клиент-сервер' на пиринговую модель 'клиент-клиент', но это сопряжено с целым рядом проблем:
- Отправитель и получатель должны одновременно быть онлайн. Отправка сообщения в офлайн не сработает –, а ведь возможность асинхронного обмена сообщениями с пользователем, который не в сети, считается базовым требованием к современным приложениям-чатам.
- Двум сторонам нужно каким-то образом установить прямое соединение друг с другом, а они, предположительно, обе сидят на NAT-роутерах. Да и вообще, как они найдут друг друга по IP-адресам, начнем с этого? Есть, конечно, пробивка отверстий, но и с ней приходится полагаться на третью сторону для посредничества при соединении.
К сожалению, код у Converso не в открытом доступе, а на сайте ни слова не говорится о криптографических примитивах и протоколах, что очень нехарактерно для приложения с «самыми передовыми» методами защиты, по их же собственным словам. Для сравнения, Signal, WhatsApp и Telegram предоставили общественности подробные объяснения технической реализации своих систем сквозного шифрования, причем последние были официально протестированы сторонними экспертами и получили от них оценку. Создатели Converso же утверждают, что ждут получения патента, а потом уж сделают код открытым.
Значит, остается один способ ознакомиться с внутренними процессами — обратное программирование или декомпиляция. И вот я заглянул в Android-приложение Converso, чтобы проверить утверждения компании и в общих чертах сравнить ее оригинальный протокол шифрования с уже существующими, которые сейчас используются в сервисах для обмена зашифрованными сообщениями типа Signal.
![sumj8ofhvdei-jrtrqmz05wygz0.png](https://habrastorage.org/webt/su/mj/8o/sumj8ofhvdei-jrtrqmz05wygz0.png)
![paqiigzig4c9dca3yeokegcbs3s.png](https://habrastorage.org/webt/pa/qi/ig/paqiigzig4c9dca3yeokegcbs3s.png)
Скриншоты страниц conversoapp.com/, conversoapp.com/about-us/
Забираем APK
Прежде чем открывать приложение, я решил просмотреть его содержимое. Скачал APK и заглянул внутрь.
К счастью, приложение написано на JavaScript (React Native). В файле index.android.bundle содержится весь код приложения и большинства зависимостей — он подвергся минификации, но всё равно читабелен.
![itimw7vwglwucb-vdkvscfib67w.png](https://habrastorage.org/webt/it/im/w7/itimw7vwglwucb-vdkvscfib67w.png)
Содержимое пакета Converso
Для начала запустим несколько поисков. Посмотрим, какие домены упоминаются в коде.
![zfnxis4bcpjigjqwh9aef0sxv-4.png](https://habrastorage.org/webt/zf/nx/is/zfnxis4bcpjigjqwh9aef0sxv-4.png)
Первое, что я заметил: они включили measurementId в файл конфигурации Firebase — это необязательное поле, которое разработчик может включить, чтобы запустить отслеживание в Google Analytics. Само по себе это не плохо, но таким вещам явно не место в приложении, которое клянется «не использовать абсолютно никаких пользовательских данных».
Затем я увидел еще одно интересное доменное имя — zekeseo.com. Похоже, это другой сайт создателя Converso, на нем предлагаются услуги SEO и маркетинга. Странно, что он здесь возник.
![_-n1yvdotfwehjajw3oaedsmhbg.png](https://habrastorage.org/webt/_-/n1/yv/_-n1yvdotfwehjajw3oaedsmhbg.png)
Если вкратце, код сообщает нам, что имена пользователей, в которых не содержится символа @, будут преобразовываться приложением в почтовые адреса путем прибавления @zekeseo.com. Полагаю, что-то в приложение — бэкенд, наверное — требует, чтобы имена пользователей имели формат почтового адреса, но во фронтенде ничего такого нет.
Далее, упоминается URL на Pixabay. Это, судя по всему, резервный URL для картинки в пользовательском профиле. Не уверен, почему выбор сделан в пользу внешнего ресурса, по-моему, это ошибка.
![fh5z1ebfpj4blz36oisg0slao_g.png](https://habrastorage.org/webt/fh/5z/1e/fh5z1ebfpj4blz36oisg0slao_g.png)
![pprtwksgjn-9yvrbrpwcgoyb3si.png](https://habrastorage.org/webt/pp/rt/wk/pprtwksgjn-9yvrbrpwcgoyb3si.png)
Картинка по умолчанию для пользовательского профиля, которая скачивается с cdn.pixabay.com
Ищем шифрование в коде
Когда я стал искать криптографические примитивы, то обнаружил упоминания AES и RSA. Я ожидал увидеть эллиптическую криптографию, просто потому что не встречал современных протоколов шифрования, которые использовали бы какую-то другую.
Так или иначе, похоже, сообщения действительно шифруются, как очевидно из названий функций encryptMessage и decryptMessage. Уже неплохо, однако нет гарантии, что это шифрование что-то реально дает. Каким образом шифруются сообщения, как генерируются и распространяются ключи шифрования, как часто меняются ключи, как проходит аутентификация для ключей и сообщений и как зашифрованные сообщения попадают от отправителя к получателю?
Проглядев код в окрестностях вышеупомянутых функций для шифрования, я увидел упоминания Seald и API, хранящегося на субдомене seald.io.
![taebrxid9wgcab8ay5r9v6kmwzk.png](https://habrastorage.org/webt/ta/eb/rx/taebrxid9wgcab8ay5r9v6kmwzk.png)
Достаточно пробежать глазами домашнюю страницу Seald, чтобы получить ответы на многие вопросы. Seald — это готовая SDK для разработчиков, позволяющая интегрировать сквозное шифрование «в любое приложение за считанные минуты».
Теперь всё ясно: разработчики Converso не создали никакого новаторского протокола шифрования, они просто внедрили этот SDK. Все компоненты приложения, связанные с шифрованием, приложение препоручает Seald.
![mpiyzausq9mxuuvuwy2lltdxe0e.png](https://habrastorage.org/webt/mp/iy/za/mpiyzausq9mxuuvuwy2lltdxe0e.png)
Домашняя страница Seald
Но это всё-таки не объясняет, как же Converso и, продолжая цепочку, Seald удается передавать сообщения, не сохраняя их на сервере и не собирая метаданные? Неужели SDK Seald действительно обеспечивает Converso возможность проделывать всё, что они обещают?
К счастью, ответ на этот вопрос можно найти в документации от разработчика Seald. Копаться в исходном коде Converso больше нет необходимости.
Принцип шифрования в Converso: что заявлено и что есть
Вот что происходит, когда отправляешь сообщение другому пользователю на Converso:
- Отправитель берет открытый RSA-ключ (Pk), связанный с телефонным номером получателя, с сервера Seald и считает его авторитетным.
- Отправитель шифрует сообщение (M) на AES-256-CBC, используя эфемерный симметричный ключ шифрования (K).
- Отправитель шифрует K на RSAES-OAEP так, что только владелец Pk может его расшифровать.
- Отправитель конструирует MAC для аутентификации сообщения (Mac) при помощи HMAC-SHA-256.
- Отправитель пересылает Mac открытым текстом, а также зашифрованные копии M и K, на сервер через сетевой запрос. Позже сервер доставляет то, что ему переслали, получателю.
Вот, собственно, и всё. Нет никаких пиринговых сетей — приложение использует классическую архитектуру типа 'клиент-сервер'.
Теперь, когда мы разобрались, как работает протокол шифрования в Converso, мы можем пройтись по заявлениям, которые делаются у них на сайте, и проверить, насколько они соответствуют реальности.
Примечание от 13.05.2023: с момента публикации статьи многие из этих заявлений исчезли из сети. Ниже мы приводим дословные цитаты, которые ранее содержались на сайте Converso и в официальных маркетинговых материалах.
Заявление: «Самые передовые инструменты шифрования»
Вердикт: Ложь
![fduwvzwomwxukyq2f2oz9hya9s8.png](https://habrastorage.org/webt/fd/uw/vz/fduwvzwomwxukyq2f2oz9hya9s8.png)
Скриншот страницы conversoapp.com/about-us/
Чтобы стандарт шифрования можно было отнести к «самым передовым», он должен по меньшей мере охватывать все возможности современных протоколов шифрования. Converso использует нехитрый и немолодой RSA, один из старейших доступных стандартов шифрования.
На первый взгляд, генерация случайных чисел выглядит нормально. В качестве источника генерации Converso, по всей видимости, обращается к java.security.SecureRandom на Android-устройствах и к CSPRNG от Apple — на iOS.
Далее, как Converso выстраивает защиту от атаки посредника? Именно она дает гарантии, что вы говорите с тем, с кем предполагаете, а не с имитатором или посредником. Converso полагается в этом на милость третьей стороны, серверов Seald, и отдает ей роль единственного центра сертификации, который соотносит идентичности с открытыми ключами. Третья сторона получает неограниченные возможности выдавать кого угодно за кого угодно. Ничего похожего на Safety numbers, что давало бы уверенность в неприкосновенности зашифрованных сообщений в Converso нет — в приложении отсутствует функциональность, которая позволяла бы взглянуть на октрытый ключ собеседника, не предусмотрено и оповещений, если этот ключ вдруг изменится. Это серьезное упущение.
Примечание от 17.05.2023: хочу внести ясность — я никоим образом не ставлю под вопрос безопасность Seald. Seald не повторяет обещаний Converso касательно протокола сквозного шифрования. Протоколы Seald, которые, к слову, хорошо задокументированы, представляются отличным инструментом для многих сценариев — вот только для приложения-мессенджера со сквозным шифрованием, которое сравнивает себя с Signal, они не подходят. К тому же некоторые из ошибок, как например отсутствие защиты от атак посредника, происходят от неумелого внедрения SDK.
Теперь, подлинность сообщений и аутентификация. Откуда возьмется уверенность, что сообщение не было подделано в пути и как убедиться, что оно действительно пришло от отправителя? Раз уж открытые ключи ненадежны, ни о какой осмысленной идентификации сообщений не может идти речи.
Прямая секретность? Нет такого. В отличие от зарекомендовавших себя сервисов по обмену зашифрованными сообщениями, на которые равняется Converso (Signal, WhatsApp, Telegram и Viber), если устройство пользователя Converso окажется скомпрометировано, сведущий злоумышленник сможет использовать ключи, которые на нем хранятся, для дешифровки предшествующих сообщений, в том числе и тех, которые были удалены с устройства. Ключи ассиметричного шифрования в Seald по умолчанию хранятся три года (для сравнения, в протоколе Signal они меняются после каждого сообщения).
Безопасность после компрометирования? У современных мессенджеров с шифрованием есть способность к самоизлечению, которая не позволяет злоумышленнику дешифровать будущие сообщения, после того как устройство было скомпрометировано. Converso даже не пытается.
Итого:
![bf28fznvjqsq6kkrclgnyqgkf_i.png](https://habrastorage.org/webt/bf/28/fz/bf28fznvjqsq6kkrclgnyqgkf_i.png)
Заявление: «Никаких серверов», «никакого хранения сообщений на серверах»
Вердикт: Ложь
![-doblhuhaauwbweexq2fnt0nuzm.png](https://habrastorage.org/webt/-d/ob/lh/-doblhuhaauwbweexq2fnt0nuzm.png)
Сообщения и ключи передаются на сервер и доставляются им. Само собой, сервера в этой схеме есть.
Заявление: «Абсолютно никакого использования пользовательских данных», «Никакого трекинга», «При использовании Converso ваши данные не сохраняются на серверах или где-то еще»
Вердикт: Ложь
![lfb7vzopae1_uz0y0itztaetti8.png](https://habrastorage.org/webt/lf/b7/vz/lfb7vzopae1_uz0y0itztaetti8.png)
Скриншот страницы conversoapp.com/faqs/
Converso не только внедряет трэкер Google Analytics, чтобы записывать, что вы делаете в приложении, но и собирает телефонные номера для каждого аккаунта, плюс еще метаданные, которые неизбежно сопровождают отправку или получение любого сообщения или ключа. Все эти данные хранятся на серверах.
Кроме того, предположительно в результате ошибки разработчика, каждый пользователь Converso отправляет HTTP-запрос на cdn.pixabay.com, чтобы скачать вот эту картинку для профиля, которая устанавливается по умолчанию. В политике конфиденциальности Pixabay говорится, что они записывают эти запросы — вместе с IP-адресами и информацией об устройствах.
Утверждение разработчиков Converso о том, что сообщения не оставляют «никаких метаданных» ошибочно со всех сторон, как и нижеприведенные частные заявления.
«Откуда вы выходите [остается скрытым]»
При использовании приложения ваш IP-адрес, а также местонахождение и информация об устройстве передаются Seald, Google Analytics и Pixabay.
«Кто вы [остается скрытым]»
При регистрации требуется указать номер телефона, и он сохраняется на сервере.
«С кем вы общаетесь [остается скрытым]»
Сообщения адресуются по номерам телефонов и доставляются через сервер. Серверу нужно знать получателя каждого сообщения, чтобы он мог направить последнее на нужное устройство. Он в курсе, с кем вы общаетесь и когда.
Заявление: «Сквозное шифрование платформы невозможно обойти», «Каждое сообщение защищено сквозным шифрованием, то есть прочитать его может только тот, кому оно предназначено»
Вердикт: Ложь
Как видно из вышесказанного, протокол шифрования у Converso устаревший и уязвимый для различных атак.
Так как ключи асимметричного шифрования совершенно не надежны, Converso не может гарантировать безопасность тем, кто его использует. Протокол шифрования Converso полагается на то, что его стороннее доверенное лицо-посредник всегда будет поступать порядочно.
Заявление: «Так совпало, что Signal основало то же лицо, которое стояло у истоков WhatsApp»
Вердикт: Ложь
![mvhjkerw7rtpl9jq-t2v8ir5bna.png](https://habrastorage.org/webt/mv/hj/ke/mvhjkerw7rtpl9jq-t2v8ir5bna.png)
Скриншот страницы converso.com
Сооснователь WhatsApp действительно внес свой вклад в основание и деятельность фонда, который сейчас занимается разработкой Signal, но заниматься ей он стал спустя восемь лет после зарождения компании. Этот фонд и Signal — разные сущности.
Signal в 2014 году выпустила компания Open Whisper Systems под руководством Мокси Марлинспайка, но история этого продукта тянется еще с 2010 года; ранее он был известен как TextSecure и RedPhone. В 2018 году Брайан Эктон, сооснователь WhatsApp, помогал с запуском Signal Technology Foundation, некоммерческой организации с миссией «поддерживать, ускорять и расширять миссию Signal по повышению доступности и всеохватности приватного общения».
Заявление: «WhatsApp, Telegram и Viber хранят сообщения (в читаемой форме) на сервере»
Вердикт: Ложь или сильное искажение истины
![8beutenkpk4au85d-ibfs-nhetm.png](https://habrastorage.org/webt/8b/eu/te/8beutenkpk4au85d-ibfs-nhetm.png)
Скриншот страницы conversoapp.com/converso-security/
И у WhatsApp, и у Viber внедрен протокол Signal. Зашифрованные беседы в Telegram используют MTProto. Оба этих протокола отлично известны, хорошо задокументированы и прошли официальную оценку сторонних экспертов.
Зашифрованные сообщения не хранятся в читаемом формате на серверах WhatsApp, Telegram и Viber. (В Telegram сквозное шифрование — опция, которую можно подключить, простые, незашифрованные сообщения доступны с сервера).
В другом тексте Converso утверждает, что WhatsApp «генерирует незашифрованные бэкапы чатов на Google Cloud или iCloud», однако это не совсем так. Бэкапы у WhatsApp создаются по желанию пользователя и могут быть защищены сквозным шифрованием (хотя это пока еще не происходит по умолчанию).
Заявление: «Любая беседа, которая разворачивается на нашей платформе, является частью децентрализованной архитектуры»
Вердикт: Ложь
Converso использует центральные серверы для передачи ключей и сообщений. Децентрализованная архитектура в Converso не применяется — его создатели опираются на традиционную централизованную модель «клиент-сервер».
Заявление: »[Signal] опирается на Amazon S3 при распространении блокчейн-данных»
Вердикт: Ложь или сильное искажение истины
В Signal предусмотрена поддержка одноранговых транзакций через MobileCoin, частную криптовалюту, но эту функциональность никому не навязывают и она не пользуется популярностью. Простые сообщения со сквозным шифрованием в Signal никак не связаны с MobileCoin или любыми другими блокчейн-данными.
Обобщая сказанное
Из этого всего можно в очередной раз сделать вывод: не всё, что пишут в интернете, соответствует действительности. Converso создает ложный образ своего продукта как мессенджера с самыми передовыми технологиями сквозного шифрования, что очень и очень далеко от истины. Правда такова: неимоверные заявления, которые команда Converso делает у себя на сайте (обещания полной безопасности в приложении и камни в огород инструментов шифрования высшего уровня), ложны, и это несложно доказать. Таким образом, я считаю, что полагаться на Converso в вопросах безопасности определенно не стоит и тем более не стоит платить им 4,95 $ в месяц.
![n1wg1evavgjr8igl0bdkxfdc8tm.png](https://habrastorage.org/webt/n1/wg/1e/n1wg1evavgjr8igl0bdkxfdc8tm.png)
Скриншот страницы conversoapp.com/download-converso/
Но погодите, всё гораздо хуже
Когда я дописывал текст выше, то заметил в коде нечто странное — прежде это как-то проходило мимо меня. Там оказалась масса упоминаний чего-то напоминающего функции, связанные с базой данных Google Firestore.
Еще до этого я подметил, что для некоторых простых операций, например локального индексирования адресных книг на устройстве, в коде используется SQLite. Я полагал, что при помощи SQLite обрабатываются и другие вещи, скажем, сообщения, и что серверы нужны только для пересылки данных, а не для длительного их хранения. Я ошибался.
![n60-wru9nl2676blbwxjsx3bofw.png](https://habrastorage.org/webt/n6/0-/wr/n60-wru9nl2676blbwxjsx3bofw.png)
Код с SQLite из предыдущих изысканий (кто найдет здесь дополнительную уязвимость, тот молодец)
Похоже на то, что Converso используют фреймворк базы данных Firestore для хранения всевозможных данных из приложения, включая полученные и отправленные сообщения, логии звонков, регистрационные данные пользователей и, возможно, другие категории пользовательского контента.
![y4jrz2nk2jt5qmzaothl66iwwq4.png](https://habrastorage.org/webt/y4/jr/z2/y4jrz2nk2jt5qmzaothl66iwwq4.png)
Меня это поразило и привело в замешательство, так как Firestore — облачная база данных, хостинг которой обеспечивает Google. Однозначно не внутренний, строго офлайн интерфейс базы данных, который ожидаешь увидеть в приложение такого типа. И, судя по всему, Firestore используется для многих данных, которые точно стоило бы держать в офлайне. Я увидел такие названия коллекций и субколлекций Firestore, как users, chats, messages, missedCalls, videoInfo, recents, rooms, fcmTokens, phoneRooms, phoneInfo, usersPublic, loginError, callerCandidates и calleeCandidates.
Но эта база данных Firestore, разумеется, закрыта… ведь закрыта?
От любых баз данных в онлайне ожидаешь, что со стороны сервера установлены правила, ограничивающие доступ, чтобы предотвратить несанкционированное распространение конфиденциальной информации.
Я решил опробовать те учетные данные Firebase, которые ранее нашел в коде, чтобы посмотреть, защищены ли данные должным образом по правилам безопаности Firestore. Одних этих данных не должно быть достаточно для предоставления неограниченного доступа к конфиденциальной информации из базы.
Я написал несколько строк кода, чтобы поглядеть, что получится, если попытаюсь взять данные из коллекции users:
initializeApp({
apiKey: "AIzaSyBBswl_VaCb7h7nIj8xBhreuxj2NH6aqis",
authDomain: "converso-448da.firebaseapp.com",
projectId: "converso-448da",
storageBucket: "converso-448da.appspot.com",
messagingSenderId: "1025894877514",
appId: "1:1025894877514:web:58f4a74a44071f727c19b3"
});
const db = getFirestore();
const querySnapshot = await getDocs(collection(db, "users"));
И вот что я получил:
Небольшой фрагмент из коллекции users
Похоже, я ненароком вторгся в базу данных с пользователями Converso. Коллекция users, открытая всему интернету и публично доступная, содержит регистрационные данные каждого из пользователей приложения. Номера телефонов, время регистрации и идентификаторы групп, к которым пользователи принадлежат (иными словами, кто с кем разговаривает).
Многие другие коллекции баз данных в той же мере открыты всем желающим. Коллекции fcmTokens, loginError, missedCalls, phoneInfo, phoneRooms, rooms, usersPublic и videoPublic не требуют никакой авторизации со стороны сервера для предоставления доступа.
Для тех, кто незнаком с Firestore, эта ошибка практически равнозначна тому, чтобы создать базу данных на SQL, обращенную к интернету, и не задать к ней имени пользователя и пароля — каждый сможет прочитать и написать всё, что захочет.
Метаданные Converso находятся в открытом доступе
Converso не только собирает и сохраняет огромные массивы метаданных, которых якобы не существует вообще, но и помещает эти метаданные в открытый доступ. Когда вы кому-то звоните, информация об этом распространяется на весь белый свет, и любой, кому интересно, может отслеживать ситуацию в реальном времени.
Эти данные в незашифрованном виде хранятся на серверах Google — весьма иронично для компании, которая в своих маркетинговых слоганах восстает против «технических гигантов» (и конкретно против Google).
«Converso создан для тех, кто хочет абсолютной приватности и свободы от любого надзора, будь то со стороны государства или технических гигантов» — conversoapp.com
![bc19-hy4ksdxlcehm3j-ip9edz0.png](https://habrastorage.org/webt/bc/19/-h/bc19-hy4ksdxlcehm3j-ip9edz0.png)
Скриншот страницы conversoapp.com/converso-security/
Изучаем прочие коллекции в Firestore
Коллекция rooms содержит метаданные, касающиеся сессий видеозвонков (для видео- и аудиостримов между пользователями применяется WebRTC).
Небольшой фрагмент коллекции rooms
Подобным же образом в phoneRooms содержатся метаданные к аудиозвонкам, совершенным из приложения.
Небольшой фрагмент коллекции phoneRooms
Коллекция fcmTokens, похоже, содержит длинный список токенов регистрации FCM для каждого пользователя. Это идентификаторы, которые выдаются GCM серверами, чтобы клиенты могли получать уведомления и другие типы внутренних сообщений приложения.
Не уверен, как именно эти токены применяются в Converso, однако в документации Firebase сказано ясно: «регистрационные токены должны оставаться секретными».
Небольшой фрагмент коллекции fcmTokens
Мне не удалось попасть в коллекции chats и messages — видимо, там внедрена некая система разрешений (хоть где-то). Не могу точно сказать, какие там правила безопасности, возможно, разберу их как-нибудь потом. Но вернемся к коду.
База данных онлайн-сообщений Converso
В Converso две разновидности сообщений: сообщения открытым текстом и зашифрованные сообщения. И те, и другие хранятся в коллекции messages на Firestore, которую хостит Google.
Записи выглядят вот так:
{
createdAt: ,
number: "",
message: "",
encryptedMessage: "",
messageContent: "",
tokens: [""],
selfDestruct: , // optional
}
Следующие категории сообщений в Converso никак не шифруются:
- Сообщения-изображения. Это сообщения, у которых isImage: true и прописанное открытым текстом название файла с изображением в поле imageName. Файлы с картинками переводятся в незашифрованный формат при помощи сервиса облачного хранения на Firebase.
- Анимированные изображения. Есть записи сообщений, у которых добавлены поля url и isGif: true. Когда на устройство получателя приходит сообщения такого рода, соответствующее изображение автоматически скачивается без запроса — и отключить это никак нельзя. Это, как представляется, создает очевидную и серьезную уязвимость: любой может узнать IP-адрес какого угодно пользователя Converso, просто отправив сообщение с указанием на URL, который хостит отправитель.
- Запросы на очистку истории в беседе. Это записи открытым текстом с isClear: true.
- Запросы на удаление одного из предыдущих сообщений. Это записи открытым текстом с isDelete: true и полем messageId, в котором указывается, какое сообщение нужно удалить.
- Оповещения о том, что сделан скриншот. Если приложение зафиксирует, что вы делаете скриншот, то создаст запись открытым текстом, где будет прописано «Screenshot taken» («Сделан скриншот») и isScreenshot: true.
Зашифрованные сообщения — их записи в Firestore включают строку encryptedMessage — передаются SDK Seald для дешифровки при помощи функции decryptMessage. Эта функция переводит закодированный на base64 шифротекст в открытый текст способом, описанным выше.
![ggplz-wa08gzunip2uavierv1vo.png](https://habrastorage.org/webt/gg/pl/z-/ggplz-wa08gzunip2uavierv1vo.png)
Присмотримся к «зашифрованным» сообщениям
Внимательно изучив код, связанный с Seald, я обнаружил, что Converso использует модуль Seald @seald-io/sdk-plugin-ssks-password. В документации разработчика говорится, что это позволяет Converso пользоваться «надежным сервисом для хранения ключей» от Seald, который позволяет «просто и надежно сохранять идентичности Seald с шифрованием пользовательским паролем».
Итак, закрытые ключи дублируются на сервера Seald и шифруются там пользовательскими паролями. Если пользователь удалит приложение Converso, то сможет впоследствии восстановить свой суперсекретный RSA-ключ, забрав зашифрованную версию с сервера и дешифровав ее локально при помощи своего пароля. Восстановив ключ, он сможет дешифровать старые сообщения, хранящиеся в базе данных Firestore.
Но тут возникает одна серьезная проблема: в Converso нет никаких паролей. Чтобы создать аккаунт, нужно просто ввести свой номер телефона, а затем — СМС-код для подтверждения. Но если паролей не существует, то чем же тогда шифруются ключи?
![tbcdyev6cf8pgeij3ygtgfzmxck.png](https://habrastorage.org/webt/tb/cd/ye/tbcdyev6cf8pgeij3ygtgfzmxck.png)
В минимизированном JavaScript довольно сложно разобрать, откуда берется переменная пароля (u). Пришло время подключить инструмент, который сделает код чуть более читабельным.
$ npx react-native-decompiler -i ./index.android.bundle -o ./output
Теперь отслеживать переменные по функциям стало проще. Приглядевшись, я вижу, что рассматриваемый код — компонент React Native под названием Seald.
![buk4fklsu5wffpmqjwqw3lrjodw.png](https://habrastorage.org/webt/bu/k4/fk/buk4fklsu5wffpmqjwqw3lrjodw.png)
![74pjmyym4_fg-2hetvjl0eeh8uc.png](https://habrastorage.org/webt/74/pj/my/74pjmyym4_fg-2hetvjl0eeh8uc.png)
Как выясняется, имя пользователя в Seald — его телефонный номер, а пароль — попросту идентификатор пользователя. И это очень плохо. Пароли для дешифрования совпадают с идентификаторами пользователей в Firebase, а идентификаторы находятся в открытом доступе.
Список телефонных номеров и идентификаторов всех пользователей у меня уже есть — скачал, когда работал с коллекцией users. То есть у меня на руках достаточно учетных данных, чтобы скачать и дешифровать все закрытые ключи Converso до единого, а это дает мне возможность дешифровать любое зашифрованное сообщение.
С опорой на официального SDK Seald запущу небольшой скрипт, чтобы проверить свои выводы.
![ylixdp3_lyzhb0futmcedoep_80.png](https://habrastorage.org/webt/yl/ix/dp/ylixdp3_lyzhb0futmcedoep_80.png)
Использую учетные данные Seald из кода приложения, телефонный номер случайного пользователя и идентификатор из публичной базы данных Converso.
$ node test.js
[19:29:17.328 04/05/2023 UTC+10] info :seald-sdk - Seald SDK - Start
[19:29:17.338 04/05/2023 UTC+10] info :goatee - Instantiating Goatee
[19:29:17.341 04/05/2023 UTC+10] info :goatee - Initializing goatee
[19:29:18.993 04/05/2023 UTC+10] info :seald-sdk - Already initialized
[19:29:19.028 04/05/2023 UTC+10] info :goatee - Setting new default user...
[19:29:23.590 04/05/2023 UTC+10] info :goatee/database/models/User - Sigchain updated for user 2yXXXXXXXXXXXLEw. Sigchain matches with db: true
good password!
О нет.
На этом я прекращаю свои изыскания — сейчас я уже в шаге от того, чтобы вторгнуться в чью-то частную жизнь, прочитав сообщение, которое должно быть зашифрованным и личным.
Закрытые ключи тоже в открытом доступе
Для посторонних глаз доступны не только метаданные, но и ключи, которые используются для шифрования сообщений. Кто угодно может скачать закрытый ключ пользователя Converso и с его помощью дешифровать его личные беседы.
Разница между открытым текстом и зашифрованным сообщением стирается — ничего не шифруется так, чтобы в этом был смысл. В интересах собственной безопасности пересылайте при помощи Converso только то, что опубликовали бы и у себя в Твиттере.
Обо всех этих вопиющих уязвимостях я сообщил в Converso, прежде чем опубликовать статью.
05.05.2023: В Converso передана информация об уязвимостях. Подготовлен черновик статьи.
05.05.2023: От Converso пришел ответ: «Спасибо за отклик и за время, которые вы потратили на эту проблему. Я переслал ваше письмо генеральному и техническому директорам, и мы безотлагательно возьмем ее в работу. Вышлем Вам подробный ответ при первой возможности».
05.05.2023: В Converso интересуются: «Как Вам удалось провести декомпиляцию исходного кода приложения и какие меры, по Вашему мнению, следует принять для защиты от таких инцидентов в будущем?».
05.05.2023: Мой ответ: «Ваше приложение написано на React Native. Достаточно переименовать файл APK Converso в файл '.7z', а затем извлечь файл 'assets/index.android.bundle'. В этом файле содержится исходный код Converso и его зависимостей на JavaScript после бандлинга. Защищаться тут не от чего — у других приложений всё работает так же. Кроме того, даже если можно было бы усложнить этот процесс, укреплять безопасность серверной стороны со стороны клиента — ненадежная стратегия».
05.05.2023: В Converso интересуются: «Можно узнать, кто Вы и где находитесь? Спасибо».
06.05.2023: Converso сообщает: «Мы хотели сообщить Вам, что развернули новую версию и ждем, пока билд пройдет модерацию. В дальнейшей работе над обновлениями мы будет учитывать Вашу обратную связь, за которую очень благодарны».
06.05.2023: Apple пропускает новую версию приложения на iOS. В примечаниях к релизу разработчик Converso говорит, что в новой версии «устранены незначительные баги» и «добавлено еще больше улучшенных механизмов защиты нового поколения».
05.05.2023–05–06: Google пропускает новую версию приложения на Android.
09.05.2023: Converso пишет: «Уязвимость с правилами на Firebase устранена, приглашаем Вас протестировать. Другая уязвимость, связанная с предзаданными ключами дешифрования, реализована на нашей стороне, ждем только новых учетных данных, чтобы существующие пользователи прошли повторную идентификацию. Но все существующие сообщения, отправленные со старыми ключами дешифрования, защищены правилами Firebase, так что посторонние всё равно не могут их прочитать».
10.05.2023: Из Converso ко мне снова приходит благодарность за то, что обратил их внимание на уязвимости. Я публикую статью в своем блоге.
11.05.2023 — 12.05.2023: Основатель Converso Таннер Хаас сообщает мне, что он и его юридический отдел недовольны моей статьей и советует ее удалить. Он отправляет мне серию писем с обвинениями в клевете и утверждениями, что я либо «их сотрудник [Signal], либо сам Мокси». В то же время Converso начинает удалять контент с сайта и из маркетинговых материалов, в том числе большую часть ложных или искажающих истину заявлений, которые я цитировал выше.
14.05.2023: Converso публикуют новую статью в блоге на своем сайте; это выглядит как попытка сместить в поисковой выдаче страницу с моей статьей и, возможно, другие, где указывается на пробелы в защите и неверные суждения Converso. В мета-тэге описания присутствует словосочетание «проверяем заявления Converso».
16.05.2023: Приложение Converso, по всей видимости, больше не доступно для скачивания на Google Play Store и iOS App Store. Пока неясно, какова причина и вернется ли приложение на маркеты. Либо Converso умышленно изъято из доступа, либо у маркетов появились к нему претензии.
16.05.2023: В письме ко мне сотрудники Converso подтверждают, что добровольно сняли приложение с продажи в магазинах, пока ведется работа над «усовершенствованиями и устранением недостатков».