Особенности использования сторонних сервисов в мобильных приложениях на примере Firebase
Всем привет!
На связи Веселина Зацепина ( @VeselinaZatsepina ) и Юрий Шабалин, эксперты по безопасности мобильных приложений из компании Стингрей. Сегодня мы затронем очень интересную тему — сервисы Firebase. Поговорим об их применении в мобильных приложениях и о том, как обеспечить их безопасность. Эта статья призвана обратить внимание разработчиков и ИБ-специалистов на внешние сервисы, которые используют приложения, поскольку они часто остаются без должного внимания и аудита. Очень надеемся, что после прочтения вы начнёте по-другому смотреть на безопасность мобильных продуктов, ведь они обмениваются данными не только с собственными серверами, но и с многими другими.
И, конечно, мы попробуем ответить на вопрос: что же может быть страшного в, казалось бы, стандартных и привычных сервисах? Интересно? Тогда начнём!
Что же такое Firebase?
Сегодня практически каждая мобильная разработка включает в себя использование сторонних сервисов, и Firebase, разработанный компанией Google, стал одним из самых популярных. Этот инструмент предлагает обширный набор функциональности: от аутентификации пользователей до аналитики, уведомлений и базы данных. Firebase значительно упрощает жизнь разработчикам, позволяя быстро создавать мощные мобильные приложения и сократить время проектирования инфраструктуры. Но, как это часто бывает, удобство несёт и свои риски.
Firebase — это платформа, разработанная Google, которая предлагает набор инструментов для создания мобильных приложений на iOS, Android, Web, Flutter, Unity и C++. Она позволяет разработчикам быстро создавать и развертывать приложения, используя облачные ресурсы, а также предоставляет решения для анализа и оптимизации работы программ.
Для разработки и запуска приложений Firebase предлагает следующие сервисы:
Сервисы для разработки
Сервисы для запуска
Давайте сосредоточимся на самых популярных и вместе с тем потенциально уязвимых (при неправильном использовании) сервисах:
Firebase Remote Config — облачный сервис, позволяющий изменять поведение и внешний вид клиентского приложения без необходимости его обновления со стороны пользователей. Так, например, можно закрывать определённые функции с помощью «фича-флагов».
Firebase Machine Learning — мобильный SDK для работы с моделями машинного обучения. Можно использовать как свои модели, так и готовые — например, для распознавания лиц на изображениях.
Firebase Authentication — сервис для аутентификации пользователей в приложении, поддерживающий подтверждение с использованием паролей, номеров телефонов, а также через Google, Facebook и другие популярные платформы.
Cloud Storage for Firebase — это мощный, но при этом простой в использовании и экономичный сервис для хранения изображений, аудио, видео или другого пользовательского контента.
Cloud Firestore — аналогичен Cloud Storage for Firebase, также прост в использовании и экономичен.
Firebase и Realtime Database — базы данных, синхронизирующие информацию между клиентскими приложениями в реальном времени. Firestore гибкая и масштабируемая, а Realtime Database хранит данные в формате JSON.
О проблемах с сервисами Firebase
Итак, мы узнали, какие сервисы предоставляет Firebase, и даже внедрили их в наше Production Ready приложение. Всё, кажется, хорошо, но что же может пойти не так?
Для того чтобы показать процесс проверки приложения, использующего сервисы Firebase, мы написали небольшое приложение. Оно поможет вам определить, какие сервисы доступны с заданными параметрами доступа. Для того, чтобы использовать этот проект нам необходимо приложение, которое мы хотим проверить и которое Firebase. Для дальнейшей работы нам необходимо получить из него всю необходимую информацию для проверки.
Чтобы использовать описанные выше сервисы, нам нужно сначала инициализировать Firebase в коде с помощью данных, которые выдаются при создании проекта в его консоли. Это обычный файл формата JSON (google-services.json), из которого нам понадобятся несколько значений.
Для начала найдём и скачаем APK-файл любого приложения из Google Play. Затем воспользуемся инструментом jadx для декомпиляции этого файла и запустим:
$ jadx-gui my_apk.apk
При компиляции данные из google-services.json попадают в консолидированный файл со строковыми ресурсами strings.xml, который нам и нужен:
Файл strings.xml в декомпилированном коде
В нем нам нужно найти значения следующих ключей:
……..
……..
……..
……..
……..
Повторюсь, эти значения нам будут нужны для инициализации Firebase и получения доступа к сервисам.
А теперь, для того чтобы проверить, насколько правильно настроен доступ и ограничения Firebase, мы модифицируем наше приложение и попробуем «достучаться» до каждого из сервисов. То есть, будем пытаться записать произвольные данные в базы данных и файловое хранилище, добавить нового пользователя в Firebase Auth, считать Remote Config и распознать лицо на картинке с помощью Firebase ML.
То есть, алгоритм примерно следующий:
Скачиваем целевое apk и находим нужные строки;
Распаковываем приложение для проверки (например, через apktool)
Заменяем в ресурсах нужные строки на найденные значения;
Собираем приложение для проверки, подписываем;
Запускаем и радуемся.
После последнего пункта (запуск модифицированного приложения), видим следующую картину:
Результат проверки сервисов Firebase
Результаты показывают, что нам удалось удачно распознать лицо на картинке с помощью Firebase ML, а также считать Remote Config. В дальнейшем для получения данных из Remote Config можно просто отправить запрос:
curl -v -X POST "https://firebaseremoteconfig.googleapis.com/v1/projects/{PROJECT_ID}/namespaces/firebase:fetch?key={API_KEY}" -H "Content-Type: application/json" --data '{"appId": "{APP_ID}", "appInstanceId": «PROD"}'
В ответе нам придет список значений, который содержится внутри Remote Config. И вот его уже необходимо тщательно изучить и проверить, что внутри нет никакой конфиденциальной информации.
В случае, если нам также удалось успешно сохранить информацию в базы данных и файловое хранилище, загорятся соответствующие сервисы:
Результат проверки с доступ ко всем сервисам
К сожалению, так просто получить данные из этих сервисов обычным curl-запросом уже не получится. Да и проверить валидность тоже, именно поэтому нам и пришлось разрабатывать приложение.
Хорошо, нам удалось достучаться до сервисов. Но с какими проблемами столкнется компания, если все подряд начнут пользоваться данными из настроек Firebase?
Поскольку практически все сервисы платные, то постоянные обращения к сервису вынудят компанию в итоге оплатить круглую сумму, помимо того, что мы можем «попортить» данные в БД или скачать данные, не предназначенные для пользователя. Мы провели небольшой эксперимент и в цикле начали обращаться к собственному проекту из стороннего приложения. Все, что мы получили — это сообщение в консоли Firebase о том, что мы исчерпали половину запросов, после которого начинается платный тариф. И ушло на это у нас не больше 5 минут:
Уведомление об исчерпании половины запросов к API
А учитывая, что оплачиваем мы счет в конце месяца, может случиться, что о сумме мы узнаем только когда выставят счет (конечно, если никто дополнительно эти показатели не мониторит).
И конечно же, отдельным пунктом хотелось бы отметить хранение внутри приложения серверного ключа от FCM, который, как минимум, позволяет отправлять PUSH-уведомления от имени приложения. Эта атака была известна уже давно и описана в данной статье (к сожалению оригинальная статья с многочисленными подробностями уже не доступна).
С того времени многое изменилось, например, больше нельзя использовать отрицательное условие на вхождение в группу, если такой группы не существует. То есть, если раньше срабатывало условие «пользователь не подписан на топик ASKDJHEWFG», то теперь нужно угадать название топика, прежде чем PUSH уйдет. Но давайте будем честны, во-первых, названия топиков можно предугадать, так как они наверняка будут именоваться «Android» / «iOS», или перебирать их по словарю, или посмотреть в код приложения, куда именно подписывается пользователь. То есть атака до сих пор реальна, но условия эксплуатации усложнились. Да и компания Google обещала запретить использование подобных ключей в Production, но, как всем понятно, не спешит исполнять свои обещания. Так мы свое приложение с подобным ключом загрузили вполне себе без проблем.
Если выразиться более кратко, то основные риски:
Утечка данных через открытые для чтения базы и конфигурации;
Изменение/добавление новых данных записей в сервисы с открытым доступом на запись/модификацию;
Исчерпание ресурсов через запросы к сервисам;
Трата денег компании на оплату счета;
«Бесплатное» использование некоторых сервисов за счет компании;
Использование прямого функционала сервисов для атаки на компанию (рассылка PUSH-уведомлений).
И это только один сервис Firebase, а ведь приложения используют и другие сервисы.
Реальные примеры из жизни
Мы посмотрели на различные нюансы использования Firebase. На самом деле всё выглядит логично и понятно: выставляй себе права на ключи и живи счастливо. Но эта статья не появилась бы на свет, если бы мы не наблюдали катастрофическое количество приложений, которые этим самым правилом пренебрегают. И действительно, до марта этого года все ключи по умолчанию создавались без ограничений. Сейчас же при создании нового ключа по умолчанию выставляется ограничение на использование только из приложения. Но будем честны: при публикации в огромном количестве магазинов приложений для Android, где используются разные ключи для подписи, применять подобные механизмы защиты становится весьма проблематично.
В этом разделе мне бы хотелось собрать самые интересные проблемы, которые мы встречали в Firebase-сервисах в самых различных приложениях. Все нижеприведенные примеры и истории — это реальные приложения, которыми мы с вами пользуемся. И поэтому, если вдруг вы увидите какое-либо сходство с вашим проектом или угадаете, что скрывается за ширмой, пожалуйста, не пишите об этом в комментариях — пусть это будет нашей маленькой тайной.
Серверные ключи FCM/GCM
Да, несмотря на то, что на дворе 2024-й год, часть приложений по-прежнему использует серверные ключи в своих сборках. Как я говорил выше, это может привести к таким интересным последствиям, как, например, отправка произвольных PUSH-уведомлений:
Результат эксплуатации серверного ключа FCM
Хоть атака и стала чуть более сложной из-за ограничений на стороне Google, она по-прежнему реальна. Представим, что от нашего приложения для части пользователей будет отправлено предложение перейти к конкурентам или вообще нецензурно-оскорбительное сообщение. Не самая приятная ситуация, особенно, если это растиражируют в СМИ.
В одном из наших исследований мы выявили около 25 приложений, которые использовали подобные ключи, и, конечно же, связались с разработчиками. Некоторые ответы достойны того, чтобы их показать.
От одних ребят мы получили вот такой ответ:
Здравствуйте. Спасибо, мы в курсе.
Кто-то благодарил и исправлял:
Добрый день.
Спасибо, коллеги уже занимаются проверкой и устранением данной проблемы.
Одно приложение особенно порадовало. Вместо того, чтобы перевыпустить ключ и заменить его на правильный, они просто в коде разделили его на три строки и склеивали при обращению к сервису, чтобы наш инструмент не нашел точного значения. Но кто же знал, что мы еще и в трафик смотрим!
К сожалению, общая тенденция такая: когда ты сообщаешь о проблеме, которая может перерасти в серьезную неприятность, тебя просто не слушают.
Firebase Remote Config
Здесь стоит остановиться подробнее. Как было сказано выше, это облачный сервис, который позволяет вам изменять поведение и внешний вид вашего клиентского приложения или сервера, не требуя от пользователей загрузки обновления (например, так удобно закрывать разные «фичи» приложений с помощью «фича флагов»). По большому счету, это очень удобный механизм для передачи в приложение различной информации. Например, можно не «зашивать» рекламный баннер внутри программы, а передавать ссылку на картинку через Remote Config и с помощью дополнительного флага определять, показывать его или нет. При этом в документации большими буквами указано — пожалуйста, не храните никаких конфиденциальных данных в этом сервисе. Но многие ли обращаются к документации?
Ключи и токены в открытом виде в Remote Config сервисе
Огромное количество Production и Dev-ключей от самых различных сервисов. То есть, их уже можно считать скомпрометированными. Теперь, по-хорошему, их нужно перевыпускать, заменять и проводить прочие самые неприятные операции.
Есть и примеры немного попроще, например, обход логики приложения. В одном из приложений после совершения определенной цепочки действий в конце давалась возможность выбрать себе подарок от партнеров, но только один раз. Но, обратившись к Firebase Remote Config, мы сразу же получаем нужную ссылку:
URL к внешним сервисам в Remote Config сервисе
И если по ней перейти, то сразу попадаем на выбор «подарка»:
Выбор подарка за счет компании
То есть, теперь мы можем выбрать себе пару скидок неограниченное количество раз и все расходы понесет компания. Мелочь, а приятно!
И таких примеров огромное количество. Чего только не хранят в этих Config-ах — от ключей шифрования до токенов доступа к платежным системам:
Ключи для доступа к платежным системам
Так что, если ваше приложение использует этот сервис, внимательно посмотрите, что именно вы в него передаете.
Открытая для записи БД
Во время одного из сканирований наш инструмент нашел открытую для записи базу данных. Она была доступна любому исследователю, который умеет анализировать строки, полученные из файла приложения. И, что интересно, кто-то уже находил эту проблему и даже пробовал ее проэксплуатировать:
Сторонние записи в открытой для записи БД
Давайте пофантазируем, что здесь можно было бы придумать? К примеру, уже сейчас, после принятия закона об оборотных штрафах за утечку ПДн, сгенерировать/получить из любых источников персональные данные, загрузить их в открытую базу для записи. После этого написать «кляузу» на компанию за утечку данных. Крайне неприятная история может получиться, даже если вы сможете доказать, что это не ваши данные, а кто-то другой их загрузил. Это потраченные деньги, время, суд, удар по репутации и прочие издержки. Так что, будьте очень внимательны к тому, какие права доступа вы выдаете на используемые ресурсы.
А есть ли какой-то способ защиты от такого рода атак, запрещающий использовать наши токены и данные, которые, согласно документации, должны храниться в открытом виде в strings.xml. Давайте разбираться!
Способы защиты
Начнем с самого простого — наложение ограничений на Api Key Firebase.
Ключ API — это уникальная строка, которая используется для маршрутизации запросов к вашему проекту Firebase при взаимодействии со службами Firebase и Google. Проект Firebase может иметь множество ключей API, но каждый ключ API может быть связан только с одним проектом Firebase. Все настройки находятся в console.cloud.google.com. После входа в аккаунт вы можете увидеть список ваших ключей:
Список ключей в профиле
Естественно, в зависимости от компании, список может выглядеть иначе. На каждый из этих ключей необходимо наложить ограничения.
Кликнув на любой из ключей, вы переходите в следующее меню:
Меню настроек по ограничению ключей
Где, как раз, вы можете указать, например, с каких IP адресов может использоваться ключ или в каких конкретно приложениях он может быть использован, например так:
Ограничение ключа по приложениям
Помимо этого, вы также можете выбрать API, которые работают с этим ключом.
Кроме ограничений на ключ, нужно будет создать определенные условия для доступа в зависимости от сервиса. Так, для Firestore, Cloud Storage и Realtime Database важно прописать правила, в которых будет указано, кто имеет права на чтение, запись и т.д.
Например, вот такие правила дадут полный доступ к данным:
Полный доступ к данным
А такие уже нет:
Ограничения на доступ
Поэтому будьте внимательны при написании своих собственных правил.
Теперь давайте поговорим о Firebase Authentication. Если вы используете аутентификацию Firebase на основе пароля и ваш ключ API оказался в чужих руках, это не даст злоумышленникам доступ к базам данных вашего проекта или облачному хранилищу, при условии, что эти данные защищены правилами безопасности Firebase. Однако они смогут использовать ваш ключ API для взаимодействия с Firebase Authentication и отправки запросов на аутентификацию.
Чтобы снизить риск злоупотребления ключом API для попыток атак методом перебора, рекомендуется ужесточить квоты на запросы, чтобы они соответствовали нормальному уровню трафика вашего приложения. Важно учитывать, что при резком росте числа пользователей приложения может возникнуть необходимость увеличения квоты, чтобы избежать проблем с входом в систему.
Заключение
В заключение хочется сказать несколько вещей. При исследовании защищенности мобильных приложений, к большому сожалению, многие не обращают внимание на сторонние сервисы, которые этот продукт использует, а в особенности на то, как они сконфигурированы. И это очень зря, поскольку в мобилках традиционно используется достаточно большое количество внешних интеграций для ускорения работы. Определенно стоит обращать на них больше внимания и провести аудит того, куда «ходит» ваше приложение.
Рассмотренный в статье Firebase — это лишь частный пример из множества подобных сервисов. Поэтому у меня есть совет или даже просьба: если вы занимаетесь безопасностью — спросите у коллег из разработки, какие сервисы они используют и как они сконфигурированы. А если вы из разработки, проведите аналогичное упражнение и посмотрите на свои сервисы: какие рекомендации по настройке с точки зрения безопасности в них есть и, может быть, что-то из этого можно применить уже сейчас.
Ну, а мы всегда поможем вам и вашим проектам становиться более защищенными и выявлять подобные проблемы.
Ссылки:
О Firebase
О ключах Firebase
Правила Безопасности Firebase
О сервисах Firebase