ProGuard/R8: Offuscamento e Protezione delle App Android

Autore: IT Sectr Pubblicato: 2026-02-14 Tempo di lettura: 8 min

ProGuard e R8 sono strumenti di offuscamento, minificazione e ottimizzazione per applicazioni Android. ProGuard, creato nel 2002, è stato a lungo lo standard de facto per la protezione del codice Java. R8 è il suo successore, sviluppato da Google e integrato in Android Gradle Plugin a partire da AGP 3.4. Entrambi gli strumenti riducono le dimensioni dell'APK, rimuovono il codice morto e complicano il reverse engineering. Secondo Android Developers, R8 esegue build 2–3 volte più velocemente di ProGuard con una qualità di offuscamento comparabile.

Punti Chiave

  • ProGuard — strumento di offuscamento e ottimizzazione del bytecode Java, standard per Android dagli anni 2000
  • R8 — successore di ProGuard di Google, integrato in AGP, esegue offuscamento, minificazione e ottimizzazione in un unico passaggio
  • Offuscamento rinomina classi e metodi in nomi brevi, complicando il reverse engineering dell'app
  • Minificazione rimuove classi, metodi e campi inutilizzati, riducendo la dimensione finale di APK/AAB
  • Regole ProGuard (file .pro) controllano quali parti del codice vengono preservate, offuscate o rimosse

Cos'è ProGuard?

ProGuard è uno strumento open source (Apache 2.0) per offuscamento, minificazione, ottimizzazione e preverifica del bytecode Java. È stato sviluppato da Eric Lafarge nel 2002 come parte del progetto SourceForge. ProGuard prende in input classi Java compilate (.class) o archivi JAR e produce classi elaborate dello stesso formato, ma di dimensioni ridotte e con elementi rinominati.

Per molto tempo, ProGuard è stato l'unico standard per proteggere le applicazioni Android dal reverse engineering. Google ne ha raccomandato ufficialmente l'uso in Android SDK e ha fornito una configurazione predefinita nel file proguard-android-optimize.txt all'interno degli strumenti SDK. ProGuard funzionava come strumento separato, avviato dopo la compilazione del codice Java in bytecode e prima del confezionamento in DEX.

Architettura di ProGuard

ProGuard è costituito da quattro fasi sequenziali: shrink (rimozione delle classi inutilizzate), optimize (ottimizzazione del bytecode — inlining, rimozione del codice morto), obfuscate (rinominazione di classi, metodi e campi in nomi brevi), preverify (verifica della compatibilità JVM). Ogni fase è controllata da regole separate dai file di configurazione.

Durante la fase di offuscamento, ProGuard genera un file di mapping (mapping.txt) che mappa i nomi originali a quelli offuscati. Questo file è fondamentale per decodificare i log di crash dalle build di rilascio tramite l'utilità retrace. Senza un file di mapping, una traccia dello stack diventa un insieme di lettere a(), b(), c() senza possibilità di ripristinare il contesto originale.

Fase ProGuardScopoRisultato
ShrinkAnalisi del grafo delle chiamate e rimozione del codice mortoMeno classi nell'APK
OptimizeInlining dei metodi, rimozione dei parametri inutilizzatiEsecuzione del codice più veloce
ObfuscateRinominazione di classi, campi e metodiProtezione dal reverse engineering
PreverifyAggiunta di attributi StackMap per JVMCompatibilità Java 6+

Cos'è R8?

R8 è uno strumento di offuscamento e minificazione di prossima generazione di Google, presentato per la prima volta in Android Studio 3.3 (novembre 2018) e diventato standard in AGP 3.4 (agosto 2019). A differenza di ProGuard, R8 fa parte del compilatore D8/R8 che converte il bytecode Java in formato DEX. R8 esegue tutte le fasi — offuscamento, minificazione e ottimizzazione — in un unico passaggio, senza passare file intermedi tra gli strumenti.

Google ha sviluppato R8 con due obiettivi: accelerare le build (ProGuard funzionava come strumento esterno) e garantire un'integrazione perfetta con lo stack Android moderno (Desugar, Core Library Desugaring, D8). R8 è scritto in Kotlin e Java e fa parte del repository R8/Desugar su AOSP (Android Open Source Project).

Un importante vantaggio di R8 è la piena compatibilità all'indietro con le regole ProGuard. I file .pro esistenti funzionano senza modifiche. R8 supporta persino direttive ProGuard specifiche, tra cui -whyareyoukeeping, -printconfiguration e -printmapping. Ciò significa che la transizione da ProGuard a R8 è trasparente: basta aggiornare AGP.

kotlin
// build.gradle.kts — attivazione di R8 tramite minifyEnabled
android {
    buildTypes {
        getByName("release") {
            isMinifyEnabled = true
            isShrinkResources = true

            proguardFiles(
                // Configurazione di base da Android SDK
                getDefaultProguardFile("proguard-android-optimize.txt"),
                // Regole personalizzate del progetto
                "proguard-rules.pro"
            )
        }
    }
}

Il codice mostra una configurazione standard di build di rilascio. Il flag isMinifyEnabled = true attiva R8 per offuscamento e ottimizzazione. isShrinkResources = true rimuove inoltre le risorse inutilizzate. getDefaultProguardFile carica le regole predefinite dall'SDK, mentre proguard-rules.pro contiene le impostazioni specifiche del progetto.

Offuscamento del Codice in Android

L'offuscamento è il processo di trasformazione del codice sorgente in una forma difficile da analizzare per gli esseri umani ma che mantiene la piena funzionalità. Nel contesto Android, l'offuscamento significa rinominare classi, metodi e campi in nomi brevi e privi di significato: com.example.app.auth.LoginManager diventa a.a.a, il metodo authenticateUser diventa a, il campo userToken diventa b.

Perché l'Offuscamento è Necessario

I file APK Android sono archivi che possono essere aperti con qualsiasi archiviatore (ZIP, 7z, WinRAR). Senza offuscamento, un aggressore ottiene una mappa completa dell'applicazione: nomi dei pacchetti, classi, metodi e campi. Strumenti come jadx o Bytecode Viewer possono ripristinare codice Java quasi originale da file DEX in pochi secondi. L'offuscamento non rende il codice invulnerabile, ma aumenta significativamente la barriera d'ingresso: invece di nomi significativi, il lettore vede a(), b(), c().

Obiettivi tipici dell'offuscamento: proteggere la logica commerciale (algoritmi, formule di calcolo), ostacolare il furto di chiavi API e token, prevenire la sostituzione di classi tramite reflection e prevenire la modifica e il reimpacchettamento dell'APK (attacco di repackage). In pratica, il 70% dei compiti viene risolto solo con la rinomina — ecco perché si usano ProGuard/R8.

Esempio di Regole ProGuard

Di seguito è riportato un tipico file proguard-rules.pro per un progetto Android con Retrofit, Gson e Parcelable. Le regole -keep preservano le classi e i metodi necessari per il funzionamento delle librerie tramite reflection. Senza queste regole, R8 rimuoverà o rinominerà le classi a cui la libreria accede per nome stringa.

pro
# =====================
# Retrofit — preservazione delle interfacce
# =====================
-keep,allowobfuscation,allowshrinking interface retrofit2.** { *; }
-keepattributes Signature, Exceptions

# =====================
# Gson — serializzazione JSON
# =====================
-keepclassmembers class * {
    @com.google.gson.annotations.SerializedName <fields>;
}
-keep class *.serialization.** {
    <fields>;
}

# =====================
# Parcelable — Creator
# =====================
-keepclassmembers class * implements android.os.Parcelable {
    public static final android.os.Parcelable$Creator CREATOR;
}

# =====================
# Logging — rimozione dei log dalla release
# =====================
-assumenosideeffects class android.util.Log {
    public static boolean isLoggable(String, int);
    public static int v(...);
    public static int d(...);
    public static int i(...);
    public static int w(...);
    public static int e(...);
}

# =====================
# Classi dati Kotlin — preservazione dei costruttori
# =====================
-keepclassmembers class * {
    @kotlin.Metadata <fields>;
}

# =====================
# Activity — punto di ingresso
# =====================
-keep class * extends android.app.Activity {
    @android.annotation.SuppressLint <methods>;
}

Ogni direttiva in un file .pro risolve un compito specifico. -keep impedisce che l'intera classe venga rimossa o rinominata. -keepclassmembers protegge solo i membri della classe (campi e metodi) ma consente di rimuovere la classe stessa se non utilizzata. -assumenosideeffects dice a R8 che una chiamata a metodo non ha effetti collaterali e può essere rimossa in sicurezza. La direttiva -keepattributes preserva i metadati nel bytecode — annotazioni, firme, eccezioni.

La regola -keep,allowobfuscation,allowshrinking per Retrofit consente a R8 di rinominare le interfacce ma non di rimuoverle. Ciò è necessario perché Retrofit accede alle interfacce tramite proxy dinamici (java.lang.reflect.Proxy) e la rimozione causerebbe ClassNotFoundException in fase di esecuzione. Allo stesso modo, Gson utilizza la reflection per accedere ai campi annotati con @SerializedName — senza -keepclassmembers i campi verranno rimossi come inutilizzati.

Minificazione e ShrinkResources

La minificazione (shrinking) è il processo di rimozione del codice e delle risorse inutilizzati dalla build finale. ProGuard e R8 analizzano il grafo delle chiamate a partire dai punti di ingresso (Activity, Service, BroadcastReceiver) e rimuovono classi e metodi che non possono essere raggiunti attraverso la catena di chiamate. ShrinkResources è una fase aggiuntiva che rimuove le risorse inutilizzate da res/ (layout, drawable, string, color).

La minificazione offre il massimo beneficio nei progetti grandi con librerie. Uno scenario tipico: un progetto utilizza il 10% del codice di una libreria collegata (ad esempio Google Play Services). Senza minificazione, tutto il codice della libreria finisce nell'APK. Con la minificazione, R8 rimuove il 70–90% del codice della libreria, lasciando solo le classi e i metodi effettivamente utilizzati. Ciò influisce direttamente sulle dimensioni dell'APK, sui tempi di caricamento e sul consumo di memoria.

ShrinkResources in Azione

Il meccanismo ShrinkResources funziona in tandem con la minificazione del codice. Dopo che R8 ha determinato quali classi vengono utilizzate, il shrinking delle risorse analizza i riferimenti alle risorse dal codice: R.layout.main, R.drawable.icon, getString(R.string.title). Tutte le risorse senza un riferimento diretto o indiretto vengono rimosse dall'APK o AAB finale. Ciò viene fatto utilizzando il file delle risorse resources.arsc e le cartelle res/.

Una sfumatura importante: le risorse possono essere accedute tramite getIdentifier() o Resources.getResourceName() per nome stringa, bypassando la classe R. In tali casi, R8 non vede un collegamento diretto e può rimuovere una risorsa che è effettivamente utilizzata. Per proteggere tali risorse, esiste la direttiva -keep class **.R$* { *; } — preserva tutti gli identificatori della classe R.

xml
<!-- Esempio: risorsa utilizzata solo tramite getIdentifier() -->
<string name="dynamic_title_welcome">Benvenuto</string>
<string name="dynamic_title_share">Condividi</string>

<!-- Codice Kotlin che accede per stringa -->
<!-- val title = getString(resources.getIdentifier( -->
<!--     \"dynamic_title_${type}\", \"string\", packageName)) -->

In questo caso, R8 non vede un riferimento statico a dynamic_title_welcome nella classe R perché l'accesso avviene tramite getIdentifier con un nome dinamico. Per preservare tali risorse, aggiungi la direttiva -keepclassmembers class **.R$string { *; } a proguard-rules.pro — impedisce la rimozione di qualsiasi campo da tutte le classi R$string.

DirettivaScopoEsempio
-keepPreserva la classe e tutti i suoi membri-keep class com.example.api.** { *; }
-keepclassmembersPreserva solo i membri della classe-keepclassmembers class * { @SerializedName <fields>; }
-keepattributesPreserva i metadati del bytecode-keepattributes *Annotation*, Signature
-assumenosideeffectsRimuove le chiamate senza effetti collaterali-assumenosideeffects class Log { d(...); }
-dontwarnSopprime gli avvisi-dontwarn com.example.legacy.**

R8 vs ProGuard: Differenze Chiave

Nonostante R8 sia il successore di ProGuard, esistono differenze fondamentali tra gli strumenti in termini di architettura, prestazioni e comportamento. Google ha ufficialmente interrotto il supporto di ProGuard in Android Gradle Plugin a partire da AGP 7.0, ma ProGuard continua a essere utilizzato in progetti che richiedono un comportamento di ottimizzazione specifico non disponibile in R8.

Tabella Comparativa

CaratteristicaProGuardR8
SviluppatoreGuardSquare (Eric Lafarge)Google
Anno di rilascio20022018 (stabile nel 2019)
Architettura4 fasi separate (shrink → optimize → obfuscate → preverify)Singolo passaggio: shrink + optimize + obfuscate simultaneamente
Integrazione AGPStrumento esterno, eseguito dopo javacIntegrato nel compilatore D8 DEX
Velocità di build2–3 volte più lentoPiù veloce grazie al passaggio unico e all'integrazione nativa
Supporto KotlinLimitato (problemi con inline, lambda, coroutine)Completo: coroutine, funzioni inline, data class
File di mappingmapping.txt (compatibile con retrace)mapping.txt (stesso formato)
Personalizzazione ottimizzazione60+ opzioni -optimizationpasses, -optimizationsLimitata: la maggior parte delle ottimizzazioni abilitate per impostazione predefinita
Stato del supportoSostituito da R8 (AGP 7.0+ non lo usa)Sviluppo attivo, parte di AOSP

Quando R8 Può Rompere la Build

R8 è più aggressivo di ProGuard nel rimuovere il codice che considera morto. Ciò porta a situazioni in cui la build di debug funziona ma la build di rilascio si blocca con ClassNotFoundException o NoSuchMethodException. Casi tipici: librerie che utilizzano la reflection per nome di classe (Gson, Moshi, Retrofit, Room, Dagger); chiamate ServiceLoader o java.util.ServiceLoader; proxy dinamici (java.lang.reflect.Proxy); metodi nativi (JNI). La soluzione è aggiungere -keep per tutte le classi chiamate tramite reflection.

pro
# Problemi tipici di reflection — R8 non vede il collegamento statico

# Room — preservazione di DAO e migrazioni
-keep class * extends androidx.room.RoomDatabase { *; }
-keep class *.DatabaseMigrations { *; }

# Dagger / Hilt — preservazione dei componenti
-keep class * extends dagger.hilt.android.components.** { *; }

# JNI — non rinominare i metodi nativi
-keepclasseswithmembernames class * {
    native <methods>;
}

# Data Binding — preservazione delle classi Binding
-keep class *.databinding.** { *; }

Se dopo aver aggiunto le regole la build si blocca ancora, utilizza il flag -printconfiguration full-config.txt in proguard-rules.pro. R8 genererà un file di configurazione completo che mostra quali regole vengono applicate e quali classi vengono preservate. Utile anche la direttiva -whyareyoukeeping class com.example.MyClass — mostra il motivo per cui R8 ha deciso di preservare la classe in questione.

Configurazione delle Regole ProGuard

La corretta configurazione delle regole ProGuard è la chiave per un offuscamento stabile senza bug in fase di esecuzione. Di seguito è riportato un processo di configurazione passo passo per un nuovo progetto o per un progetto in cui l'offuscamento causa errori.

Passo 1: Configurazione di Base

Inizia includendo il file standard di Android SDK — proguard-android-optimize.txt. Contiene regole per i componenti Android di base: Activity, Service, BroadcastReceiver, ContentProvider, View, Fragment. Questo file si trova nella cartella SDK: $ANDROID_HOME/tools/proguard/proguard-android-optimize.txt. Se usi AGP, getDefaultProguardFile lo caricherà automaticamente.

Passo 2: Librerie

Ogni libreria popolare ha regole ProGuard raccomandate. Retrofit, OkHttp, Glide, Fresco, Coil, Room, Dagger/Hilt, Kotlin Coroutines — tutte richiedono regole -keep specifiche. Di solito le regole sono incluse nella libreria AAR e vengono collegate automaticamente tramite le regole consumer. Verifica che la libreria fornisca un file proguard.txt all'interno dell'AAR — questo indica che le regole sono già state considerate.

Passo 3: Test della Build di Rilascio

Prima della pubblicazione, assicurati di testare la build di rilascio su un dispositivo reale o emulatore. I problemi di offuscamento si manifestano solo in fase di esecuzione. Verifica: autenticazione (login/registrazione), caricamento dati dalla rete, navigazione tra schermate, fotocamera e galleria, notifiche push, Deeplink, WebView. Ogni crash nella build di rilascio deve essere decodificato con retrace utilizzando il file di mapping e vanno aggiunte le regole -keep mancanti.

Passo 4: File di Mapping e CI

Il file di mapping viene generato in build/outputs/mapping/release/mapping.txt. Questo file deve essere conservato: senza di esso è impossibile decodificare i log di crash da Google Play Console. Includi mapping.txt nel tuo sistema di controllo versione o caricalo come artefatto CI. Google Play Console accetta il file di mapping automaticamente quando carichi un AAB con uploading mapping.txt abilitato.

Di seguito è riportato un flusso di lavoro completo di configurazione dell'offuscamento nel file proguard-rules.pro con commenti per ogni gruppo di regole.

pro
# ===========================================
# proguard-rules.pro — esempio completo
# ===========================================

# --- Impostazioni generali ---
-keepattributes *Annotation*, Signature, Exceptions, InnerClasses, EnclosingMethod
-dontpreverify

# --- Componenti Android ---
-keep public class * extends android.app.Activity
-keep public class * extends android.app.Service
-keep public class * extends android.content.BroadcastReceiver
-keep public class * extends android.content.ContentProvider
-keep public class * extends android.app.Fragment
-keep public class * extends androidx.fragment.app.Fragment
-keep public class * extends android.view.View

# --- OkHttp / Retrofit ---
-dontwarn okhttp3.**
-dontwarn okio.**
-keep class retrofit2.** { *; }
-keepattributes Exceptions

# --- Gson / Moshi ---
-keepclassmembers class * {
    @com.google.gson.annotations.SerializedName <fields>;
}
-keep class com.google.gson.** { *; }

# --- Firebase ---
-keep class com.google.firebase.** { *; }
-keep class com.google.android.gms.** { *; }

# --- Kotlin Coroutines ---
-keepnames class kotlinx.coroutines.internal.MainDispatcherFactory {}
-keepnames class kotlinx.coroutines.CoroutineExceptionHandler {}

# --- Serializzazione ---
-keepclassmembers class * implements java.io.Serializable {
    private static final java.io.ObjectStreamField[] serialPersistentFields;
    private void writeObject(java.io.ObjectOutputStream);
    private void readObject(java.io.ObjectInputStream);
    java.lang.Object writeReplace();
    java.lang.Object readResolve();
}

# --- Solo R8: preservazione forzata ---
# (ProGuard ignora questa direttiva)
-keep,allowobfuscation class * implements android.os.Parcelable {
    public static final android.os.Parcelable$Creator CREATOR;
}

Dopo la configurazione, esegui la build: ./gradlew assembleRelease. Verifica che i file siano apparsi in build/outputs/mapping/release/: mapping.txt (mappatura dei nomi originali a quelli offuscati), seeds.txt (classi preservate dalle regole -keep), usage.txt (classi rimosse durante la minificazione). La dimensione dell'APK dopo l'offuscamento dovrebbe diminuire del 20–50% a seconda del numero di librerie collegate.

Domande Frequenti

In cosa R8 si differenzia da ProGuard?

R8 è il successore di ProGuard, sviluppato da Google. R8 esegue offuscamento, minificazione e ottimizzazione in un unico passaggio, funziona 2–3 volte più velocemente di ProGuard ed è integrato direttamente in Android Gradle Plugin. ProGuard utilizza quattro fasi separate e richiede esecuzione esterna. A partire da AGP 7.0, ProGuard non viene utilizzato — R8 funziona per impostazione predefinita.

Devo scrivere regole ProGuard quando uso R8?

Sì, R8 utilizza le stesse regole ProGuard (file .pro). Le direttive -keep, -keepclassmembers, -keepattributes, -assumenosideeffects funzionano in modo identico. Le regole di base si trovano in proguard-android-optimize.txt dell'Android SDK, mentre le regole specifiche delle librerie (Retrofit, Room, Gson) vengono aggiunte in proguard-rules.pro del progetto. Senza queste regole, R8 potrebbe rimuovere le classi necessarie per le librerie che funzionano tramite reflection.

Come abilitare R8 in un progetto Android?

R8 è abilitato per impostazione predefinita in Android Gradle Plugin a partire da AGP 3.4. Per attivare la minificazione, imposta isMinifyEnabled = true nel blocco release buildType del file build.gradle.kts. Il flag aggiuntivo isShrinkResources = true abilita la rimozione delle risorse inutilizzate. In gradle.properties, puoi disabilitare forzatamente R8 tramite android.enableR8=false, ma non è raccomandato — R8 è più veloce e stabile.

Cos'è l'offuscamento del codice in Android?

Offuscamento — rinomina di classi, metodi e campi in nomi brevi privi di significato (a, b, c). La classe com.example.app.auth.LoginManager diventa a.a.a, il metodo authenticateUser diventa a. Ciò complica il reverse engineering dell'applicazione ma non influisce sulla logica di esecuzione. ProGuard e R8 rinomina solo gli elementi non protetti dalle regole -keep. Il file di mapping preserva la corrispondenza dei nomi originali e offuscati per decodificare i log di crash.

Come eseguire il debug di un log di crash da un'applicazione offuscata?

Per decodificare una traccia dello stack, utilizza l'utilità retrace (parte del SDK ProGuard/R8). Comando: retrace mapping.txt crash-stacktrace.txt. Il file di mapping si trova in build/outputs/mapping/release/mapping.txt. Google Play Console supporta anche il caricamento di mapping.txt durante la pubblicazione di un AAB — i log di crash vengono decodificati automaticamente nella console. Senza un file di mapping, la traccia dello stack conterrà solo nomi offuscati come a.b.c(), inutile per il debug.

Riepilogo

  • ProGuard — un classico strumento di offuscamento e ottimizzazione del bytecode Java, composto da quattro fasi sequenziali
  • R8 — un moderno successore di Google, integrato in AGP, che esegue tutte le fasi in un unico passaggio con prestazioni 2–3 volte superiori
  • Offuscamento rinomina classi, metodi e campi in nomi brevi, complicando il reverse engineering e proteggendo la logica commerciale dell'applicazione
  • Minificazione rimuove codice e risorse inutilizzati, riducendo le dimensioni dell'APK del 20–50% nei progetti tipici
  • Regole ProGuard (file .pro) controllano il comportamento dell'offuscamento — le direttive -keep, -keepclassmembers, -assumenosideeffects specificano quali elementi preservare, rimuovere o rinominare
  • File di mapping (mapping.txt) è un artefatto di build di importanza critica per decodificare i log di crash dalle build di rilascio tramite retrace
  • Test della build di rilascio su un dispositivo reale è obbligatorio — i problemi di offuscamento si manifestano solo in fase di esecuzione e richiedono l'aggiunta di regole -keep mancanti

Svilupperemo un'applicazione mobile chiavi in mano

IT Sectr crea applicazioni iOS e Android per startup e aziende dal 2017. Ti consulteremo e ti proporremo la soluzione migliore.

Discuti il progetto

Leggi anche