¿Qué puede proteger realmente la ofuscación (R8/ProGuard) y qué no puede?
R8 renombra tus clases a a, b, c, pero no cifra tus strings, no detiene a Frida ni hace segura una API key hardcodeada.
APRENDE HACIENDOPon a prueba tu intuición3 pasos
¿Qué puede proteger realmente la ofuscación (R8/ProGuard) y qué no puede?
Usa los ejemplos y la explicación de la lección para comprobar tu idea.
Reflexión personal. No se envía a ningún servicio de IA.#La ofuscación nunca fue el trabajo principal
R8 (la herramienta que sustituyó a ProGuard como el shrinker integrado por defecto en el compilador de Android) existe primero para hacer tu APK más pequeño y rápido: elimina clases y métodos sin referencias (shrinking), simplifica e inlinea bytecode (optimización) y, como efecto secundario del shrinking, renombra clases, métodos y campos a nombres cortos y sin significado (ofuscación) porque a.b.c comprime mejor y no hay razón para enviar tus nombres de símbolo reales si nada externo los necesita. Los equipos de seguridad después se apoyaron en ese renombrado como disuasivo de ingeniería inversa, y es razonable hacerlo, pero vale la pena ser preciso en una entrevista sobre qué tipo de disuasivo es: eleva el costo de una lectura casual, no crea confidencialidad.
Esa distinción se nota de inmediato en cuanto descompilas un APK de release. El renombrado sobrevive el viaje de ida y vuelta por una herramienta como jadx, pero casi ningún otro significado del código lo hace:
// Código fuenteclass UserSessionRepository(private val api: AuthApi) {fun refreshToken(refreshToken: String): TokenResponse {return api.refresh(refreshToken)}}
// Descompilado desde el APK ofuscadopublic class a {private final b a;public a(b api) {this.a = api;}public TokenResponse a(String str) {return this.a.a(str);}}
Los nombres de clase y método desaparecieron, pero la forma sigue completamente intacta: una dependencia inyectada por el constructor, un método público que delega en ella, y un tipo de retorno (TokenResponse) que R8 deja tal cual porque es parte de tu modelo de datos y — algo crítico — los literales de texto no se tocan por defecto. Cualquier API key, URL base o clave criptográfica embebida como constante de texto está sentada en el archivo dex en texto plano, localizable con strings sobre los bytes crudos, con o sin ofuscación.
#Lo que sí protege razonablemente
- Lectura casual y copiar-pegar. Un ingeniero de la competencia hojeando tu código descompilado para ver "cómo implementaron X" se topa con un muro de clases
a,b,csin comentarios, sin nombres con significado y con código muerto ya eliminado — suficientemente molesto como para que la mayoría abandone antes de invertir un esfuerzo serio y financiado. - Fuga accidental de información. Las reglas
-assumenosideeffectsque eliminan las llamadas aLog.d/Log.ven los builds de release evitan que un log de depuración que olvidaste quitar termine mandando estado interno detallado a logcat en el dispositivo de cada usuario. - Tamaño y estructura del APK. Eliminar código de librerías sin usar (una fracción grande de la mayoría de los árboles de dependencias) reduce el binario y quita superficie que un atacante, de otro modo, tendría que revisar.
# proguard-rules.pro-keep class com.myapp.network.models.** { *; } # conserva clases de modelo accedidas por reflexión-assumenosideeffects class android.util.Log {public static int d(...);public static int v(...);}-allowaccessmodification-repackageclasses '' # modo full de R8: aplana todo en un solo paquete ofuscado
#Lo que no protege
- Instrumentación dinámica. Frida engancha por jerarquía de clases, firma de método y las llamadas de framework alrededor, no por si un nombre se lee como inglés con significado. Un
a.a(String)ofuscado es exactamente igual de "hookeable" queAuthRepository.refreshToken(String)— el atacante solo agrega un paso extra para averiguar qué método renombrado es cuál, normalmente observando el comportamiento en tiempo de ejecución en vez de leer nombres. - Manipulación en tiempo de ejecución en dispositivos rooteados/con jailbreak. Los scripts de bypass de SSL pinning, de checks biométricos y de detección de root operan sobre el proceso en ejecución, parchando valores de retorno de métodos o enganchándose en la capa de framework por debajo de tu código ofuscado por completo. La ofuscación no toca esa capa.
- Lógica con autoridad en el cliente. Si el cálculo de precio, el control de funciones o la validación de "esta compra es válida" vive en el cliente y solo reporta un resultado al servidor, la ofuscación solo hace que el ciclo de parchear y re-empaquetar le tome un poco más de tiempo al atacante — no hace que la autoridad del lado cliente sea segura. Esa lógica pertenece a un servidor que tú controlas.
- Re-empaquetado. Un atacante puede descompilar, parchar y re-firmar un APK con su propia clave sin importar cuán agresivamente se haya ofuscado. Defenderse de eso requiere que tu backend rechace peticiones de clientes no atestiguados o manipulados (Play Integrity API), no más ofuscación.