Control de acceso basado en permisos a componentes exportados

Categoría de OWASP: MASVS-PLATFORM: Interacción con la plataforma

Descripción general

Un permiso de Android es un identificador de cadena declarado en el manifiesto de la app para solicitar acceso a datos o acciones restringidos, que el framework de Android aplica en el tiempo de ejecución.

Los niveles de permisos de Android indican el posible riesgo asociado con el permiso:

  • Normal: Son permisos de bajo riesgo que se otorgan automáticamente en el momento de la instalación.
  • Peligrosos: Son permisos de alto riesgo que podrían permitir el acceso a datos sensibles del usuario y que requieren la aprobación explícita del usuario durante el tiempo de ejecución.
  • Firma: Se otorga solo a las apps firmadas con el mismo certificado que la app que declara el permiso, por lo general, se usa para apps del sistema o interacciones entre apps del mismo desarrollador.

Las vulnerabilidades relacionadas con los controles de acceso basados en permisos ocurren cuando el componente de una app (como la actividad, el receptor, el proveedor de contenido o el servicio) cumple con todos los siguientes criterios:

  • El componente no está asociado con ningún android:permission en el Manifest.
  • El componente realiza una tarea sensible para la que existe un permiso que el usuario ya aprobó.
  • Se exporta el componente.
  • El componente no realiza ninguna verificación de permisos manual (a nivel del manifiesto o del código).

Cuando esto sucede, una app maliciosa puede realizar acciones sensibles abusando de los privilegios del componente vulnerable y usando un proxy para los privilegios de la app vulnerable en la app maliciosa.

Impacto

La exportación de componentes vulnerables se puede usar para obtener acceso a recursos sensibles o realizar acciones sensibles. El impacto de este comportamiento no deseado depende del contexto del componente vulnerable y sus privilegios.

Mitigaciones

Exige permisos para tareas sensibles

Cuando exportes un componente con permisos sensibles, requiere esos mismos permisos para cualquier solicitud entrante. El IDE de Android Studio tiene verificaciones de lint para receptores y servicios para detectar esta vulnerabilidad y recomendar que se requieran los permisos adecuados.

Los desarrolladores pueden requerir permisos para las solicitudes entrantes declarándolos en el archivo Manifest o a nivel de código cuando implementan el servicio, como en los siguientes ejemplos.

XML

<manifest ...>
    <uses-permission android:name="android.permission.READ_CONTACTS" />

    <application ...>
        <service android:name=".MyExportService"
                 android:exported="true"
                 android:permission="android.permission.READ_CONTACTS" />

        </application>
</manifest>

Kotlin

class MyExportService : Service() {

    private val binder = MyExportBinder()

    override fun onBind(intent: Intent): IBinder? {
        // Enforce calling app has the required permission
        enforceCallingPermission(Manifest.permission.READ_CONTACTS, "Calling app doesn't have READ_CONTACTS permission.")
        // Permission is enforced, proceed with export logic
        return binder
    }

    // Inner class for your Binder implementation
    private inner class MyExportBinder : Binder() {
        // Permission is enforced, proceed with export logic
    }
}

Java

public class MyExportService extends Service {

    @Override
    public IBinder onBind(Intent intent) {
        // Enforce calling app has the required permission
        enforceCallingPermission(Manifest.permission.READ_CONTACTS, "Calling app doesn't have READ_CONTACTS permission.");

        return binder;

    }

    // Inner class for your Binder implementation
    private class MyExportBinder extends Binder {
        // Permission is enforced, proceed with export logic

    }
}

No exportes el componente.

Evita exportar componentes con acceso a recursos sensibles, a menos que sea absolutamente necesario. Para lograrlo, configura el android:exported del archivo Manifest como false para tu componente. A partir del nivel de API 31, este atributo se establece en false de forma predeterminada.

XML

<activity
    android:name=".MyActivity"
    android:exported="false"/>

Aplica permisos basados en firmas

Cuando compartes datos entre dos apps que controlas o posees, usa permisos basados en firmas. Estos permisos no requieren la confirmación del usuario y, en cambio, comprueban que la app que accede a los datos esté firmada con la misma clave. Esta configuración ofrece una experiencia del usuario más segura y simplificada. Si declaras permisos personalizados, ten en cuenta los lineamientos de seguridad correspondientes.

XML

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.myapp">
    <permission android:name="my_custom_permission_name"
                android:protectionLevel="signature" />

Extremos de una sola tarea

Implementa tu app siguiendo el principio de diseño de separación de preocupaciones. Cada extremo solo debe realizar un pequeño conjunto de tareas específicas con privilegios específicos. Esta práctica de diseño también permite al desarrollador aplicar permisos detallados para cada extremo. Por ejemplo, evita crear un extremo único que entregue el calendario y los contactos.

Recursos