Android authority что это

Content provider (Контент-провайдер)

Что такое контент-провайдер

Контент-провайдер или «Поставщик содержимого» (Content Provider) — это оболочка (wrapper), в которую заключены данные. Если ваше приложение использует базу данных SQLite, то только ваше приложение имеет к ней доступ. Но бывают ситуации, когда данные желательно сделать общими. Простой пример — ваши контакты из телефонной книги тоже содержатся в базе данных, но вы хотите иметь доступ к данным, чтобы ваше приложение тоже могло выводить список контактов. Так как вы не имеете доступа к базе данных чужого приложения, был придуман специальный механизм, позволяющий делиться своими данными всем желающим.

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

В Android существует возможность выражения источников данных (или поставщиков данных) при помощи передачи состояния представления — REST, в виде абстракций, называемых поставщиками содержимого. Базу данных SQLite можно заключить в поставщик содержимого. Чтобы получить данные из поставщика содержимого или сохранить в нём новую информацию, нужно использовать набор REST-подобных идентификаторов URI. Например, если бы вам было нужно получить набор книг из поставщика содержимого, в котором заключена электронная библиотека, вам понадобился бы такой URI (по сути запрос к получению всех записей таблицы books):

Чтобы получить из библиотеки конкретную книгу (например, книгу №23), будет использоваться следующий URI (отдельный ряд таблицы):

Любая программа, работающая в устройстве, может использовать такие URI для доступа к данным и осуществления с ними определенных операций. Следовательно, поставщики содержимого играют важную роль при совместном использовании данных несколькими приложениями.

Встроенные поставщики

В Android используются встроенные поставщики содержимого (пакет android.provider). Вот неполный список поставщиков содержимого:

На верхних уровнях иерархии располагаются базы данных, на нижних — таблицы. Так, Browser, СаllLog, Contacts, MediaStore и Settings — это отдельные базы данных SQLite, инкапсулированные в форме поставщиков. Обычно такие базы данных SQLite имеют расширение DB и доступ к ним открыт только из специальных пакетов реализации (implerentation package). Любой доступ к базе данных из-за пределов этого пакета осуществляется через интерфейс поставщика содержимого.

Создание собственного контент-провайдера

Для создания собственного контент-провайдера нужно унаследоваться от абстрактного класса ContentProvider:

В классе необходимо реализовать абстрактные методы query(), insert(), update(), delete(), getType(), onCreate(). Прослеживается некоторое сходство с созданием обычной базы данных.

А также его следует зарегистрировать в манифесте с помощью тега provider с атрибутами name и authorities. Тег authorities служит для описания базового пути URI, по которому ContentResolver может найти базу данных для взаимодействия. Данный тег должен быть уникальным, поэтому рекомендуется использовать имя вашего пакета, чтобы не произошло путаницы с другими приложениями, например:

Источник поставщика содержимого аналогичен доменному имени сайта. Если источник уже зарегистрирован, эти поставщики содержимого будут представлены гиперссылками, начинающимися с соответствующего префикса источника:

Итак, поставщики содержимого, как и веб-сайты, имеют базовое доменное имя, действующее как стартовая URL-страница.

Необходимо отметить, что поставщики содержимого, используемые в Android, могут иметь неполное имя источника. Полное имя источника рекомендуется использовать только со сторонними поставщиками содержимого. Поэтому вам иногда могут встретиться поставщики содержимого, состоящие из одного слова, например contacts, в то время как полное имя такого поставщика содержимого — com.google.android.contacts.

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

URI для идентификации отдельно взятой записи будет иметь вид:

Символ # соответствует конкретной записи (ряд таблицы). Ниже приведено еще несколько примеров URI, которые могут присутствовать в поставщиках содержимого:

Обратите внимание — здесь поставщики содержимого content://media и content://contacts имеют неполную структуру. Это обусловлено тем, что данные поставщики содержимого не являются сторонними и контролируются Android.

Структура унифицированных идентификаторов содержимого (Content URI)

Для получения данных из поставщика содержимого нужно просто активировать URI. Однако при работе с поставщиком содержимого найденные таким образом данные представлены как набор строк и столбцов и образуют объект Android cursor. Рассмотрим структуру URI, которую можно использовать для получения данных.

Унифицированные идентификаторы содержимого (Content URI) в Android напоминают HTTP URI, но начинаются с content и строятся по следующему образцу:

Вот пример URI, при помощи которого в базе данных идентифицируется запись, имеющая номер 23:

Читайте также:  Файрвол для андроида что это

После content: в URI содержится унифицированный идентификатор источника, который используется для нахождения поставщика содержимого в соответствующем реестре. Часть URI ru.alexanderklimov.provider.notepad представляет собой источник.

/notes/23 — это раздел пути (path section), специфичный для каждого отдельного поставщика содержимого. Фрагменты notes и 23 раздела пути называются сегментами пути (path segments). Одной из функций поставщика содержимого является документирование и интерпретация раздела и сегментов пути, содержащихся в URI.

UriMatcher

Провайдер имеет специальный объект класса UriMatcher, который получает данные снаружи и на основе полученной информации создаёт нужный запрос к базе данных.

Вам нужно задать специальные константы, по которым провайдер будет понимать дальнейшие действия. Если используется одна таблица, то обычно применяют две константы — любые два целых числа, например, 100 для таблицы и 101 для отдельного ряда таблицы. Схематично можно изобразить так.

URI pattern Code Contant name
content://ru.alexanderklimov.provider.notepad/notes 100 NOTES
content://ru.alexanderklimov.provider.notepad/notes/# 101 NOTES_ID

В коде с помощью switch создаётся ветвление — хотим ли мы получить информацию о всей таблице (код 100) или к конкретному ряду (код 101).

Приложение может быть сложным и иметь несколько таблиц. Тогда и констант будет больше. Например, так.

URI pattern Code Contant name
content://com.android.contacts/contacts 1000 CONTACTS
content://com.android.contacts/contacts/# 1001 CONTACTS_ID
content://com.android.contacts/lookup/* 1002 CONTACTS_LOOKUP
content://com.android.contacts/lookup/*/# 1003 CONTACTS_LOOKUP_ID
. . .
content://com.android.contacts/data 3000 DATA
content://com.android.contacts/data/# 3001 DATA_ID
. . .

Символ решётки (#) отвечает за число, а символ звёздочки (*) за строку.

Метод query()

Метод query() является обязательным для класса ContentProvider. Если мы используем контент-провайдер для обращения к базе данных, то в нём вызывает одноимённый метод SQLiteDatabase. Состав метода практически идентичен.

Вам нужно программно получить необходимые данные для аргументов метода. Обратите внимание на метод ContentUris.parseId(uri), который возвращает последний сегмент адреса, в нашем случае число 3, для Selection Args.

Метод insert()

Метод insert() содержит два параметра — URI и ContenValues. Первый параметр работает аналогично, как в методе query(). Вторая вставляет данные в нужные колонки таблицы.

Для вставки используется вспомогательный метод insertGuest().

Структурирование МIМЕ-типов в Android

Как веб-сайт возвращает тип MIME для заданной гиперссылки (это позволяет браузеру активировать программу, предназначенную для просмотра того или иного типа контента), так и в поставщике содержимого предусмотрена возможность возвращения типа MIME для заданного URI. Благодаря этому достигается определенная гибкость при просмотре данных. Если мы знаем, данные какого именно типа получим, то можем выбрать одну или несколько программ, предназначенных для представления таких данных. Например, если на жестком диске компьютера есть текстовый файл, мы можем выбрать несколько редакторов, которые способны его отобразить.

Типы MIME работают в Android почти так же, как и в НТТР. Вы запрашиваете у контент-провайдера тип MIME определенного поддерживаемого им URI, и поставщик содержимого возвращает двухчастную последовательность символов, идентифицирующую тип MIME в соответствии с принятыми стандартами.

Обозначение MIME состоит из двух частей: типа и подтипа. Ниже приведены примеры некоторых известных пар типов и подтипов MIME:

text/html
text/css
text/xml
image/jpeg
audio/mp3
video/mp4
application/pdf
application/msword

Основные зарегистрированные типы содержимого:

application
audio
image
message
model
multipart
text
video

В Android применяется схожий принцип для определения типов MIME. Обозначение vnd в типах MIME в Android означает, что данные типы и подтипы являются нестандартными, зависящими от производителя. Для обеспечения уникальности в Android типы и подтипы разграничиваются при помощи нескольких компонентов, как и доменные имена. Кроме того, типы MIME в Android, соответствующие каждому типу содержимого, существуют в двух формах: для одиночной записи и для нескольких записей.

Типы MIME широко используются в Android, в частности при работе с намерениями, когда система определяет по МIМЕ-типу данных, какое именно явление следует активировать. Типы MIME всегда воспроизводятся контент-провайдерами на основании соответствующих URI. Работая с типами MIME, необходимо не упускать из виду три аспекта.

  1. Тип и подтип должны быть уникальными для того типа содержимого, который они представляют. Обычно это каталог с элементами или отдельный элемент. В контексте Android разница между каталогом и элементом может быть не такой очевидной, как кажется на первый взгляд.
  2. Если тип или подтип не являются стандартными, им должен предшествовать префикс vnd (обычно это касается конкретных видов записи).
  3. Обычно типы и подтипы относятся к определенному пространству имен в соответствии с вашими нуждами.

Необходимо еще раз подчеркнуть этот момент: основной тип MIME для коллекции элементов, возвращаемый командой cursor в Android, всегда должен иметь вид vnd.android.cursor.dir, а основной тип MIME для одиночного элемента, находимый командой cursor в Android, — вид vnd.android.cursor.item. Если речь идет о подтипе, то поле для маневра расширяется, как в случае с vnd.googlе.note; после компонента vnd. вы можете свободно выбирать любой устраивающий вас подтип.

ContentResolver

Каждый объект Content, принадлежащий приложению, включает в себя экземпляр класса ContentResolver, который можно получить через метод getContentResolver().

Читайте также:  Как удалить папку другое андроид

ContentResolver используется для выполнения запросов и транзакций от активности к контент-провайдеру. ContentResolver включает в себя методы для запросов и транзакций, аналогичные тем, что содержит ContentProvider. Объекту ContentResolver не нужно знать о реализации контент-провайдера, с которым он взаимодействует — любой запрос всего лишь принимают путь URI, в котором указано, к какому объекту ContentProvider необходимо обращаться.

Источник

Контент-провайдеры — слабое место в Android-приложениях

Содержание статьи

В среднем на Android-устройствах доступно более ста экспортируемых контент-провайдеров. Они поставляются как системными приложениями, так и теми приложениями, которые пользователи устанавливают самостоятельно. Уязвимость в контент-провайдерах автоматически ставит владельца устройства под удар. Более того, кто угодно может обращаться к открытым контент-провайдерам, не имея специально запрашиваемых привилегий. И приложение, которое аккуратно считает твои приватные данные, фактически не проявит никаких признаков вредоносного кода…

WARNING

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

Сам себе злобный Буратино

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

Важной возможностью для всех операционных систем общего назначения всегда были разнообразные методы межпроцессного взаимодействия. В относительно молодой ОС Android было использовано очень много удобных решений, которые должны были облегчить жизнь разработчикам. Одним из таких решений стали контент-провайдеры. Контент-провайдер — это поставщик данных. Любое приложение может создать своего контент-провайдера, который после установки приложения будет зарегистрирован операционной системой (см. врезку «Как задаются свойства контент-провайдера»).

Разграничение доступа и контент-провайдеры

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

На самом верхнем уровне можно просто сделать провайдер неэкспортируемым и пользоваться им внутри своего приложения. Если мы все-таки решили его экспортировать, то можно глобально ограничить к нему доступ с помощью параметра android:permission в секцииманифеста. В качестве разрешения можно использовать любое, уже определенное в системе, или задать свое собственное. Это очень удобно, если мы хотим разрешить доступ к провайдеру для группы своих приложений. Мы просто даем всем своим приложениям нестандартное разрешение, точно таким же разрешением закрываем доступ к провайдеру. После этого все приложения из нашей уютной инфраструктуры получат доступ к провайдеру на чтение и запись.

Для более тонкой регулировки можно использовать параметры android:readPermission и android:writePermission. Как ясно из их названий, они позволяют установить отдельно ограничение доступа на чтение или запись. Причем эти параметры имеют больший приоритет, чем более общий параметр android:permission.

Но есть и еще один, более глубокий уровень регулировки доступа. Он позволяет разрешить доступ к определенному набору информации, который поставляет провайдер. На полную катушку в этом случае используется то, что доступ к провайдеру осуществляется через URI bit.ly.

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

Проблемы и решения

Итак, мы уже можем сделать предварительные выводы о том, чем нам грозит плохо реализованный контент-провайдер:

  1. Несанкционированный доступ к персональным данным пользователя и чувствительной информации. Причиной может быть открытый контент-провайдер, который установило в систему легитимное приложение с высоким уровнем привилегий. Предположим, мы хотим почитать из своей не совсем легальной программы SMS пользователя. Если мы запросим напрямую разрешение READ_SMS, то привлечем к себе ненужное внимание. Да и человека, который установит программу, читающую SMS, еще нужно поискать. С другой стороны, можно поискать уже зарегистрированный в системе контент-провайдер, который установило привилегированное стороннее или встроенное приложение.
  2. Уязвимости типа SQL injection в провайдерах, работающих с базами данных. К сожалению, фильтрация пользовательского ввода полностью отдана на откуп разработчику мобильных приложений. Более того, «корпорация добра» оставила в документации множество грабель, на которые наступают программисты. Так, в разделе Content Provider Basics Android SDK есть подраздел Protecting Against Malicious Input, на который многие просто не обращают внимания. Разработчики не совсем точно понимают, как работают интерфейсы для обращения к БД SQLite в Android. Например, многие полагают, что повсеместно используемый метод query из класса android.database.sqlite совершенно безопасен. Но это не соответствует действительности, исследователи из MWR Labs достаточно подробно описали проблему и даже нашли несколько уязвимостей в устройствах Samsung. Они также выпустили удобный фреймворк Mercury, который позволяет находить такие дырки в приложениях. Многие разработчики не используют prepared statements, хотя они были в Android с первой версии API.

Интерфейс командной строки MWR Mercury

Хакер #170. Малварь для OS X

Пишем свою утилиту для анализа контент-провайдеров

Ранее я уже упомянул Mercury от MWR Labs. Это замечательный набор инструментов, и я рекомендую им пользоваться. К сожалению, лично меня повергает в уныние один взгляд на пользовательское лицензионное соглашение, под которое попадает этот продукт. Кроме того, лучше всего усвоить материал на практике, тем более когда программирование не представляет существенной сложности.

Читайте также:  Apk для android хонор

Напишем свое приложение под Android для анализа контент-провайдеров. Что мы сделаем?

  1. Получим информацию обо всех зарегистрированных в операционной системе контент-провайдерах.
  2. Определим, какие из этих провайдеров не защищены с помощью привилегий.
  3. Немного поэкспериментируем ;).

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

Объект типа ProviderInfo содержит всю необходимую нам информацию. Мы получим соответствующий ContentResolver и обработаем результат и возможные исключения.

Остальная часть кода нашего приложения служит для оформления пользовательского интерфейса, поэтому я ее опускаю. На выходе мы получили небольшую утилиту.

Главное окно приложения. Позволяет выбрать параметры для вызова контент-провайдера Выпадающий список. Содержит перечень всех доступных контент-провайдеров с указанием установленных разрешений на чтение и запись Результат запроса к провайдеру settings

Раз утилита готова — как я и обещал, немного поэкспериментируем. Я использовал обычный смартфон, старый Samsung Galaxy S с последней официальной прошивкой и некоторым набором самых распространенных приложений.

В списке контент-провайдеров своего телефона ты самостоятельно можешь найти что-то забавное. Например, мое внимание привлек провайдер com.sec.provider.facekey. В advisory MWR Labs про него ничего не сказано, тем не менее он представляет определенный интерес. Дело в том, что он устанавливается и используется системой «биометрической» блокировки экрана по снимку лица. Удивительно, что привилегии, запрещающие чтение и запись, в данном случае не установлены. Попробуем передать провайдеру SQL injection вектор «* from sqlite_master—».

Биометрическая блокировка экрана (Samsung Galaxy S)

С интересом узнаем, что мы получили доступ к базе данных с табличкой facefeature следующего вида:

Данные из этой таблицы легко читаются, кстати, замечу, что наша тестовая утилита не запрашивает никаких привилегий. Но при этом вполне может считать особенности твоего лица :).

Немного порывшись в списке, можно считать настройки телефона из провайдера com.settings (например, content://com.settings/secure). Это несмотря на то, что разрешения READ_SETTINGS мы не имеем.

Интересный результат дает обращение к «content://com.google.settings/sqlite_master—» (что ж, и Google промахивается):

Дальнейшие эксперименты я оставляю читателю. Все исходные тексты утилиты доступны на GitHub.

Как задаются свойства контент-провайдера

Если обратиться к Android SDK (где весь процесс расписан очень подробно и по шагам), то видно, что для того, чтобы зарегистрировать свой контент-провайдер, тебе придется добавить описание провайдера в файл AndroidManifest.xml в секцию. Параметров при этом можно указать множество bit.ly, но мы рассмотрим лишь важные для нас:

Важная особенность — доступ к данным будет осуществляться при помощи специального URI со схемой «content». Параметр android:authorities является уникальным идентификатором провайдера и представляет собой первую часть URI, вторая часть будет указывать на то, какие именно данные мы хотим получить. Параметр android:exported показывает, доступен ли провайдер для других приложений. Уже тут есть маленькая особенность, которая может испортить жизнь разработчику. Этот параметр в версиях Android до 16-й включительно (Android 4.1 JELLY_BEAN) по умолчанию установлен в «true», и все контент-провайдеры экспортируются, естественно, такая ситуация не безопасна. Но только с версии 17 (Android 4.2 JELLY_BEAN_MR1) в ОС было внесено исправление, и теперь, чтобы экспортировать провайдер, необходимо самостоятельно изменить используемый по умолчанию «false».

Итак, в нашем случае для доступа к контент-провайдеру можно будет использовать URI вида:

Фантазия разработчика при написании провайдера мало чем ограничена, дело в том, что обязательно нужно переопределить всего лишь шесть абстрактных методов (query(), insert(), update(), delete(), getType(), onCreate()). При этом никто не запрещает сделать эти методы пустыми, возвращать на любой запрос константу, результат чтения файла или обращения к сети. Тем более если твой провайдер предоставляет доступ к данным только на чтение, то методы insert(), update() и так далее ему совсем не нужны.

Логотип Mercury

И хотя никто не принуждает программиста прятать «под капотом» своего контент-провайдера базу данных, но очень часто тут встречается хорошо знакомая всем разработчикам мобильных приложений для Android SQLite. Тем более что используемый для получения данных ContentResolver всегда возвращает объект типа Cursor, что тонко намекает…

MEMENTO MORI

Итак, мы рассмотрели одно из слабых мест в Android-приложениях. Я постарался показать, насколько важно использовать по максимуму возможности по разграничению доступа, которые предлагает операционная система.

Разработчикам мобильных приложений хочется напомнить, что, написав контент-провайдер, вы принимаете решение поделиться информацией, поэтому стоит подумать, кто и в каком объеме сможет получить к ней доступ. Также многие забывают, что уязвимостям типа SQL injection подвержены не только веб-приложения. Поэтому санитизацию пользовательского ввода и использование prepared statements никто не отменял, даже под Android.

Источник

Оцените статью