intive Argentina Blog

Protegiendo nuestro código – ProGuard

Luego de haber terminado el desarrollo de una aplicación Android y consultado diferentes fuentes, me encontré con que es muy sencillo obtener el código decompilado de un archivo APK usando casi cualquier decompilador java y alguna otra herramienta para convertir la apk en un jar (y sus consecuentes .class).

El problema con esto es que cualquiera que pueda tener acceso a ese APK puede acceder a datos sensibles de la aplicación, como passwords o keys que la app utiliza para autenticarse, por ejemplo, con algún servicio externo.

Por otra parte, además de tener acceso a estos datos sensibles, también lo tendría a todo nuestro código, que es perfectamente legible una vez decompilado (con excepción de algunos recursos que son referenciados por ids que, si bien pueden ser ubicados, daría un poco más de trabajo localizar).

Por ello, les propongo que echemos un vistazo a ProGuard una herramienta que nos ayudará a proteger nuestro código.


¿Qué es ProGuard?

ProGuard es una utilidad que nos brinda Android y nos permite:

  • Optimizar nuestra APK.
  • Eliminar referencias a código no utilizado.
  • Ofuscar código (útil para dificultar ataques de ingeniería inversa).
  • Disminuir el tamaño de la APK.

 

¿Cómo lo agrego a mi proyecto?

Debemos compilar el proyecto usando Gradle o Ant. En Gradle se agrega en el archivo build.gradle lo siguiente:

Por defecto, el minify suele estar en false, para tenerlo desactivado. El archivo por default “proguard-android.txt” sólo se encarga de ofuscar código: getDefaultProguardFile(‘proguard-android.txt’)

El archivo “proguard-android-optimize.txt” hace “alguna magia” de fondo para ver si puede optimizar nuestra aplicación, además de ofuscar código: getDefaultProguardFile(‘proguard-android-optimize.txt’).

 

 ¿Se podría ocultar una string para que no se vea al decompilar?

Si uno quiere esconder alguna clave o url que tiene “hardcodeada” en el código, Proguard, al ofuscar código, no lo va a ocultar porque sólo cambia nombres de atributos y clases, no valores.

Una manera de ocultar información sensible es entonces utilizar criptografía en el código fuente para claves, urls, etc.

De todas formas, lo mejor para ocultarlas a posibles ataques de ingeniería inversa sería guardar esta información en un servidor y consultarla mediante una conexión segura, cuando se necesite, desde la aplicación.

 

¿Con qué problemas podemos encontrarnos?

Podemos llegar a enfrentar problemas al usar librerías para des-serializar o serializar (GSON, Jackson, etc.). Los atributos se ofuscan y deben definirse explícitamente. En GSON, por ejemplo, puede definirse una clase de la siguiente manera:

Se deberían usar de manera explícita los annotation @SerializedName, porque sino se busca el atributo en el json con el mismo nombre del atributo (lo que pasaría en este caso con el campo email que no tiene la annotation).
Puede suceder que se eliminen clases que el proyecto usa, por lo que hay que excluírlas a mano (usando un archivo custom de reglas):

Para algunas situaciones, el archivo de configuración de ProGuard predeterminado (proguard-android.txt) alcanza y ProGuard elimina el código no utilizado. Sin embargo, en muchas situaciones son difíciles de analizar correctamente para ProGuard y se podría eliminar código que la app en cuestiónd realmente necesita. Algunos ejemplos de cuándo podría quitar el código incorrectamente incluyen:

  • Cuando la app referencie una clase solamente desde el archivo AndroidManifest.xml.
  • Cuando la app llame un método desde el JNI (Java Native Interface).
  • Cuando la app manipule código mientras correWhen your app manipulates code at runtime (con reflexión o introspección).

La prueba de la aplicación debe revelar cualquier error causado por el código eliminado de forma inapropiada, pero también puede inspeccionar qué código se eliminó revisando el archivo de salida «usage.txt» guardado en <module-name>/build/outputs/mapping/release/.

Para corregir errores y forzar a ProGuard a mantener cierto código, debemos agregar una línea de mantenimiento en el archivo de configuración de ProGuard. Por ejemplo: -keep public class <MyClass>.
¿Qué hacer con los stacktraces ofuscados?

Los stacktraces también se ofuscan y, si intentamos leerlos, se hace casi imposible seguir el origen del problema. Para esto se puede utilizar el archivo mapping.txt, que se genera al armar la APK que especifica qué atributo/clase se transformó y con qué nombre quedó finalmente. También se puede hacer de manera automática usando los scripts que nos da Android en la sdk:

Crashlytics lo utiliza por defecto así que no tenemos problemas de ver stacktraces ofuscados si lo usamos.

 

Un ejemplo de clase no ofuscada

 

Un ejemplo de clase ofuscada

 

Conclusión

ProGuard nos ayuda a mantener algunas cosas “ocultas” y a complicarle un poco las cosas a todo aquel interesado en conseguir datos de nuestro código. También es muy útil para disminuir la cantidad de clases y métodos de la aplicación y, por ende, reducir el tamaño del archivo APK generado. Sin embargo, no es una solución definitiva para esconder datos sensibles y, en ese caso, tendríamos que buscar una solución un poco más elaborada.

Gastón Goncalves

Trabaja en intive – FDV desde 2012. Desde febrero de 2015, es desarrollador  Android e integra dicha brigada. Antes, también en la compañía, fue desarrollador de Drools y Java EE. Es alumno de la carrera de Ingeniería en Informática de la Universidad de Buenos Aires. En la UBA, también fue docente ayudante de la materia Algoritmos y Programación II de la Facultad de Ingeniería.

6 comentarios