Приветствую тебя, дорогой читатель! Многие разработчики используют возможности Account Manager(AM) в своих приложениях. И правильно делают, ведь этот инструмент позволяет упростить некоторые вещи. Он позволяет хранить пароль, токен, да и в принципе любые строковые данные юзера. Так же позволяет автоматически обновлять токен, если тот протухает, и много других полезных штук. Но у этого удобства есть и другая сторона — безопасность. Из-за этого собственно я и написал данный текст.
Раз AM позволяет хранить такие важные данные как пароль и токен, то наверно он просто обязан это делать безопасно, ведь если они утекут, то ничего хорошего не получится. Вы можете сказать, что на рутованых андроид девайсах ничто не хранится безопасно, и я тут соглашусь. Однако если бы все ограничивалось только рутом, то не читать бы вам сие «произведение». Чтобы рассказать, ради чего мы тут собрались, я начну с самого начала.
Вообще для работы с AM в приложении нужно сделать несколько телодвижений и создать два компонента — наследника AbstractAccountAuthenticator и Service (подробней тут). Последний придется зарегистрировать в манифесте приложения таким образом:
А еще, если вы заметили, то нам надо создать в ресурсах некий xml-файл с именем authenticator.xml. Хотя название тут не важно, а важно содержимое. Оно то и играет ключевую роль рассказа. Выглядит файл внутри вот так:
Параметр label — отвечает за название в списке аккаунтов в настройках девайса, а самое интересно несет в себе accountType. Без него приложение не сможет добавлять и получать аккаунты. А еще с помощью этого параметра можно перехватить аккаунты другого приложения даже на девайсах без рута. Давайте посмотрим, как это может произойти.
На устройстве существует приложение А с accountType = «someType», это приложение создает акаунт, добавляет в него токен, пароль и прочую информацию. В один прекрасный момент на устройство устанавливается приложение B с таким же accountType = «someType». И вот если удалить приложение A, то все созданные аккаунты перейдут во власть приложения B с полным доступом. Обратное тоже верно, аккаунты приложения B передадутся приложению A, если удалить B.
Пример на видео:
Опережая вопросы про подписи apk, это не зависит от того, каким ключом было подписано приложение, релизным или дебажным, передача произойдет в любом случае. Ужасно, правда?
Все это можно проделать в плоть до API версии 23. Исправлено только с выходом андроид 7.0(API 24). Печалит в этой ситуации ничтожно малый процент устройств с этой версией андроида. Кстати, узнал я об этой проблеме из доклада по безопасности в андроид. К сожалению в тексте, пересказе презентации, об этом ничего нет вообще, а вот в видео об этом упомянули вскользь, не придав особого значения.
Так же что же ты предлагаешь? — можете спросить вы. А я предлагаю вам два варианта решения проблемы. Первый — продолжать пользоваться AM, но хранить в нем все важные данные в зашифрованном виде. Если вы перестали доверять AM, можно продолжать им пользоваться, но не хранить в нем важные данные и воспользоваться AM в связке со вторым вариантом. Второй — вообще отказаться от AM и пользоваться хранилищем, которое можно шифровать. Например SQLite в связке с sqlcipher или Realm, который поддерживает шифрование из коробки. Я выбрал последний. Тут пример для realm, где показано, как генерировать и хранить ключ шифрования.
Примеры кода в статье приводить не стал, так как в этом нет смысла. С исходниками жертвы и перехватчика вы сможете ознакомиться на гитхабе.
Спасибо за внимание!
Раз AM позволяет хранить такие важные данные как пароль и токен, то наверно он просто обязан это делать безопасно, ведь если они утекут, то ничего хорошего не получится. Вы можете сказать, что на рутованых андроид девайсах ничто не хранится безопасно, и я тут соглашусь. Однако если бы все ограничивалось только рутом, то не читать бы вам сие «произведение». Чтобы рассказать, ради чего мы тут собрались, я начну с самого начала.
Вообще для работы с AM в приложении нужно сделать несколько телодвижений и создать два компонента — наследника AbstractAccountAuthenticator и Service (подробней тут). Последний придется зарегистрировать в манифесте приложения таким образом:
<service
android:name=".account.AuthenticatorService"
android:exported="false">
<intent-filter>
<action android:name="android.accounts.AccountAuthenticator" />
</intent-filter>
<meta-data
android:name="android.accounts.AccountAuthenticator"
android:resource="@xml/authenticator" />
</service>
А еще, если вы заметили, то нам надо создать в ресурсах некий xml-файл с именем authenticator.xml. Хотя название тут не важно, а важно содержимое. Оно то и играет ключевую роль рассказа. Выглядит файл внутри вот так:
<account-authenticator
xmlns:android="http://schemas.android.com/apk/res/android"
android:accountType="@string/account_type"
android:label="@string/app_name" />
Параметр label — отвечает за название в списке аккаунтов в настройках девайса, а самое интересно несет в себе accountType. Без него приложение не сможет добавлять и получать аккаунты. А еще с помощью этого параметра можно перехватить аккаунты другого приложения даже на девайсах без рута. Давайте посмотрим, как это может произойти.
На устройстве существует приложение А с accountType = «someType», это приложение создает акаунт, добавляет в него токен, пароль и прочую информацию. В один прекрасный момент на устройство устанавливается приложение B с таким же accountType = «someType». И вот если удалить приложение A, то все созданные аккаунты перейдут во власть приложения B с полным доступом. Обратное тоже верно, аккаунты приложения B передадутся приложению A, если удалить B.
Пример на видео:
Опережая вопросы про подписи apk, это не зависит от того, каким ключом было подписано приложение, релизным или дебажным, передача произойдет в любом случае. Ужасно, правда?
Все это можно проделать в плоть до API версии 23. Исправлено только с выходом андроид 7.0(API 24). Печалит в этой ситуации ничтожно малый процент устройств с этой версией андроида. Кстати, узнал я об этой проблеме из доклада по безопасности в андроид. К сожалению в тексте, пересказе презентации, об этом ничего нет вообще, а вот в видео об этом упомянули вскользь, не придав особого значения.
Так же что же ты предлагаешь? — можете спросить вы. А я предлагаю вам два варианта решения проблемы. Первый — продолжать пользоваться AM, но хранить в нем все важные данные в зашифрованном виде. Если вы перестали доверять AM, можно продолжать им пользоваться, но не хранить в нем важные данные и воспользоваться AM в связке со вторым вариантом. Второй — вообще отказаться от AM и пользоваться хранилищем, которое можно шифровать. Например SQLite в связке с sqlcipher или Realm, который поддерживает шифрование из коробки. Я выбрал последний. Тут пример для realm, где показано, как генерировать и хранить ключ шифрования.
Примеры кода в статье приводить не стал, так как в этом нет смысла. С исходниками жертвы и перехватчика вы сможете ознакомиться на гитхабе.
Спасибо за внимание!