- PVSM.RU - https://www.pvsm.ru -
Немного информации о том, какие данные в вашем приложении могут быть доступны для других программ и какие меры можно предпринять, чтобы это предотвратить.
Архитектуру Android-приложения можно представить в виде набора компонент (activities, services и пр.) и связей между ними. Android API предоставляет несколько стандартных способов общения между компонентами приложения: старт активити/сервис через контекст, отправка сообщений-интентов, использование ServiceConnection или ContentProvider’а и другое. Обо всём этом можно почитать в любом туториале о передаче данных в Android. Однако, есть один нюанс, о котором, как правило, умалчивается. Речь идет о доступности ваших компонент и данных, передаваемых между ними, для других приложений.
Для запуска одной из своих активити вы, наверняка, используете специально подготовленный intent:
Intent myIntent = new Intent(CurrentActivity.this, NextActivity.class);
startActivity(myIntent);
Используя Context и Class, которые вы передали в конструктор интента, Android определяет package приложения и сам класс нужной активити, после чего запускает её.
Вы также можете попробовать запустить чужую активити, зная ее package name и class name:
Intent intent = new Intent();
/* Одна из активити стандартного приложения Контакты*/
intent.setClassName("com.android.contacts",
"com.android.contacts.activities.PeopleActivity");
startActivity(intent);
Результат — запущенная активити.
Значит ли это, что все активити могут быть запущены сторонним приложением? Нет. Если заменить в предыдущем примере classname на
intent.setClassName("com.android.contacts", "com.android.contacts.preference.ContactsPreferenceActivity");
то мы получим java.lang.SecurityException.
На самом деле разработчик приложения сам решает, какие активити будут доступны, а какие нет, указывая для каждой активити в манифесте тэг android:exported [1] равным true или false соответственно. Значение по умолчанию для этого тега ‒ false, т.е. все активити в AndroidManifest.xml, не имеющие этого тега, доступны только внутри приложения.
Однако, посмотрев на манифест приложения Контакты (например, тут [2]) можно заметить, что у PeopleActivity нет тега android:exported=«true», почему же нам удалось её запустить? Ответ можно найти в официальной документации: если активити содержит какой-либо интент-фильтр, то значением по умолчанию для android:exported будет true.
Разработчику приложения не нужно беспокоиться о видимости своих activity вне приложения до тех пор, пока в какой-либо активити не появляется intent-filter. Как только вы объявляете intent-filter, спросите себя, готовы ли вы к тому, что эта активити может быть запущена кем-либо еще. Если нет — не забудьте указать <android:exported=«false»>.
Видимость сервиса определяется также как и видимость активити, и мы бы не стали уделять сервисам отдельный пункт, если бы не одно “но”. В случае с сервисом, его видимость может завести куда дальше, чем незапланированный старт, как в случае с активити. Если в вашем сервисе предусмотрена возможность биндинга [3], то этой возможностью могут воспользоваться и другие и получить ссылку на ваш сервис через ServiceConnection. Давайте рассмотрим пример подключения к стандартному сервису воспроизведения музыки.
Примечание: следующий пример актуален только на достаточно старых версиях плеера. В свежей версии разработчики разобрались с флагом exported и установили его в false :)
Intent intent = new Intent();
/* Сервис проигрывания аудио - часть стандартного плеера */
intent.setClassName("com.android.music", "com.android.music.MediaPlaybackService");
ServiceConnection conn = new MediaPlayerServiceConnection();
/* Подключение к сервису */
bindService(intent , conn, 1);
class MediaPlayerServiceConnection implements ServiceConnection {
public void onServiceConnected(ComponentName name, IBinder boundService) {
Log.i("service connection", "Connected! Name: " + name.getClassName());
}
public void onServiceDisconnected(ComponentName name) {
Log.i("MediaPlayerServiceConnection", "Disconnected!");
}
}
После биндинга в переменной boundService будет храниться ссылка на сервис. Далее вы можете воспользоваться java reflection или Android Interface Definition Language (AIDL), чтобы иметь возможность вызывать методы сервиса напрямую, в результате чего можно, например, остановить воспроизведение, если стандартный плеер уже что-то играет.
Рекомендации в этом случае такие же: установите <android:exported=«false»> для всех сервисов, имеющих какой-либо интент фильтр, если вы не планируете оставить его публичным.
Основное предназначение ContentProvider’а — предоставление данных другим приложениям. Именно поэтому по умолчанию значение тега <android:exported> для этого компонента приложения равно true. Но основное предназначение — не единственное. ContentProvider часто используется:
Во всех этих случаях вам, скорее всего, не нужно предоставлять данные вне приложения. Если это действительно так, то не забудьте указать провайдеру <android:exported=«false»>.
Общение с помощью широковещательных сообщений можно разделить на 3 этапа:
Каждый приёмник (receiver) при регистрации указывает некий IntentFilter, и сообщения будут доставляться этому приёмнику в соответствии с этим фильтром. Как можно догадаться, приёмник сообщения может удовлетворять фильтру и находиться вне нашего приложения. Вы можете добиться приватности рассылаемых сообщений, указав интенту нужный package:
intent.setPackage(“com.android.example.mypackage”)
или воспользоваться LocalBroadcastManager [7] (доступен в support library), который также не даст улететь сообщению за границы приложения (на самом деле процесса):
LocalBroadcastManager.getInstance(context).sendBroadcast(intent);
Более того, LocalBroadcastManager позволяет регистрировать приёмники, так что вы можете быть уверены, что получаемые такими приёмниками интенты тоже являются локальными, а не прилетают извне.
Если коротко, то все используемые в приложении ресурсы доступны для чтения вне приложения. Для получения некоторых из них достаточно знать только package name целевого приложения:
PackageManager pm = getPackageManager();
/* Иконка приложения Youtube */
Drawable icon = pm.getApplicationIcon("com.google.android.youtube");
/* Логотип приложения Youtube */
Drawable logo = pm.getApplicationLogo("com.google.android.youtube");
ApplicationInfo applicationInfo = pm.getApplicationInfo("com.google.android.youtube", 0);
/* Название приложения Youtube */
CharSequence applicationLabel = pm.getApplicationLabel(applicationInfo);
Если вам известна информация об активити внутри приложения, вы можете проделать подобные операции для каждой из них отдельно.
Узнав имя конкретного ресурса, можно получить и его:
Resources r = getPackageManager().getResourcesForApplication("com.google.android.youtube");
/* Картинка-превью для виджета Youtube
* youtube_widget_preview - имя файла
* drawable - директория, в которой находится файл
* com.google.android.youtube - package name
*/
int id = r.getIdentifier("youtube_widget_preview", "drawable", "com.google.android.youtube");
Drawable drawable = r.getDrawable(id);
ImageView iw = new ImageView(context);
iw.setImageDrawable(drawable);
rootView.addView(iw);
Этим способом мы смогли отобразить картинку-превью для виджета Youtube внутри собственного приложения.
Главная загвоздка с ресурсами в том, что здесь нет способа их скрыть. Хранение приватных данных в ресурсах — плохая идея, но если у вас есть такая необходимость, лучше хранить их в зашифрованном виде.
В завершении статьи хотелось бы сказать еще несколько слов о файловой системе в Android. Этот вопрос уже хорошо освещен, многие знают, что в Android для каждого приложения создается собственная директория /data/data/, доступ к которой имеет только само приложение (или группа приложений c одним sharedUserId). В этой директории находятся файлы настроек SharedPreferences, файлы базы данных приложения, кэш и многое другое, однако, не стоит полагаться на защищенность этого хранилища. Файлы в этой директории недоступны только до тех пор, пока:
Это значит, что приватные данные, так же как в случае с ресурсами, необходимо шифровать, даже если они находятся в internal storage.
Полезные ресурсы по теме:
Статья Security tips на developer.android.com [8]
Книга Application Security For The Android Platform от Jeff Six [9]
Автор: eastbanctech
Источник [10]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/android-development/54820
Ссылки в тексте:
[1] android:exported: https://developer.android.com/guide/topics/manifest/activity-element.html#exported
[2] тут: http://code.metager.de/source/xref/android/4.2/packages/apps/Contacts/AndroidManifest.xml
[3] предусмотрена возможность биндинга: https://developer.android.com/guide/components/bound-services.html
[4] при реализации подсказок при поиске: https://developer.android.com/guide/topics/search/adding-custom-suggestions.html
[5] developer.android.com/training/sync-adapters/creating-stub-provider.html: https://developer.android.com/training/sync-adapters/creating-stub-provider.html
[6] как уровень абстракции над базой данных: https://developer.android.com/reference/android/content/CursorLoader.html
[7] LocalBroadcastManager: https://developer.android.com/reference/android/support/v4/content/LocalBroadcastManager.html
[8] Статья Security tips на developer.android.com: https://developer.android.com/training/articles/security-tips.html
[9] Книга Application Security For The Android Platform от Jeff Six : http://shop.oreilly.com/product/0636920022596.do
[10] Источник: http://habrahabr.ru/post/212321/
Нажмите здесь для печати.