Seguridad en aplicaciones Android: Detectar el entorno de ejecución

Consigue gratis tu cuenta

Crea una API REST con Node.js y MongoDB

En este curso vas a aprender a crear una API REST con Node.js y MongoDB, además de crear un entorno de desarrollo con Docker y Docker Compose.

Comenzar ahora
Android_seguridad_2

Una muy buena opción para aumentar la seguridad en nuestras queridas y apreciadas aplicaciones es valorar y detectar bajo que entorno está siendo ejecutada nuestra aplicación.

Este post es parte de una serie de tutoriales en los que aprenderemos a programar aplicaciones más seguras, si aun no lo has hecho, échale un vistazo al resto de publicaciones de esta serie.

Comprobar que nuestra aplicación ha sido instalada por Google Play Store y no por algún otro, cerciorarnos que está siendo ejecutada en un terminal físico y no un emulador o una máquina virtual o si la aplicación está en modo "debuggable" serán nuestros escudos en este tutorial.

Para evitar que nuestra aplicación sea funcional tras ser instalada desde cualquier otro medio que no sea por el Google Play Store, como markets alternativos por ejemplo, comprobaremos que el instalador es el que queremos mediante este código:

/**
*  Detect if Google Play Store was the installer
*/
public static boolean checkGooglePlayStore (Context context) {
    String installerPackageName = context.getPackageManager().getInstallerPackageName(context.getPackageName());
    return installerPackageName != null && installerPackageName.startsWith("com.google.android");
}

El siguiente paso será saber si esta corriendo bajo un emulador, esto lo comprobaremos de la siguiente manera:

/**
 * Detect if it runs on an emulator
*/
public static boolean isEmulator(){
    try {
            
        Class systemPropetyClazz = Class.forName("android.os.SystemProperties");
            
        boolean kernelQemu = getProperty(systemPropetyClazz, "ro.kernel.qemu").length() > 0 ;
        boolean hardwareGoldfish = getProperty(systemPropetyClazz, "ro.hardware").equals("goldfish");
        boolean modelSdk = getProperty(systemPropetyClazz, "ro.product.model").equals("sdk");
            
        if (kernelQemu || hardwareGoldfish || modelSdk){
            return true;
        }
            
    } catch (Exception e) {
        // error assumes emulator
    }
        
    return false;
}
    
private static String getProperty (Class clazz, String propertyName) throws Exception {
    return (String) clazz.getMethod("get", new Class[] {String.class}).invoke(clazz, new Object[] {propertyName});
}

Por ultimo comprobaremos que no tenemos el flag "debuggable" activado, puesto que de ser así el atacante podrá conectar via Android Debug Bridge para obtener un jugoso análisis dinámico:

/**
 * Detect if the app has the debuggable  flag enabled
 */
public static boolean isDebuggable (Context context){
    return (context.getApplicationInfo().flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0;
}

La idea en tener todas estas funciones en una única clase y llamarlas en diferentes partes del código o que sea padre de otras clases, para conseguir una app mucho más segura. ¿Como responder si alguna de las pruebas da positivo? Eso ya depende del caso y la funcionalidad de cada app, pero en general lo que nos interesará es frustrar al atacante cerrando la aplicación simplemente, limpiar la cache, o borrar las sharedpreferences por poner algunos ejemplos. Otros algo mas cabroncetes pillos pueden tener la ingeniosa idea de darle un escarmiento al entrometido, pero yo ya me libero de toda responsabilidad en este punto.


¿Quieres seguir aprendiendo?