Всем привет, сегодня я хотел бы рассказать вам о некоторых вариантах передачи данных между двумя Android-приложениями и рассмотреть их с точки зрения безопасности.
Я решил написать эту статью по двум причинам.
Во-первых, я стал часто сталкиваться с непониманием среди разработчиков механизмов работы компонентов Android-приложений.
Во-вторых, я перестал понимать, на чем основан выбор того или иного механизма при реализации функций, и захотел донести, как это должно выглядеть при минимальной заработной плате.
Задача У нас есть 2 приложения, которые обращаются к одному и тому же API. Клиенты могут получить доступ к API, используя токен доступа (sessionId).
Необходимо реализовать плавный переход от одного приложения к другому.
Для этого нужно что-то между ними пошарить; например, пусть это будет sessionId. Вариант № 1: ЗАПРОС DEEPLINK Самый очевидный вариант — передать токен в Query DeepLink. Это будет выглядеть примерно так:
В этом случае получатель сможет извлечь sessionId и использовать его, не запрашивая у пользователя авторизацию.slave://mainЭsessionId=686A885A4FB644053C584B9BE2A70C7D
С точки зрения разработчика задача выглядит выполненной, но давайте копнем немного глубже.
Взлом DeepLink
Поскольку любое приложение может зарегистрировать схему Тинькофф://, ОС может открыть не то приложение.Это возможно благодаря тому, что нет регистрации и ограничений на использование схем.
Вредоносное приложение может зарегистрировать схему Тинькофф://, перехватить запрос к приложению Тинькофф и запуститься самостоятельно.
В этом случае sessionId попадет к злоумышленникам, и ваша учетная запись будет скомпрометирована.
Кроме того, DeepLink Hijacking допускает фишинг, например, путем отображения полей логина и пароля.
Концептуально процесс выглядит так:
Есть 2 решения этой проблемы.
Во-первых, технология AppLinks больше не позволяет разработчикам настраивать схему; Вместо этого используются http/https. В этом случае ОС берет ссылку раб.
com/профиль и связывается с хостом Slave.com для проверки.
Второй — Intent URL — вместо вызова «slave://» вызывается «intent://», куда передается уникальный идентификатор приложения, которое необходимо запустить.
Это выглядит так: intent://main/#Intent;scheme=slave;package=com.example.slave.client.android;end "
В этом случае перехватить запуск приложения уже не получится, так как указан конкретный пакет. Еще есть проблема, что пользователь может установить приложение из стороннего источника с тем же packageId, что и у вашего слейва.
В этом случае, если у вас не установлено законное подчиненное приложение, вредоносное приложение установится и получит ваш токен.
Фиксация сеанса
Это атака, при которой злоумышленник заставляет клиента установить сеанс с целевым программным обеспечением, используя идентификатор сеанса, предоставленный злоумышленником.После аутентификации пользователя злоумышленник может использовать этот уже привилегированный идентификатор в своих целях.
Атака использует тот факт, что целевое программное обеспечение использует тот же идентификатор сеанса после повышения привилегий.
Как это выглядит в нашем случае:
- злоумышленник получает анонимный сеанс от приложения
- отправляет жертве красивое письмо от имени банка, в котором ему предлагается зайти в личный кабинет
- при переходе по ссылке попадаем в DeepLink с сессией злоумышленника подчиненный://mainЭsessionId=686A885A4FB644053C584B9BE2A70C7D
- мобильное приложение берет сессию, понимает, что у него недостаточно прав и просит пользователя пройти аутентификацию
- пользователь его проходит, права сессии повышаются
- пользователь в приложении, злоумышленник с привилегированной сессией, прибыль
А наш путь — отказаться от передачи токена от мастера к слейву.
Плюс это даст нам глубокую защиту и если что-то сломается в API и токены не изменятся при повышении привилегий, то атака все равно будет невозможна.
Утечка информации от третьих лиц
Еще один минус этого варианта.Многие используют сторонние сервисы для DeepLink из-за удобства генерации ссылок, аналитики и других крутых вещей.
В этом случае вы просто передаете свой токен сторонней компании.
Вариант №2: КОНТЕНТ-ПРОВАЙДЕР Как мы это сделаем? Давайте определим контент-провайдера для ведущего устройства и заставим ведомое устройство обращаться к этому контент-провайдеру за токеном.
Таким образом, мы исключаем риск передачи токена не тому приложению в случае перехвата DeepLink и делаем невозможными атаки с фиксацией сеанса.
Но у нас другие проблемы — в текущей версии любое приложение может запросить токен в любой момент, даже если мы не инициировали его запуск.
Уровень защиты
В большинстве случаев нужно проверить, что слейв подписан тем же ключом, что и мастер, то есть принадлежат одному автору.На этот случай в менеджере пакетов есть метод checkSignatures, который проверяет подписи приложений.
Чтобы использовать эту функцию, вам необходимо добавить разрешение с ProtectionLevel="signature" для Content-Provider в манифесте приложения: <permission
android:name="com.example.contentprovider.access"
android:protectionLevel="signature"/>
<application>
<provider
.
android:readPermission="com.example.contentprovider.access">
</application>
Схема останется практически неизменной от предыдущего рисунка, только будет гарантия, что доступ к токену будут иметь только приложения с подписью одного и того же автора.
Условия гонки разрешений
Есть одна очень неприятная особенность — имена разрешений не уникальны, чем может воспользоваться вредоносное приложение и прописать разрешение с нашим именем и ProtectionLevel="normal" раньше нас.В этом случае при установке нашего приложения разрешение уже будет существовать в ОС и не будет перезаписано.
Следовательно, наш контент-провайдер останется незащищенным и к нему будет разрешен доступ из любого приложения.
Разные подписи
К сожалению, приложения не всегда подписываются одним и тем же ключом, например, часть приложений была куплена, или «так сложилось исторически», но плавный переход все равно необходим.В этом случае мы берем на себя проверку подписи.
Как это можно реализовать: У Content-Provider есть метод getCallingPackage(), с помощью которого мы можем получить packageId приложения, запрашивающего данные, а с помощью packageId мы можем получить список подписей и сверить их со встроенными.
String pkg = this.getCallingPackage();
PackageInfo pkgInfo = pkgmgr.getPackageInfo(pkg, GET_SIGNATURES);
Signatures[] signatures = pkgInfo.signatures;
for (Signature sig: signatures) {
if (sig.equals(TRUSTED_SIGNATURE)) {
// trusted signature found, trust the application
}
}
Вроде бы мы все сделали идеально, но нет.
Уязвимость поддельного удостоверения личности
Проблема в том, что когда Android создает цепочку доверия, процесс проверки сравнивает только субъект и не проверяет подпись в поле подписывающего лица сертификата.В результате злоумышленник может построить цепочку доверия без фактической подписи.
Эта ошибка создает неверную цепочку сертификатов, которая может включать законные сертификаты, встроенные в APK, но фактически не используемые для подписи приложения.
В конце оставлю ссылку на коммит, исправляющий эту уязвимость.
Проблема исправлена в Android 4.4, поэтому все, что нам нужно сделать, это повысить уровень API до 19. выводы Сегодня мы рассмотрели, как должен происходить анализ функций во время разработки.
Также мы рассмотрели варианты передачи секрета между двумя приложениями, в ходе чего проанализировали проблемы каждого варианта и придумали способы их избежать.
Всем безопасных приложений! Ссылки
- Исправление уязвимости поддельного идентификатора совершить
- Презентация Уязвимость поддельного удостоверения личности
- КВЕ: Фиксация сеанса
-
Почему Мне Нравится Php?
19 Oct, 24 -
Дневники Linux
19 Oct, 24 -
Блин На Олимпиаде!
19 Oct, 24