Vés al contingut

Emmascarament

Enmascarament

[modifica]

Que és?

[modifica]

En Android, l'emmascarament fa refèrencia a les diferents precaucions que es poden prendre per protegir una app contra els intents de descodificació d'una app per veure el codi original (enginyeria inversa). Això pot ser una greu amenaça, especialment si la nostra app conté dades privades com passwords o números de comptes bancaris.

Mètodes d'emmascarament

[modifica]

A continuació es mostren uns exemples de mesures d'emmascarament, llistats de menys a més eficaç.

Strings.xml

[modifica]

Si hem de guardar el valor de l'API key o un string important, el més probable és que el guardem en l'arxiu "strings.xml" de la nostra aplicació. Desgraciadament, aquests valor és fàcilment visualitzable per qualsevol persona utilitzant la comanda strings. Aquesta comanda es troba inclosa en tots els sistemes operatius basats en Unix, però també es pot trobar per Windows i altres.

Per exemple, per un apk amb el següent arxiu "strings.xml" [1] :

<resources>
    <string name="app_name">HidingPasswords</string>
    <string name="hello_world">Hello world!</string>
    <string name="action_settings">Settings</string>
    <string name="server_password">My_S3cr3t_P@$$W0rD</string>
  </resources>

Si utilitzéssim la comanda strings [@ apk] | grep My[2], veuríem per consola:

$ strings app-x86-universal-debug.apk | grep My
  My_S3cr3t_P@$$W0rD

És cert, que podríem canviar el nom de la variable perquè no fos tan obvi el seu propòsit, però això no ens protegiria del fet que algú ho pogués esbrinar estudiant el nostre codi.

Source code

[modifica]

Un altre lloc on podríem guardar valors és en el mateix codi java de la nostra aplicació. Això complica l'extracció de la variable ja que aquest codi serà comprimit dintre del apk i no serà directament visible utilitzant la comanda strings.

Per exemple, per un apk amb el següent arxiu "MainActivity.java" [3]

public class MainActivity extends AppCompatActivity {
  //A simple static field to store sensitive keys
  private static final String myNaivePasswordHidingKey = "My_S3cr3t_P@$$W0rD";
  //A marginally better effort to store a key in a byte array (to avoid string analysis)
  private static final byte[] mySlightlyCleverHidingKey = new byte[]{
    'M','y','_','S','3','c','r','3','t','_','P','@','$','$','W','0','r','D','_','2'

Per trobar els passwords aquest cop caldrà descompilar el codi primer abans de fer servir la comanda strings sobre l'arxiu "classes.dex" que tindrem descompilat.

$ unzip app-x86-universal-debug.apk
$ strings classes.dex | grep My
	  My_S3cr3t_P@$$W0rD_2
	  My_S3cr3t_P@$$W0rD

Build Config

[modifica]

Podríem crear variables en l'arxiu "build.gradle" del nostre projecte i després donar valors a aquests variables dintre de "local.properties" o "gradle.properties". Aquest mètode és una bona idea si mantenim un repositori públic com Github, ja que aquests darrers fitxers són ignorats per .gitignore i per tant, no seran visibles públicament. Tot i això, un cop vulguem publicar l'app, aquests fitxers hauran de ser afegits a l'.apk i seran fàcilment visibles.

Exemple de build.gradle:

buildTypes {
  debug {
    minifyEnabled true
    buildConfigField "String", "hiddenPassword", "\"${hiddenPassword}\""
  }
}

Exemple de local.properties:

# ...
hiddenPassword=My_S3cr3t_P@$$W0rD

ProGuard/DexGuard

[modifica]

ProGuard ve incorporat en els SDK Tools des d'android 2.3 i és molt senzill d'utilitzar. Només cal activar-ho en el nostre fitxer "build.gradle":

android {
    buildTypes {
        release {
            //minifyEnabled indica si volem minimitzar y obfuscar el codi amb ProGuard.
            minifyEnabled true
            //getDefaultProguardFile('proguard-android.txt') agafa les regles de codificació per defecte, 
            //que venen amb la distribució d'android
            //'proguard-rules.pro' indica que afegeixi les regles personalitzables. 
            //Aquest fitxer es troba en la carpeta root del mòdul
            proguardFiles getDefaultProguardFile('proguard-android.txt'),
                    'proguard-rules.pro'
        }
    }
    ...
}

Nota: Si utilitzem ProGuard no podrem fer servir l'Instant run. Podem optimitzar el codi sense fer servir ProGuard durant els debug, com es veu aquí. Cal anar amb compte, però, ja que es possible que ProGuard elimini codi que cregui inútil, creant errors en la compilació. Per assegurar que una class no s'elimini cal afegir en l'arxiu de configuració ("proguard-rules.pro"):

-keep public class [Nom de la class]

o afegir @Keep abans de la declaració de la class o funció.

DexGuard és la versió comercial de ProGuard amb un preu consultable en la web oficial i variable depenent de la magnitud de la nostra app. Afegeix mesures més complexes i personalitzades.

Codificació

[modifica]

Una de les aproximacions més efectives. Consisteix en codificar el missatge utilitzant algun mètode de codificació com base64[4][5]. Això augmenta la complexitat del codi, fent-ho molt difícil per a un possible atacant de desxifrar el valor de la variable sense un estudi profund de l'aplicació. Tot i això, continua sent possible trobar aquesta informació.

A continuació podem veure un exemple[6] de codificació fent servir operacions XOR i codificació base64:

MainActivity.java

public class MainActivity extends AppCompatActivity {
    
    //Guardem la clau ja codificada amb base64, en un par de strings. Podem veure com
    //es va crear aquesta key mirant la funció  "generateKeyXorParts" del arxiu "HidingUtil.java"
    private static final String[] myCompositeKey = new String[]{
            "oNQavjbaNNSgEqoCkT9Em4imeQQ=","3o8eFOX4ri/F8fgHgiy/BS47"
    };
    
    //Utilitzant la llibreria base64 descodifiquem la clau i després les ajuntem fent un AND the byte a byte
    public void useXorStringHiding(String myHiddenMessage) {
        byte[] xorParts0 = Base64.decode(myCompositeKey[0],0);
        byte[] xorParts1 = Base64.decode(myCompositeKey[1], 0);

        byte[] xorKey = new byte[xorParts0.length];
        for(int i = 0; i < xorParts1.length; i++){
            xorKey[i] = (byte) (xorParts0[i] ^ xorParts1[i]);
        }
        //Ara que tenim la clau descodificada, la podem utilitzar per amagar el missatge
        HidingUtil.doHiding(myHiddenMessage.getBytes(), xorKey, false);
    }
}

HidingUtil.java

import android.util.Base64;
import android.util.Log;

public class HidingUtil {
    private static final String TAG = "HidingUtil";
        
    //Funció per codificar un missatge fent una operació XOR amb la clau
    public static int xorValues(byte[] msg, byte[] pwd){
        int i;
        for(i = 0; i < msg.length; i++){
            int keyOffset = i % pwd.length;
            msg[i] = (byte) (msg[i] ^ pwd[keyOffset]);
            }
        return i;
    }
        
    //Funció que codifica el missatge  cridant a xorValues i després codifica el missatge amb base64
    public static void doHiding(byte[] msg, byte[] pwd, boolean isHidden){
            
        xorValues(msg, pwd);

        if(!isHidden){
            String hiddenMessage = Base64.encodeToString(msg, 0);
            Log.i(TAG, String.format("Hidden Message: %s", hiddenMessage));
            doHiding(msg, pwd, true);
        }else{
            Log.i(TAG, String.format("Unhidden Message: %s", new String(msg)));
        }
    }

    //Funció per generar el par de claus codificades fent XOR d'un byte de la key i un número  aleatori
    //després els dos pars de claus són codificats amb base64
    public static String[] generateKeyXorParts(String key){
        String[] keyParts = new String[2];

        byte[] xorRandom = new byte[key.length()];
        byte[] xorMatch = new byte[key.length()];
        byte[] keyBytes = key.getBytes();
        for(int i = 0; i < key.length(); i++){
            xorRandom[i] = (byte)(256 * Math.random());
            xorMatch[i] = (byte) (xorRandom[i] ^ keyBytes[i]);
        }
        keyParts[0] = Base64.encodeToString(xorRandom, 0);
        keyParts[1] = Base64.encodeToString(xorMatch, 0);
        Log.i(TAG, "XOR Key Part 0: " + keyParts[0]);
        Log.i(TAG, "XOR Key Part 1: " + keyParts[1]);

        return keyParts;
    }
}

Aquí podeu veure sobre diferents metodes de codificació.

Servidor extern

[modifica]

El mètode més efectiu i recomanat per Google[7]. Consisteix en no mantenir localment cap dada personal i en comptes d'això, enviar-les a un servidor extern propi on podem aplicar mètodes de seguretat molt més estrictes. Per a fer la comunicació segura es pot utilitzar SSL o HTTPS.

SSL:

$ openssl s_client -connect wikipedia.org:443 | openssl x509 -noout -subject -issuer
subject= /serialNumber=sOrr2rKpMVP70Z6E9BT5reY008SJEdYv/C=US/O=*.wikipedia.org/OU=GT03314600/OU=See www.rapidssl.com/resources/cps (c)11/OU=Domain Control Validated - RapidSSL(R)/CN=*.wikipedia.org
issuer= /C=US/O=GeoTrust, Inc./CN=RapidSSL CA

HTTPS:

URL url = new URL("https://wikipedia.org");
URLConnection urlConnection = url.openConnection();
InputStream in = urlConnection.getInputStream();
copyInputStreamToOutputStream(in, System.out);

Conclusió

[modifica]

Per tal d'ofuscar el codi i que sigui més difícil de fer enginyeria inversa, ProGuard és una eina molt útil. Desgraciadament, cap mètode pot amagar completament el valor d'una variable. Per tant, es recomana desar tota possible informació delicada en un servidor extern.

Bibliografia

[modifica]
  1. Afegir ProGuard a un Android Project https://developer.android.com/studio/build/shrink-code.html
  2. Estratègies d'emmascarament https://rammic.github.io/2015/07/28/hiding-secrets-in-android-apps/
  3. Raonament sobre seguretat en Android http://rammic.github.io/2015/07/16/this-is-an-intervention/
  4. Connexions segures amb Android https://developer.android.com/training/articles/security-ssl.html

Referències

[modifica]
  1. «Exemple de String.xml» (en anglés). [Consulta: 29/10/2017].
  2. «Strings (Unix)» (en anglés). Wikipedia. [Consulta: 29/10/2017].
  3. «Exemple de MainActivity.java» (en anglés). [Consulta: 29/10/2017].
  4. «Codificació Base64» (en anglés). Wikipedia. [Consulta: 30/10/2017].
  5. «Documentació llibreria Base64 en Android» (en anglés). [Consulta: 30/10/2017].
  6. «Exemple codificació» (en anglés). [Consulta: 30/10/2017].
  7. «Recomendacions de Google sobre seguretat en Android» (en anglés). [Consulta: 30/10/2017].