Системные разрешения

Android это система с разделением прав, в которой каждое приложение запущено с индивидуальным идентификатором (ID пользователя и ID группы системы Linux). Части системы также содержат собственные идентификаторы. Таким образом, Linux изолирует приложения друг от друга и от системы.

Дополнительные функции безопасности предоставляют механизм “разрешений”, который усиливает ограничения на доступ к операциям, которые определенный процесс может выполнять. Также выдаются разрешения для каждого URI для предоставления индивидуального доступа к определенным частям данных.

В этом документе мы рассмотрим как разработчики приложений могут использовать функции безопасности, которые предоставляются системой Android. Подробнее о безопасности читайте в этом разделе.

Архитектура безопасности

Центральным звеном архитектуры безопасности Android является то, что по умолчанию, ни одно из приложений не имеет разрешения на выполнение каких-либо операций, которые могут отрицательно повлиять на другие приложения, операционную систему или пользователя. Эти ограничения включают в себя право чтения или записи личных данных (например контактов или email сообщений), чтение или запись файлов других приложений, доступ к сети, право выключать устройство и так далее.

Поскольку все приложения выполняется в песочнице, они должны явно указывать данные, доступные другим. Это делается с помощью разрешения дополнительных возможностей, не предусмотренных основной песочницей. Приложения статически запрашивают разрешения, которые им необходимы, а система Android запрашивает у пользователя согласие во время установки. В Android нет механизма для пересмотра разрешений динамически во время выполнения программы, поскольку это усложняет работу пользователя в ущерб безопасности.

Изолированная среда приложения не зависит от технологии, используемой при сборке. В частности, виртуальная машина Dalvik не является преградой для выполнения приложением нативного кода (смотрите Android NDK). Все типы приложений – Java, нативные и гибридные – запускаются в одинаковых песочницах и защищены одинаково.

Подпись приложения

Все APK файлы должны быть подписаны сертификатом, закрытый ключ которого хранится у их разработчика. Данный сертификат идентифицирует автора приложения. Сертификаты не обязательно должны быть подписаны центром сертификации; это совершенно допустимо и характерно для Android приложений – использовать собственные сертификаты. Они позволяют системе предоставлять или отказывать в доступе приложениям с подписанным уровнем доступа, а также разрешать или отказывать приложениям иметь такой-же идентификатор, как у другого приложения.

Идентификаторы пользователя и доступ к файлам

Во время установки, Android назначает каждому пакету уникальный идентификатор пользователя Linux (UID). Идентификатор остается постоянным на всё время существования пакета на этом устройстве. На другом устройстве тот же пакет может иметь другой UID; важно, чтобы каждый пакет на данном устройстве имел свой собственный идентификатор пользователя.

Поскольку управление безопасностью происходит на уровне процесса, код любых двух пакетов не может нормально работать в одном и том же процессе, так как они должны выполняться под различными пользователями Linux. Вы можете использовать атрибут sharedUserId в файлах манифеста для нескольких пакетов, чтобы указать общий UID. После этого в целях безопасности, два пакета будут выполняться как одно и тоже приложение, с одинаковым UID и одинаковыми правами на файлы. Помните, что в целях безопасности, только два приложения с одинаковой подписью (и хранящие одинаковый атрибут sharedUserId) будут иметь одинаковый идентификатор пользователя.

Любые данные, которые сохраняет приложение, будут связаны с идентификатором приложения и в нормальных условиях будут недоступны другим пакетам. При создании нового файла с использованием методов getSharedPreferences(String, int), openFileOutput(String, int) или openOrCreateDatabase(String, int, SQLiteDatabase.CursorFactory), вы можете использовать флаги MODE_WORLD_READABLE и MORE_WORLD_WRITEABLE, чтобы разрешить другим пакетам читать или записывать данный файл. При установке данных флагов, файл все еще принадлежит приложению, но он глобально доступен для чтения и записи любым приложением.

Использование разрешений

Стандартное Android приложение не имеет разрешений, связанных с ним по умолчанию, то есть оно не может сделать ничего, что могло бы негативно повлиять на пользователя или на какие-либо данные. Для использования защитных функций устройства, вы должны включить в файл AndroidManifest.xml один или несколько тегов <uses-permission>, указав таким образом разрешения, которые вам нужны.

Например, если приложение должно отслеживать входящие SMS сообщения:

Во время установки приложения, запрошенные разрешения предоставляются на основе проверок подписи приложения и взаимодействия с пользователем. Во время выполнения приложения никакие проверки разрешений с уведомлением пользователя не проводятся; если приложению выдано конкретное разрешение на этапе установки, оно может его использовать по желанию в любое время. Если разрешение не выдано, любая попытка использовать функцию отменяется без уведомления пользователя.

Часто при отказе в доступе выдается исключение SecurityException. Однако, это не всегда так. Например, метод sendBroadcast(Intent) проверяет разрешение на данные, которые передаются каждому приемнику, после чего метод возвращает управление, поэтому вы не получите исключение, если произойдет отказ в доступе. Почти по всех случаях о нарушении разрешений будет сделана запись в системный журнал.

Однако, в нормальной ситуации (например при установке приложения из Google Play), приложение не сможет быть установлено, если пользователь не подтвердил каждое запрошенное разрешение. В большинстве случаев вам не надо беспокоиться о ошибках времени выполнения, связанных с недостающими разрешениями, поскольку сам факт того, что приложение установлено, означает, что все желаемые разрешения ему были предоставлены.

Разрешения, предоставляемые системой Android, расположены в классе Manifest.permission. Любые приложения могут описывать и требовать свои собственные разрешения, так что это не полный перечень всех возможных разрешений.

Особые разрешения могут быть выполнены в нескольких местах во время работы приложения:

  • Во время вызова в системе, чтобы предотвратить выполнение приложением некоторых функций.
  • При запуске явления, чтобы избежать запуска приложения другими приложениями.
  • Во время отправки и получения широковещательных сообщений, чтобы контролировать кто может получать ваши сообщения и кому вы их можете посылать.
  • Во время доступа и работы с поставщиками содержимого.
  • При связывании или запуске сервиса.

Внимание: со временем, в API могут быть добавлены новые ограничения, так что для их использования может понадобиться запрос разрешений, даже если раньше они не требовались. Поскольку существующие приложения считают доступ к некоторым функциям открытым, Android может добавлять новые разрешения в манифест, чтобы предотвратить нарушение работы приложения в новой версии платформы. Android принимает решение о необходимости добавить разрешение, основываясь на значении атрибута targetSdkVersion. Если значение ниже, чем версия, в которой разрешение было добавлено, Android сам добавит разрешение в манифест.

Например, разрешение WRITE_EXTERNAL_STORAGE было добавлено в API 4, чтобы закрыть доступ к общему хранилищу. Если вы установите targetSdkVersion равное 3 или ниже, на новых версиях Android это разрешение будет автоматически добавлено приложению.

Помните, что если такое случится с вашим приложением, на Google Play данное разрешение будет в списке необходимых, хотя ваше приложение может вообще в них не нуждаться.

Чтобы предотвратить такое поведение и удалить разрешения, в которых приложение не нуждается, всегда устанавливайте targetSdkVersion в максимально возможное значение. Какие разрешения добавлялись в каждой из версий, вы можете посмотреть в документации Build.VERSION_CODES.

Объявление и требование разрешений

Чтобы требовать собственное разрешение, вы должны сначала объявить его в файле манифеста, используя элемент <permission>.

Например, приложение должно контролировать, кто может запустить одно из его явлений. Для этого объявим новое разрешение следующим образом:

Обязательный атрибут <protectionLevel> сообщает системе, как пользователь будет проинформирован о приложениях, требующих разрешение, или кто имеет право использовать такое разрешение, как описано в связанном документе.

Необязательный атрибут <permissionGroup> используется, чтобы помочь системе показать разрешение пользователю. Как правило выбирается либо стандартная системная группа (список групп смотрите здесь android.Manifest.permission_group), либо, в более редких случаях, определенная самостоятельно. Предпочтительно использовать существующую группу, так как это упрощает интерфейс пользователя для демонстрации.

Помните, что метка и описание должны быть заданы. Это строковые ресурсы, которые могут быть показаны пользователю при просмотре списка разрешений (android:label) или при просмотре описания отдельно взятого разрешения (android:description). Метка должна быть короткой, несколько ключевых слов о функциях, которые защищает разрешение. Описание должно состоять из нескольких предложений, описывающих то, что разрешение позволяет делать. Мы рекомендуем составить описание из двух частей: в первой описать разрешение, а во второй предупредить пользователя о возможных плохих последствиях.

Вот пример описания разрешения CALL_PHONE:

Вы можете посмотреть список текущих разрешений в приложении Настройки, а также с помощью команды adb shell pm list permissions. Для использования приложения Настройки, перейдите в Настройки->Приложения. Выберите приложение и пролистайте вниз до строки “разрешения”. Для разработчиков ключ ‘-s’ команды adb позволяет показать разрешения в той форме, в которой его обычно видит пользователь:

Требование разрешений в AndroidManifest.xml

Разрешения на высоком уровне, ограничивающие доступ ко всем компонентам системы или отдельным приложениям, могут быть установлены в файле AndroidManifest.xml. Все, что требуется, это добавить атрибут android:permission желаемому компоненту, указав разрешение, которое будет использоваться для контроля доступа к нему.

Разрешения для явлений (применяются к тегу <activity>) указывает, кто может запустить данное явление. Разрешение проверяется при выполнении методов Context.startActivity() и Activity.startActivityForResult(); если вызывающий компонент не имеет нужного разрешения, будет выброшено исключение SecurityException.

Разрешения для сервисов (применяются к тегу <service>) указывают, кто может запускать или связываться с сервисом. Разрешения проверяются во время выполнения методов Context.startService(), Context.stopService() и Context.bindService(); если вызывающий компонент не имеет нужного разрешения, будет выброшено исключение SecurityException.

Разрешения для широковещательных приемников (применяются к тегу <receiver>) указывают, кто может отправлять сообщения данному получателю. Разрешения проверяются после завершения работы метода Context.sendBroadcast(), когда система пытается доставить сообщение данному приемнику. В результате отказ не приведет к выбросу исключения; просто сообщение не будет доставлено. В таких случаях, разрешение может быть выставлено в методе Context.registerReceiver(), чтобы контролировать кто может передавать сообщения программно зарегистрированным приемникам. Можно пойти другим путем, установив разрешения при вызове метода Context.sendBroadcast(), чтобы указать, какие объекты BroadcastReceiver могут получать сообщения.

Разрешения для поставщиков содержимого (применяются к тегу <provider>) указывают, кто может получить доступ к данным, которые предоставляет объект ContentProvider. (Поставщики содержимого имеют важное дополнительное средство защиты – разрешения для URI, которые описаны ниже). В отличие от других компонентов, есть два отдельных разрешения, которые можно установить: android:readPermission, которое указывает кто может читать из поставщика содержимого, и android:writePermission, которое указывает кто в него может записывать. Помните, что если поставщик защищен правами и на чтение и на запись, получение прав только на запись не подразумевает автоматическое получение прав на чтение. Разрешения проверяются при первом обращении к поставщику содержимого (если у вас нет ни одного из разрешений, будет выброшено исключение SecurityException) и во время выполнения его операций. Для использования метода ContentResolver.query() требуются права на чтение; для методов ContentResolver.insert(), ContentResolver.update() и ContentResolver.delete() требуются права на запись. Во всех случаях при отсутствии разрешений будет выброшено исключение SecurityException.

Требование разрешений при отправке широковещательных сообщений

В дополнение к разрешениям, проверяющим, кто может отправлять намерения зарегистрированным широковещательным приемникам (как описано выше), вы можете также задать разрешения при отправке широковещательного сообщения. Вызов метода Context.sendBroadcast() со строкой разрешения, потребует наличия этого разрешения у получающего приложения.

Помните, что и отправители и получатели могут требовать разрешения. В таких случаях должны быть успешно пройдены обе проверки, чтобы сообщение достигло заданной цели.

Требование других разрешений

Произвольные разрешения могут быть затребованы при любом вызове в службе. Это возможно с помощью вызова метода Context.checkCallingPermission(). Вызов этого метода со строкой разрешения вернет целочисленный индикатор, который показывает, было ли выдано разрешение на момент вызова. Отметим, что такой подход может быть использован только при поступлении вызова от другого процесса, обычно через интерфейс IDL, опубликованный сервисом или переданного другому процессу иным способом.

Есть несколько других способов проверки разрешений. Если у вас есть pid другого процесса, вы можете использовать для проверки разрешений метод Context.checkPermission(String, int, int). Если у вас есть имя пакета другого приложения, вы можете использовать напрямую метод пакетного менеджера PackageManager.checkPermission(String, String), чтобы выяснить, было ли предоставлено конкретному пакету указанное разрешение.

Разрешения на URI

Стандартной системы проверки разрешений, описанной выше, не достаточно при использовании поставщиков содержимого. Поставщики могут защитить себя, используя разрешения на чтение и запись, в то время как их прямые клиенты также должны передавать определенные URI другим приложениям для работы с ними. Типичный пример – это вложение в письмо в почтовом клиенте. Доступ к письмам должен быть защищен разрешениями, так как это конфиденциальные данные. Однако, если URI вложенного изображения передается в просмотрщик изображений, последний не сможет его открыть, поскольку нет причин давать ему доступ ко всей почте.

Решением проблемы являются разрешения для URI: при запуске явления или возвращении результата в явление, вызывающий компонент может установить флаги Intent.FLAG_GRANT_READ_URI_PERMISSION и Intent.FLAG_GRANT_WRITE_URI_PERMISSION. Эти флаги дают доступ принимающему явлению к конкретному URI, переданному в намерении, независимо от того, имеет ли он разрешения на доступ к данным поставщика содержимого.

Этот механизм позволяет реализовать общую совместимую модель, в которой действие пользователя (при открытии вложения, выборе контакта из списка, и.т.д) приводит к точечному подтверждению разрешений. Это позволяет сократить число разрешений, применяя их только для тех приложений, которые непосредственно связаны с работой текущего.

Предоставление точечных URI разрешений, однако, требует некоторого взаимодействия с поставщиком содержимого, который владеет этим URI. Настоятельно рекомендуется, чтобы поставщики содержимого реализовали этот объект и объявили, что он поддерживает с помощью атрибута android:grantUriPermission или тега <grant-uri-permissions>.

Подробную информацию читайте в документации по методам Context.grantUriPermission(), Context.revokeUriPermission() и Context.checkUriPermission().

Добавить комментарий