Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Android Security OWASP Tips

Android Security OWASP Tips

Manoel Aranda Neto

February 07, 2017
Tweet

More Decks by Manoel Aranda Neto

Other Decks in Programming

Transcript

  1. O que é OWASP? • Comunidade online gratuita e de

    código aberto • Criada em 2011 por Mark Curphey • +42k de voluntários • Foco em segurança de aplicações, vulnerabilidades… • Publicam na área de segurança: ◦ Artigos ◦ Metodologias ◦ Documentações ◦ Ferramentas ◦ Tecnologias...
  2. OWASP - Mobile Security Project • Mobile Top 10 2016

    (RELEASE CANDIDATE) ◦ M1 - Improper Platform Usage ◦ M2 - Insecure Data Storage ◦ M3 - Insecure Communication ◦ M4 - Insecure Authentication ◦ M5 - Insufficient Cryptography ◦ M6 - Insecure Authorization ◦ M7 - Poor Code Quality ◦ M8 - Code Tampering ◦ M9 - Reverse Engineering ◦ M10 - Extraneous Functionality
  3. Local data storage • Shared Preferences • SQLite Databases •

    Internal Storage • External Storage • KeyChain and KeyStore
  4. • Shared Preferences SharedPreferences sharedPref = getSharedPreferences("key", MODE_WORLD_READABLE); SharedPreferences.Editor editor

    = sharedPref.edit(); editor.putString("username", "administrator"); editor.putString("password", "supersecret"); editor.commit();
  5. • Shared Preferences • Dados salvos em texto limpo /data/data/<PackageName>/shared_prefs/key.xml

    • MODE_WORLD_READABLE permite que todas aplicações acessem/leiam o conteúdo de key.xml • Aplicações compiladas em SDK < 17 podem ser afetadas, se rodar em Android < 4.2 (JELLY_BEAN_MR1)
  6. • Shared Preferences SharedPreferences sharedPref = getSharedPreferences("key", MODE_PRIVATE); • Apenas

    sua aplicação pode acessar • Vulnerável se o Android for rooteado • Não criptografado
  7. • Shared Preferences - SecurePreferences SharedPreferences prefs = new SecurePreferences(context,

    "userpassword", "my_user_prefs.xml"); <map> <string name="TuwbBU0IrAyL9znGBJ87uEi7pW0FwYwX8SZiiKnD2VZ7"> pD2UhS2K2MNjWm8KzpFrag==:MWm7NgaEhvaxAvA9wASUl0HUHCVBWkn3c2T1WoSAE/ g=rroijgeWEGRDFSS/hg </string> <string name="8lqCQqn73Uo84Rj">k73tlfVNYsPshll19ztma7U"> pD2UhS2K2MNjWm8KzpFrag==:MWm7NgaEhvaxAvA9wASUl0HUHCVBWkn3c2T1WoSAE/ g=:jWm8KzUl0HUHCVBWkn3c2T1WoSAE/g= </string> </map>
  8. • SQLite Databases SQLiteDatabase notSoSecure = openOrCreateDatabase("privateNotSoSecure", MODE_PRIVATE, null); notSoSecure.execSQL(

    "CREATE TABLE IF NOT EXISTS Accounts(Username VARCHAR,Password VARCHAR);"); notSoSecure.execSQL("INSERT INTO Accounts VALUES('admin','AdminPass');"); notSoSecure.close();
  9. • SQLite Databases • Dados salvos em texto limpo /data/data/<PackageName>/databases/privateNotSoSecure

    • Vulnerável se o Android for rooteado • Não criptografado • Cuidado com SQL Injection
  10. • SQLite Databases - SQLCipher net.sqlcipher.database.SQLiteDatabase secureDB = net.sqlcipher.database.SQLiteDatabase.openOrCreateDatabase( databaseFile,

    "password123", null); secureDB.execSQL( "CREATE TABLE IF NOT EXISTS Accounts(Username VARCHAR,Password VARCHAR);"); secureDB.execSQL("INSERT INTO Accounts VALUES('admin','AdminPassEnc');"); secureDB.close();
  11. • Internal Storage FileOutputStream fos = null; try { fos

    = openFileOutput(FILENAME, Context.MODE_PRIVATE); fos.write(test.getBytes()); fos.close(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); }
  12. • Internal Storage • Apenas sua aplicação pode acessar •

    Ainda vulnerável se o Android for rooteado • Não criptografado
  13. • Internal Storage - Conceal // Creates a new Crypto

    object with default implementations of a key chain KeyChain keyChain = new SharedPrefsBackedKeyChain( context, CryptoConfig.KEY_256); Crypto crypto = AndroidConceal.get().createDefaultCrypto(keyChain); // Check for whether the crypto functionality is available // This might fail if Android does not load libaries correctly. if (!crypto.isAvailable()) { return; }
  14. • Internal Storage - Conceal OutputStream fileStream = new BufferedOutputStream(

    new FileOutputStream(file)); // Creates an output stream which encrypts the data as // it is written to it and writes it out to the file. OutputStream outputStream = crypto.getCipherOutputStream( fileStream, Entity.create("entity_id")); // Write plaintext to it. outputStream.write(plainText); outputStream.close();
  15. • External Storage File file = new File (Environment.getExternalFilesDir(), "password.txt");

    String password = "SecretPassword"; FileOutputStream fos = new FileOutputStream(file); fos.write(password.getBytes()); fos.close();
  16. • External Storage • Todos Apps podem acessar • Acesso

    facilitado via USB quando habilitado modo “Transferir arquivos” • Arquivos não serão deletados quando o App for desinstalado • Não criptografado • O que fazer? Evitar/criptografar (Internal Storage)
  17. KeyChain • Armazena e recupera chaves privadas e seus respectivos

    certificados Intent intent = KeyChain.createInstallIntent(); byte[] p12 = readFile("keystore-test.pfx"); intent.putExtra(KeyChain.EXTRA_PKCS12, p12); startActivity(intent);
  18. KeyChain public class KeystoreTest extends Activity implements OnClickListener, KeyChainAliasCallback {

    @Override public void onClick(View v) { KeyChain.choosePrivateKeyAlias(this, this, new String[] { "RSA" }, null, null, -1, null); } @Override public void alias(final String alias) { Log.d(TAG, "selected alias: " + alias); } }
  19. KeyChain PrivateKey pk = KeyChain.getPrivateKey(ctx, alias); X509Certificate[] chain = KeyChain.getCertificateChain(ctx,

    alias); byte[] data = "foobar".getBytes("ASCII"); Signature sig = Signature.getInstance("SHA1withRSA"); sig.initSign(pk); sig.update(data); byte[] signed = sig.sign(); PublicKey pubk = chain[0].getPublicKey(); sig.initVerify(pubk); sig.update(data); boolean valid = sig.verify(signed); Log.d(TAG, "signature is valid: " + valid);
  20. • KeyStore • Gerar chave pública e privada • Criptografar

    com chave pública • Descriptografar com chave privada • É um container que armazena as chaves privadas • Criptografado com o PIN/Senha do usuário (lockscreen) • KeyStore é vulnerável se o Android for rooteado (Android precisa estar desbloqueado) /data/misc/keystore/
  21. • KeyStore • Chaves devem ser gerados com KeyPairGenerator •

    Utilizar mecanismos KeyStore, Cipher e SecureRandom para “garantir” a segurança • Utilizar AccountManager para login
  22. • KeyStore KeyPairGenerator kpg = KeyPairGenerator.getInstance( KeyProperties.KEY_ALGORITHM_EC, "AndroidKeyStore"); kpg.initialize(new KeyGenParameterSpec.Builder(

    alias, KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_VERIFY) .setDigests(KeyProperties.DIGEST_SHA256, KeyProperties.DIGEST_SHA512) .build()); KeyPair kp = kpg.generateKeyPair();
  23. • KeyStore KeyStore ks = KeyStore.getInstance("AndroidKeyStore"); ks.load(null); KeyStore.Entry entry =

    ks.getEntry(alias, null); if (!(entry instanceof PrivateKeyEntry)) { Log.w(TAG, "Not an instance of a PrivateKeyEntry"); return false; } Signature s = Signature.getInstance("SHA256withECDSA"); s.initVerify(((PrivateKeyEntry) entry).getCertificate()); s.update(data); boolean valid = s.verify(signature);
  24. • Cuidado quando utilizar criptografía • A chave/senha não deve

    estar em ◦ Código fonte ◦ SharedPreferences ◦ Escondido no sistema de arquivos ◦ NDK dificulta, mas não garante • Podemos fazer o seguinte ◦ Pedir PIN/Senha para o usuário quando for ler informação sensível ◦ Armazenar no servidor e solicitar em tempo real (App. só vai funcionar online)
  25. • Observações • PIN e senha fraca fica frágil com

    Brute force • Evitar algoritmos de criptografia que utilizam operações Bit (XOR ou Bit flipping) • Ofuscar código para dificultar engenharia reversa
  26. • Algoritmos de criptografia recomendados • Confidentiality: AES-256 • Integrity:

    SHA-256, SHA-384, SHA-512 • Digital signature: RSA (3072 bits and higher), ECDSA with NIST P-384 • Key establishment: RSA (3072 bits and higher), DH (3072 bits or higher), ECDH with NIST P-384
  27. • Dados sensíveis em logs • Remover logs no release

    de produção ◦ Utilizando algum parâmetro condicional via Gradle ◦ ProGuard ◦ DexGuard
  28. • Dados sensíveis em logs -assumenosideeffects class android.util.Log { public

    static boolean isLoggable( java.lang.String, int); public static int v(...); public static int i(...); public static int w(...); public static int d(...); public static int e(...); public static int wtf(...); }
  29. • Dados sensíveis na nuvem via Backup • Auto Backup

    é habilitado por padrão e enviado para Google Drive/Android Backup Service • Verificar se dados sensíveis estão criptografados • Desabilitar Backup caso não seja utilizado <application ... android:allowBackup="false"> </app>
  30. • Dados sensíveis enviado para terceiros • Uso de bibliotecas

    e SDKs desconhecidos devem ser evitados ou analisados antes do uso • Nenhum ID que possa ser mapeado para uma conta ou sessão de usuário deve ser enviado para terceiros
  31. • Dados sensíveis em cache de teclado • Não salvar

    dados digitados em campos com dados sensíveis <EditText android:id="@+id/KeyBoardCache" android:inputType="textNoSuggestions"/>
  32. • Dados sensíveis no clipboard • Não salvar dados digitados

    no clipboard em campos com dados sensíveis <EditText android:id="@+id/ClipboardCache" android:longClickable="false"/>
  33. • Dados sensíveis no clipboard EditText etxt = (EditText) findViewById(R.id.editText1);

    etxt.setCustomSelectionActionModeCallback(new Callback() { public boolean onPrepareActionMode(ActionMode mode, Menu menu) { return false; } public void onDestroyActionMode(ActionMode mode) { } public boolean onCreateActionMode(ActionMode mode, Menu menu) { return false; } public boolean onActionItemClicked(ActionMode mode, MenuItem item) { return false; } });
  34. • Dados sensíveis via IPC • Binders • Services ◦

    Bound Services ◦ AIDL • Intents • ContentProviders
  35. • Dados sensíveis via IPC private void vulnerableBroadcastFunction() { //

    ... Intent VulnIntent = new Intent(); VulnIntent.setAction("com.owasp.omtg.receiveInfo"); VulnIntent.putExtra( "ApplicationSession", "SESSIONID=A4EBFB8366004B3369044EE985617DF9"); VulnIntent.putExtra("Username", "litnsarf_omtg"); VulnIntent.putExtra("Group", "admin"); } this.sendBroadcast(VulnIntent);
  36. • Dados sensíveis via IPC • Não expor dados sensíveis

    via IPC • Se apenas sua aplicação vai utilizar esse mecanismo, utilizar LocalBroadcastManager android:exported="false" • Caso outras aplicações precisam receber esses dados, aplicar políticas de segurança utilizando <permission android:protectionLevel=["normal" | "dangerous" | "signature" | "signatureOrSystem"] />
  37. • Dados sensíveis em Screenshots • Apps maliciosos podem tirar

    screenshots continuamente em busca de dados sensíveis getWindow().setFlags( WindowManager.LayoutParams.FLAG_SECURE, WindowManager.LayoutParams.FLAG_SECURE); setContentView(R.layout.activity_main);
  38. • Dados sensíveis em memória • Objetos com dados sensíveis

    como login, senha, chaves de criptografia devem ser setadas para NULL imediatamente após uso • Utilizar objetos imutáveis para dados sensíveis, assim eles não poderão ser alterados
  39. • Políticas de segurança • Apps que vão processar dados

    sensíveis, devem ser verificados algumas políticas de segurança ◦ PIN ou senha para desbloquear o dispositivo ◦ Versão mínima do Android ◦ Detecção de USB debugging ativo ◦ Detecção de dispositivo encriptado ◦ Detecção de dispositivo rooteado
  40. • Segurança externa • Utilizar protocolos HTTPS ou SSLSocket ◦

    Existe certificado gratuito (letsencrypt) • Verificar certificado X.509 • Verificar hostname • SSL Pinning
  41. • Verificar certificado X.509 TrustManager[] trustAllCerts = new TrustManager[] {

    new X509TrustManager() { public java.security.cert.X509Certificate[] getAcceptedIssuers() { return new java.security.cert.X509Certificate[] {}; } public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException { } public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException { } }}; context.init(null, trustAllCerts, new SecureRandom());
  42. • Verificar hostname final static HostnameVerifier NO_VERIFY = new HostnameVerifier()

    { public boolean verify(String hostname, SSLSession session) { return true; } }; HostnameVerifier NO_VERIFY = org.apache.http.conn.ssl.SSLSocketFactory .ALLOW_ALL_HOSTNAME_VERIFIER;
  43. Outras dicas importantes • WebView e JavaScript habilitado é bom

    ser evitado • Verificar permissões adicionadas após Merge do Manifest • storePassword e keyPassword devem ser fortes e diferentes • Man in the middle