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

Android Security OWASP Tips

Android Security OWASP Tips

Avatar for Manoel Aranda Neto

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