Permisos del sistema

Patrones de diseño

Permisos

Video

Google I/O 2015; Permisos de Android M: Prácticas recomendadas para desarrolladores

Android es un sistema operativo con privilegios independientes, en el que cada aplicación se ejecuta con una identidad de sistema distinta (ID de usuario de Linux y ID de grupo). También se separan partes del sistema en identidades distintas. Así, Linux aísla las aplicaciones entre sí y del sistema operativo.

Se ofrecen funciones de seguridad adicionales más precisas mediante un mecanismo de “permisos” que aplica restricciones en las operaciones específicas para que un proceso en particular puede realizar y permisos por URI para la dar acceso ad-hoc a elementos específicos de datos.

En este documento, se describe la manera en que los desarrolladores de aplicaciones pueden usar las funciones de seguridad provistas por Android. Se brinda información más general sobre seguridad de Android en el Proyecto de Código Abierto de Android.

Arquitectura de seguridad

Un punto central del diseño de la arquitectura de seguridad de Android consiste en que ninguna aplicación, de manera predeterminada, tiene permiso para realizar operaciones que pudieran tener consecuencias negativas para otras aplicaciones, el sistema operativo o el usuario. Esto incluye leer datos privados de los usuarios o escribir en ellos (por ejemplo, los contactos o los mensajes de correo electrónico), leer archivos de otra aplicación o escribir en ellos, acceder a una red, mantener el dispositivo activo, etc.

Debido a que cada aplicación de Android funciona en una zona de pruebas de proceso, las aplicaciones deben compartir recursos y datos de manera explícita. Hacen esto declarando los permisos que necesitan para capacidades adicionales no previstas por la zona de pruebas básica. Las aplicaciones declaran estáticamente los permisos que necesitan y el sistema Android solicita al usuario su consentimiento.

La zona de pruebas de aplicaciones no depende de la tecnología empleada para crear una aplicación. En especial, la VM Dalvik no supone un límite de seguridad y cualquier app puede ejecutar código nativo (consulta el NDK de Android). Todos los tipos de aplicaciones (Java, nativas e híbridas) se colocan en zonas de pruebas de la misma manera y tienen el mismo grado de seguridad.

Firma de aplicaciones

Todos los APK (archivos .apk) deben estar firmados con un certificado cuya clave privada esté en manos del desarrollador. Este certificado identifica al autor de la aplicación. No es necesario que el certificado lleve la firma de una autoridad de certificación; es perfectamente admisible, y común, que las aplicaciones de Android usen certificados autofirmados. El objetivo de los certificados de Android es distinguir a los autores de las aplicaciones. Esto permite al sistema otorgar o denegar a las aplicaciones el acceso a permisos de nivel de firma y otorgar o denegar a una aplicación una solicitud para usar la misma la identidad de Linux que otra aplicación.

ID de usuario y acceso a archivos

En el momento de la instalación, Android asigna a cada paquete un ID de usuario de Linux distinto. La identidad se mantiene constante durante la vida útil del paquete en ese dispositivo. En un dispositivo diferente, el mismo paquete puede tener otro ID de usuario; lo que importa es que cada paquete tenga un ID de usuario distinto en un dispositivo determinado.

Debido a que la aplicación de la seguridad tiene lugar en el nivel del proceso, el código de dos paquetes cualesquiera normalmente no puede ejecutarse en el mismo proceso, ya que deben funcionar como diferentes usuarios de Linux. Puedes usar el atributo sharedUserId en la etiqueta manifest de AndroidManifest.xml de cada uno de los paquetes para que se les asigne el mismo ID de usuario. Al hacer esto, por motivos de seguridad, los dos paquetes se tratan como si fueran una misma aplicación, con el mismo ID de usuario y permisos de archivo. Ten en cuenta que, a fin de mantener la seguridad, solo se asignará el mismo ID de usuario a dos aplicaciones que tengan la misma firma (y soliciten el mismo sharedUserId).

A los datos almacenados por una aplicación se les asignará el ID de usuario de esta; además, normalmente otros paquetes no podrán acceder a ellos. Al crear un archivo nuevo con getSharedPreferences(String, int), openFileOutput(String, int) o openOrCreateDatabase(String, int, SQLiteDatabase.CursorFactory), puedes usar los indicadores MODE_WORLD_READABLE o MODE_WORLD_WRITEABLE para permitir que cualquier otro paquete realice operaciones de lectura y escriba en él. Al establecer estos indicadores, el archivo sigue siendo propiedad de tu aplicación, pero sus permisos de lectura y escritura generales se han configurado adecuadamente para que cualquier otra aplicación pueda verlo.

Uso de permisos

Una aplicación básica de Android no tiene permisos asociados de manera predeterminada. Esto significa que no puede hacer nada que afecte negativamente la experiencia del usuario o los datos en el dispositivo. Para usar funciones protegidas del dispositivo, debes incluir una o más etiquetas <uses-permission> en el manifiesto de tu app.

Por ejemplo, una aplicación que tiene que controlar los mensajes SMS entrantes especificaría lo siguiente:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.android.app.myapp" >
    <uses-permission android:name="android.permission.RECEIVE_SMS" />
    ...
</manifest>

Si en el manifiesto de tu app se incluyen permisos normales (es decir, los que no presentan un gran riesgo para la privacidad del usuario o el funcionamiento del dispositivo), el sistema otorga automáticamente esos permisos. Si en el manifiesto de tu app se incluyen permisos riesgosos (es decir, los que podrían afectar la privacidad del usuario o el funcionamiento normal del dispositivo), el sistema solicita al usuario que otorgue explícitamente esos permisos. La manera en que Android realiza la solicitud depende de la versión del sistema y la versión del sistema objetivo de tu app:

  • Si el dispositivo tiene instalado Android 6.0 (nivel de API 23) o versiones posteriores y el atributo targetSdkVersion de la app es 23 o superior, la app solicita los permisos al usuario en el tiempo de ejecución. El usuario puede revocar los permisos en cualquier momento. Por ello, la app debe controlar si tiene los permisos cada vez que se ejecuta. Para obtener más información sobre la solicitud de permisos en tu app, consulta la guía de capacitación Cómo trabajar con permisos del sistema.
  • Si el dispositivo tiene instalado Android 5.1 (nivel de API 22) o versiones anteriores, o el atributo targetSdkVersion de la app es 22 o un valor inferior, el sistema solicita al usuario que otorgue los permisos al instalar la app. Si agregas un permiso nuevo a una versión actualizada de la app, el sistema solicita al usuario que otorgue ese permiso al actualizar la app. Una vez que el usuario instale la app, la única manera que tiene de revocar el permiso es desinstalar la app.

Muchas veces, un error en los permisos hará que se envíe SecurityException a la aplicación. Sin embargo, no es seguro que esto ocurra en todos lados. Por ejemplo, el método sendBroadcast(Intent) controla los permisos a medida que los datos se proporcionan a cada receptor, una vez que se devuelve la llamada del método, para que no recibas una excepción si hay errores en los permisos. En la mayoría de los casos, no obstante, se asentará un error de permisos en el registro del sistema.

Los permisos que proporciona el sistema Android se pueden encontrar en Manifest.permission. Cualquier aplicación también puede definir y aplicar sus propios permisos, por lo cual esta no es una lista completa de todos los permisos posibles.

Un permiso en particular se puede aplicar en varios puntos durante el funcionamiento de tu programa:

  • Cuando una llamada ingresa al sistema, para evitar que una aplicación ejecute determinadas funciones.
  • Cuando comienza una actividad, para evitar que las aplicaciones inicien actividades de otras aplicaciones.
  • Cuando se envían y reciben transmisiones, para tener control sobre quiénes pueden recibir tu transmisión o quién puede enviarte una.
  • Cuando se accede a un proveedor de contenido y se realizan operaciones en uno.
  • Cuando se realiza la vinculación con un servicio o cuando este se inicia.

Ajustes automáticos de permisos

Con el tiempo, es posible que se agreguen restricciones nuevas a la plataforma a fin de que, para usar determinadas API, tu app deba solicitar un permiso que antes no necesitaba. Debido a que las apps existentes suponen que el acceso a esas API es libre, Android puede implementar la solicitud del permiso nuevo en el manifiesto de la app para evitar dañarla en la versión nueva de la plataforma. Android toma las decisiones relacionadas con el hecho de que una app pueda necesitar el permiso según el valor indicado para el atributo targetSdkVersion. Si el valor es inferior al de la versión en la cual el permiso se agregó, Android agrega el permiso.

Por ejemplo, el permiso WRITE_EXTERNAL_STORAGE se agregó en el nivel de API 4 para restringir el acceso a los espacios de almacenamiento compartido. Si tu atributo targetSdkVersion es 3 o inferior, este permiso se agrega a la app en versiones nuevas de Android.

Advertencia: Si un permiso se agrega automáticamente a tu app, en Google Play, tu app indicará que necesita estos permisos adicionales aunque podría no necesitarlos realmente.

Para evitar esto y quitar los permisos predeterminados que no necesitas, siempre actualiza el atributo targetSdkVersion para que tenga el valor más alto posible. En la documentación de Build.VERSION_CODES, puedes ver los permisos agregados en cada versión.

Permisos normales y riesgosos

Los permisos del sistema se dividen en varios niveles de protección. Los dos niveles más importantes que debes conocer son el normal y el riesgoso.

  • Los permisos normales abarcan áreas en las cuales tu app tiene que acceder a datos o recursos fuera de su zona de pruebas, pero donde existe un riesgo mínimo para la privacidad del usuario o el funcionamiento de otras apps. Por ejemplo, el permiso para establecer el huso horario es un permiso normal. Si una app declara que necesita un permiso normal, el sistema le otorga automáticamente el permiso. Para conocer una lista completa con los permisos normales actuales, consulta Permisos normales.
  • Los permisos riesgosos abarcan áreas en las cuales la app requiere datos o recursos que incluyen información privada del usuario, o bien que podrían afectar los datos almacenados del usuario o el funcionamiento de otras apps. Por ejemplo, la capacidad de leer los contactos del usuario es un permiso riesgoso. Si una app declara que necesita un permiso riesgoso, el usuario tiene que otorgarle explícitamente el permiso.

Grupos de permisos

Todos los permisos riesgosos del sistema Android pertenecen a grupos de permisos. Si el dispositivo tiene Android 6.0 (nivel de API 23) instalado y el atributo targetSdkVersion de la app es 23 o un valor superior, el siguiente comportamiento del sistema tiene lugar cuando tu app solicita un permiso riesgoso:

  • Si una app solicita un permiso riesgoso incluido en su manifiesto y no tiene permisos actualmente en el grupo de permisos, el sistema muestra un cuadro de diálogo al usuario en el que se describe el grupo de permisos al cual la app desea acceder. En el cuadro de diálogo no se describe el permiso específico dentro de ese grupo. Por ejemplo, si una app solicita el permiso READ_CONTACTS, en el cuadro de diálogo del sistema se indica únicamente que la app necesita acceso a los contactos del dispositivo. Si el usuario brinda la aprobación, el sistema otorga a la app solamente el permiso que solicitó.
  • Si una app solicita un permiso riesgoso incluido en su manifiesto y ya tiene otro permiso riesgoso en el mismo grupo de permisos, el sistema lo otorga de inmediato sin interacción con el usuario. Por ejemplo, si a una app ya se le otorgó el permiso READ_CONTACTS y luego esta solicita el permiso WRITE_CONTACTS, el sistema lo otorga de inmediato.

Cualquier permiso puede pertenecer a un grupo de permisos; entre ellos, los normales y los que define tu app. Sin embargo, el grupo de un permiso solo afecta la experiencia del usuario si es riesgoso. Puedes ignorar el grupo de permisos para los permisos normales.

Si el dispositivo tiene instalado Android 5.1 (nivel de API 22) o versiones anteriores, o si el atributo targetSdkVersion de la app es 22 o inferior, el sistema solicita al usuario que otorgue el permiso en el momento de la instalación. Nuevamente, el sistema solo dice al usuario qué grupos de permisos necesita la app y no los permisos individuales.

Tabla 1. Permisos riesgosos y grupos de permisos.

Grupo de permisos Permisos
CALENDAR
CAMERA
CONTACTS
LOCATION
MICROPHONE
PHONE
SENSORS
SMS
STORAGE

Definición y aplicación de permisos

Para aplicar tus propios permisos, primero debes declararlos en AndroidManifest.xml con uno o más elementos <permission>.

Por ejemplo, una aplicación en la que se busca tener control sobre quiénes pueden iniciar una de las actividades de esta podría declarar un permiso para esta operación de esta manera:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.myapp" >
    <permission android:name="com.example.myapp.permission.DEADLY_ACTIVITY"
        android:label="@string/permlab_deadlyActivity"
        android:description="@string/permdesc_deadlyActivity"
        android:permissionGroup="android.permission-group.COST_MONEY"
        android:protectionLevel="dangerous" />
    ...
</manifest>

Nota: El sistema no permite que varios paquetes declaren un permiso con el mismo nombre, a menos que todos los paquetes estén firmados con el mismo certificado. Si un paquete declara un permiso, el sistema no permite que el usuario instale otros paquetes con el mismo nombre de permiso, a menos que esos paquetes estén firmados con el mismo certificado que el primero. Para evitar conflictos de nombres, recomendamos usar la notación de nombre de dominio inverso para los permisos personalizados; por ejemplo, com.example.myapp.ENGAGE_HYPERSPACE.

El atributo protectionLevel es obligatorio e indica al sistema la manera en que se debe informar al usuario cuando las aplicaciones requieren el permiso o quién está autorizado a tener ese permiso, como se describe en la documentación del vínculo.

El atributo android:permissionGroup es opcional y solo se usa para ayudar al sistema a mostrar los permisos al usuario. En la mayoría de los casos, puedes establecer este atributo en un grupo estándar del sistema (mencionado en android.Manifest.permission_group), aunque puedes definir un grupo tú mismo. Se recomienda usar un grupo existente, ya que esto simplifica la IU de permisos que se muestra al usuario.

Debes incluir un nombre y una descripción para el permiso. Estos son recursos de strings que el usuario puede ver cuando mira una lista de permisos (android:label) o información detallada de un único permiso (android:description). El nombre debe ser corto; solo unas pocas palabras que describan la funcionalidad clave que el permiso protege. La descripción debe constar de un par de oraciones que describan lo que el permiso autoriza al portador. Por convención, usamos una descripción de dos oraciones: en la primera se describe el permiso y en la segunda se advierte al usuario sobre lo que puede salir mal si se otorga el permiso a una aplicación.

A continuación se ofrece un ejemplo de una etiqueta y una descripción para el permiso CALL_PHONE:

<string name="permlab_callPhone">directly call phone numbers</string>
<string name="permdesc_callPhone">Allows the application to call
    phone numbers without your intervention. Malicious applications may
    cause unexpected calls on your phone bill. Note that this does not
    allow the application to call emergency numbers.</string>

Puedes ver los permisos definidos actualmente en el sistema usando la app Settings y el comando shell adb shell pm list permissions. Para usar la app Settings, dirígete a Settings > Applications. Elige una app y desplázate hacia abajo para ver los permisos que usa. Para los desarrolladores, la opción “-s” de la herramienta adb muestra los permisos con un aspecto similar al que observarán los usuarios:

$ adb shell pm list permissions -s
All Permissions:

Network communication: view Wi-Fi state, create Bluetooth connections, full
Internet access, view network state

Your location: access extra location provider commands, fine (GPS) location,
mock location sources for testing, coarse (network-based) location

Services that cost you money: send SMS messages, directly call phone numbers

...

Recomendaciones de permisos personalizados

Las apps pueden definir permisos personalizados propios y solicitar permisos personalizados de otras apps definiendo los elementos <uses-permission>. Sin embargo, debes analizar cuidadosamente si es necesario que tu app lo haga.

  • Si diseñas un conjunto de apps que exponen su funcionalidad entre ellas, intenta diseñarlas para que cada permiso se defina una sola vez. Debes hacer esto si las apps no están firmadas con el mismo certificado. Incluso cuando todas las apps estén firmadas con el mismo certificado, se recomienda definir cada permiso solo una vez.
  • Si la funcionalidad únicamente está disponible para las apps que tienen la misma firma que la app proveedora, es posible que puedas evitar la definición de permisos personalizados usando controles de firma. Cuando una de tus apps solicita usar otra de tus apps, la segunda app puede verificar que ambas estén firmadas con el mismo certificado antes de cumplir con la solicitud.
  • Si desarrollas un conjunto de apps que se ejecutará solamente en tus dispositivos, debes desarrollar e instalar un paquete que administre los permisos para todas las apps del conjunto. No es necesario que este paquete proporcione servicios. Simplemente declara todos los permisos y las demás apps del conjunto solicitan esos permisos con el elemento <uses-permission>.

Aplicación de permisos en AndroidManifest.xml

Puedes implementar permisos de alto nivel que restringen el acceso a todos los componentes del sistema o la aplicación a través de tu AndroidManifest.xml. Para hacer esto, incluye un atributo android:permission en el componente deseado e indica el permiso que controla el acceso al componente.

Los permisos Activity (aplicados a la etiqueta <activity>) fijan restricciones respecto de la autorización para iniciar la actividad asociada. El permiso se controla durante Context.startActivity() y Activity.startActivityForResult(); si el emisor no tiene el permiso obligatorio, la llamada genera SecurityException.

Los permisos Service (aplicados a la etiqueta <service>) fijan restricciones respecto de la autorización para iniciar o vincular el servicio asociado. El permiso se controla durante Context.startService(), Context.stopService() y Context.bindService(); si el emisor no tiene el permiso obligatorio, la llamada genera SecurityException.

Los permisos BroadcastReceiver (aplicados a la etiqueta <receiver>) fijan restricciones respecto de la autorización para enviar transmisiones al receptor asociado. El permiso se controla después de que se muestra Context.sendBroadcast(), mientras el sistema intenta proporcionar la transmisión enviada al receptor en cuestión. Como consecuencia, un error de permisos no generará una excepción para el emisor; no proporcionará la intent. De la misma manera, se puede suministrar un permiso a Context.registerReceiver() para tener control sobre quiénes pueden realizar transmisiones a un receptor registrado programáticamente. Por otro lado, se puede suministrar un permiso cuando se llama a Context.sendBroadcast() a fin de restringir los objetos BroadcastReceiver que tienen autorización para recibir la transmisión (consulta más adelante).

Los permisos ContentProvider (aplicados a la etiqueta <provider>) fijan restricciones respecto de la autorización para acceder a datos en ContentProvider. (Los proveedores de contenido disponen de un recurso adicional importante que se denomina permisos del URI, que se describe más adelante). A diferencia de los otros componentes, puedes establecer dos atributos de permisos separados: android:readPermission restringe quién puede leer del proveedor y android:writePermission restringe quién puede escribir en él. Ten en cuenta que si un proveedor está protegido con un permiso de escritura y un permiso de lectura, tener el permiso de escritura no implica que puedas realizar operaciones de lectura en un proveedor. Los permisos se controlan cuando obtienes un proveedor por primera vez (si no tienes ninguno de los permisos, se generará SecurityException) y cuando realizas operaciones en el proveedor. Para usar ContentResolver.query() se debe tener el permiso de lectura; para usar ContentResolver.insert(), ContentResolver.update() y ContentResolver.delete() se debe tener el permiso de escritura. En todos estos casos, si no tienes el permiso obligatorio, la llamada genera SecurityException.

Aplicación de permisos cuando se envían transmisiones

Además del permiso que determina quién puede enviar intents a un BroadcastReceiver registrado (como se describió antes), también puedes especificar un permiso obligatorio cuando se envía una transmisión. Llamando a Context.sendBroadcast() con una string de permiso, se exige que la aplicación de un receptor tenga ese permiso a fin de recibir la transmisión.

Ten en cuenta que tanto un receptor como un transmisor pueden requerir un permiso. Cuando esto sucede, ambos controles de permisos deben aprobarse para que la intent se proporcione al objetivo asociado.

Aplicación de otros permisos

Los permisos específicos arbitrarios se pueden aplicar en cualquier llamada a un servicio. Esto se logra con el método Context.checkCallingPermission(). Realiza una llamada con la string de permiso. Esta mostrará un número entero que indicará si se otorgó ese permiso al proceso de llamada actual. Ten en cuenta que esto solo se puede usar cuando ejecutas una llamada que ingresa de otro proceso; por lo general, mediante una interfaz IDL publicada por un servicio o proporcionada de alguna manera diferente a otro proceso.

Existen varias otras formas útiles de controlar los permisos. Si tienes el PID de otro proceso, puedes usar el método contextual Context.checkPermission(String, int, int) para verificar un permiso según ese PID. Si tienes el nombre de paquete de otra aplicación, puedes usar el método PackageManager directo PackageManager.checkPermission(String, String) para averiguar si se le otorgó un permiso específico a ese paquete en particular.

Permisos de URI

El sistema de permisos estándar descrito hasta ahora suele no ser suficiente cuando se usa con proveedores de contenido. Un proveedor de contenido puede desear protegerse con permisos de lectura y escritura, mientras que sus clientes directos también deben proporcionar URI específicos a otras aplicaciones para que puedan operar en ellas. Un ejemplo típico es el de los archivos adjuntos de una aplicación de correo. El acceso al correo debe estar protegido por permisos, ya que son datos confidenciales del usuario. Sin embargo, si un URI de una imagen adjunta se proporciona a un visor de imágenes, este no tendrá permiso para abrir el archivo adjunto, debido a que no existirá un motivo para que tenga un permiso que le permita acceder a todo el correo electrónico.

La solución para este problema son los permisos por URI: cuando inicia una actividad o muestra un resultado en una actividad, el emisor puede establecer Intent.FLAG_GRANT_READ_URI_PERMISSION o Intent.FLAG_GRANT_WRITE_URI_PERMISSION. Esto otorga a la actividad receptora permisos de acceso al URI de datos específico en la intent, independientemente de que tenga permiso para acceder a los datos en el proveedor de contenido correspondiente a la intent.

Este mecanismo permite emplear un modelo de estilo de capacidad común en el cual la interacción del usuario (abrir un archivo adjunto, seleccionar un contacto de una lista, etc.) impulsa el otorgamiento de permisos específicos. Este puede ser un recurso clave para reducir los permisos que necesitan las aplicaciones únicamente a aquellos relacionados de manera directa con su comportamiento.

El otorgamiento de permisos de URI específicos requiere, sin embargo, cierta cooperación por parte del proveedor de contenido que tiene esos URI. Se recomienda que los proveedores de contenido implementen este recurso y declaren que lo admiten mediante el atributo android:grantUriPermissions o la etiqueta <grant-uri-permissions>.

Se ofrece más información en los métodos Context.grantUriPermission(), Context.revokeUriPermission() y Context.checkUriPermission().

Continúa leyendo:

Permisos que implican requisitos de funciones
Información sobre cómo la solicitud de algunos permisos restringirá implícitamente tu app a dispositivos que incluyan la característica de hardware o software correspondiente.
<uses-permission>
Referencia de API para la etiqueta del manifiesto que declara los permisos del sistema obligatorios de tu app.
Manifest.permission
Referencia de API para todos los permisos del sistema.

También te puede interesar:

Compatibilidad con dispositivos
Información acerca de cómo funciona Android en diferentes tipos de dispositivos e introducción a la manera en que puedes optimizar tu app para cada dispositivo o restringir la disponibilidad de esta para diferentes dispositivos.
Información general de seguridad de Android
Análisis detallado del modelo de seguridad de la plataforma Android.