Java For Android

Recommendations for OWASP Mobile TOP 10 2016

M1: Improper Platform Usage

  • Set android:exported=false in the manifest, for the components being used in the application.
  • secure coding and configuration practices on the server-side of the mobile application.
  • Components such as Intent, Container, e.t.c should not be exported.

M2: Insecure Data Storage

Insecure Logging

  • use tools like ProGuard (included in Android Studio). ProGuard is a free Java class file shrinker, optimizer, obfuscator, and preverifier. It detects and removes unused classes, fields, methods, and attributes and can also be used to delete logging-related code.
  • To determine whether all the android.util.Log class' logging functions have been removed, check the ProGuard configuration file (proguard-project.txt) for the following options
 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(...);
}

Insecure Local Storage of Sensitive Data

  • For local storage the enterprise android device administration API can be used to force encryption to local file-stores using “setStorageEncryption”
  • For SD Card Storage some security can be achieved via the ‘javax.crypto’ library. You have a few options, but an easy one is simply to encrypt any plain text data with a master password and AES 128.
  • Ensure any shared preferences properties are NOT MODE_WORLD_READABLE unless explicitly required for information sharing between apps.
  • Avoid exclusively relying upon hardcoded encryption or decryption keys when storing sensitive information assets.
  • Consider providing an additional layer of encryption beyond any default encryption mechanisms provided by the operating system.
  • set FLAG_SECURE to protect application from screen capturing
getWindow().setFlags(WindowManager.LayoutParams.FLAG_SECURE,
                WindowManager.LayoutParams.FLAG_SECURE);

setContentView(R.layout.activity_main);

Using SQLiteDatabase

  • With the library SQLCipher, SQLite databases can be password-encrypted.
SQLiteDatabase secureDB = SQLiteDatabase.openOrCreateDatabase(database, "password123", null);
secureDB.execSQL("CREATE TABLE IF NOT EXISTS Accounts(Username VARCHAR,Password VARCHAR);");
secureDB.execSQL("INSERT INTO Accounts VALUES('admin','AdminPassEnc');");
secureDB.close();

If encrypted SQLite databases are used, determine whether the password is hard-coded in the source, stored in shared preferences, or hidden somewhere else in the code or filesystem. Secure ways to retrieve the key include:

  • Asking the user to decrypt the database with a PIN or password once the app is opened (weak passwords and PINs are vulnerable to brute force attacks)
  • Storing the key on the server and allowing it to be accessed from a web service only (so that the app can be used only when the device is online)

Using Realm Databases

  • The database and its contents can be encrypted with a key stored in the configuration file.
//the getKey() method either gets the key from the server or from a KeyStore, or is deferred from a password.
RealmConfiguration config = new RealmConfiguration.Builder()
  .encryptionKey(getKey())
  .build();
Realm realm = Realm.getInstance(config);
  • If the database is not encrypted, you should be able to obtain the data. If the database is encrypted, determine whether the key is hard-coded in the source or resources and whether it is stored unprotected in shared preferences or some other location.

Using Internal Storage

  • Files saved to internal storage are containerized by default and cannot be accessed by other apps on the device. When the user uninstalls your app, these files are removed. The following code would persistently store sensitive data to 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();
}
  • You should check the file mode to make sure that only the app can access the file. You can set this access with MODE_PRIVATE. Modes such as MODE_WORLD_READABLE (deprecated) and MODE_WORLD_WRITEABLE (deprecated) may pose a security risk.

There are several different open-source libraries that offer encryption capabilities specific for the Android platform.

  • Java AES Crypto - A simple Android class for encrypting and decrypting strings.
  • SQL Cipher - SQLCipher is an open source extension to SQLite that provides transparent 256-bit AES encryption of database files.
  • Secure Preferences - Android Shared preference wrapper than encrypts the keys and values of Shared Preferences

Sensitive data exposure via IPC mechanisms

  • Determine whether the value of the export tag (android:exported) is “true”. Even if it is not, the tag will be set to “true” automatically if an has been defined for the tag. If the content is meant to be accessed only by the app itself, set android:exported to “false”. If not, set the flag to “true” and define proper read/write permissions.
  • Determine whether the data is being protected by a permission tag (android:permission). Permission tags limit exposure to other apps.
  • Determine whether the android:protectionLevel attribute has the value signature. This setting indicates that the data is intended to be accessed only by apps from the same enterprise (i.e., signed with the same key). To make the data accessible to other apps, apply a security policy with the element and set a proper android:protectionLevel. If you use android:permission, other applications must declare corresponding elements in their manifests to interact with your content provider. You can use the android:grantUriPermissions attribute to grant more specific access to other apps; you can limit access with the element

Sensitive Data Disclosure Through the User Interface

do this setting.

android:inputType="textPassword"

Sensitive Data Leakage Via Memory Leakage

  • Do not keep long-lived references to a context-activity (a reference to an activity should have the same life cycle as the activity itself)
  • Try using the context-application instead of a context-activity
  • Avoid non-static inner classes in an activity if you don’t control their life cycle, use a static inner class and make a weak reference to the activity inside. The solution to this issue is to use a static inner class with a WeakReference to the outer class, as done in ViewRoot and its W inner class for instance
  • A garbage collector is not an insurance against memory leaks

M3: Insecure Communication

  • It is imperative to apply SSL/TLS to transport channels used by the mobile app to transmit sensitive information, session tokens, or other sensitive data to a backend API or web service.
  • Account for outside entities like third-party analytics companies, social networks, etc. by using their SSL versions when an application runs a routine via the browser/webkit. Avoid mixed SSL sessions as they may expose the user’s session ID.
  • Ensure usage of strong, industry standard cipher suites with appropriate key lengths.
  • Use certificates signed by a trusted CA provider.
  • Never allow self-signed certificates, and consider certificate pinning for security conscious applications.
  • Always require SSL chain verification.
  • Only establish a secure connection after verifying the identity of the endpoint server using trusted certificates in the keychain.
  • In case the mobile app detects an invalid certificate, alert users through the UI.
  • Avoid sending sensitive data over alternate channels (e.g, SMS, MMS, or notifications).
  • Apply a separate layer of encryption to any sensitive data before it is given to the SSL channel. In the event that future vulnerabilities are discovered in the SSL implementation, the encrypted data will provide a secondary defense against confidentiality violation

M4: Insecure Authentication

  • Do not allow the loading of app data unless the server has authenticated a user session. Storing app data locally can create faster loading, but it can compromise on user security;
  • Wherever local storage of data becomes an eventuality, make sure that it is encrypted with an encrypted key derived from the login credentials of the user, thus forcing the app to authenticate the app data at least once;
  • Persistent authentication requests should be stored on the server and not locally. Always remember to caution the user when they opt-in for the remember-me option;
  • The security team needs to be careful with using device-centric authorization tokens in the app since a stolen device app becomes vulnerable. A device-centric authorization token for an app ensures that the app cannot be accessed if the new owner of the device changes the device access password;
  • Since unauthorized physical access of mobile devices is common, the security team should enforce periodic authentication of user credentials and logouts from the server-side;
  • Make sure that the user is forced to choose alphanumeric characters for passwords. They are more secure than simple letter combinations. Alongside this, the developer can force the user to identify an image or word before allowing access to the app. The two-factor authentication method is fast gaining currency

M5: Insufficient Cryptography

  • You should stop specifying a security provider and use the default implementation (AndroidOpenSSL, Conscrypt).

  • You should stop using Crypto security provider and its SHA1PRNG as they are deprecated.

  • You should specify a security provider only for the Android Keystore system.

  • You should stop using Password-based encryption ciphers without IV.

  • You should use KeyGenParameterSpec instead of KeyPairGeneratorSpec

  • Avoid using below listed cryptographic algorithms as these are known to be weak

    • DES, 3DES
    • RC2
    • RC4
    • BLOWFISH
    • MD4
    • MD5
    • SHA1
  • The following algorithms are recommended:

  • Confidentiality algorithms: AES-GCM-256 or ChaCha20-Poly1305

  • Integrity algorithms: SHA-256, SHA-384, SHA-512, Blake2, the SHA-3 family

  • Digital signature algorithms: RSA (3072 bits and higher), ECDSA with NIST P-384

  • Key establishment algorithms: RSA (3072 bits and higher), DH (3072 bits or higher), ECDH with NIST P-384

  • you should always rely on secure hardware (if available) for storing encryption keys, performing cryptographic operations.

M6: Insecure Authorization

  • Verify the roles and permissions of the authenticated user using only information contained in backend systems. Avoid relying on any roles or permission information that comes from the mobile device itself;

  • Backend code should independently verify that any incoming identifiers associated with a request (operands of a requested operation) that come along with the identify match up and belong to the incoming identity

  • When defining API routes it is necessary to include both authentication and authorization middlewares.

    • Non Compliant Code:
      router.put('/accounts/:username/notes/:note', auth, async (req, res, next) => {
          // ...
      });
      router.get('/accounts/:username/notes', auth, async (req, res, next) => {
          // ...
      });
    
    
  • The above code only includes the authentication middleware whereas the below code includes both authorization and authentication middleware.

    • Compliant Code
     router.put('/accounts/:username/notes/:note', [auth, ownership], async (req, res, next) => {
         // ...
     });
    
     router.get('/accounts/:username/notes', [auth, ownership], async (req, res, next) => {
         // ...
     });
    
    

M7: Client Code Quality

  • Maintain consistent coding patterns that everyone in the organization agrees upon;
  • Write code that is easy to read and well-documented;
  • When using buffers, always validate that the the lengths of any incoming buffer data will not exceed the length of the target buffer;
  • Via automation, identify buffer overflows and memory leaks through the use of third-party static analysis tools; and
  • Prioritize solving buffer overflows and memory leaks over other ‘code quality’ issues.
  • For a release build, this attribute should always be set to “false” (the default value).
  • Dynamic symbols can be stripped via the visibility compiler flag. Adding this flag causes gcc to discard the function names while preserving the names of functions declared as JNIEXPORT.
    • Make sure that the following has been added to build.gradle:
  externalNativeBuild {
    cmake {
        cppFlags "-fvisibility=hidden"
    }
} 
   
  • Before Using third party libraries check whether the library has a version in which the vulnerability is patched. If not, check whether the vulnerability actually affects the application.
  • Make sure that all confidential information handled by high-risk applications is always wiped during execution of the finally blocks
  • Adding a general exception handler for uncaught exceptions is a best practice for resetting the application’s state when a crash is imminent:
public class MemoryCleanerOnCrash implements Thread.UncaughtExceptionHandler {

    private static final MemoryCleanerOnCrash S_INSTANCE = new MemoryCleanerOnCrash();
    private final List<Thread.UncaughtExceptionHandler> mHandlers = new ArrayList<>();

    //initialize the handler and set it as the default exception handler
    public static void init() {
        S_INSTANCE.mHandlers.add(Thread.getDefaultUncaughtExceptionHandler());
        Thread.setDefaultUncaughtExceptionHandler(S_INSTANCE);
    }

     //make sure that you can still add exception handlers on top of it (required for ACRA for instance)
    public void subscribeCrashHandler(Thread.UncaughtExceptionHandler handler) {
        mHandlers.add(handler);
    }

    @Override
    public void uncaughtException(Thread thread, Throwable ex) {

            //handle the cleanup here
            //....
            //and then show a message to the user if possible given the context

        for (Thread.UncaughtExceptionHandler handler : mHandlers) {
            handler.uncaughtException(thread, ex);
        }
    }
}

Now the handler’s initializer must be called in your custom Application class (e.g., the class that extends Application):

@Override
protected void attachBaseContext(Context base) {
    super.attachBaseContext(base);
    MemoryCleanerOnCrash.init();
}
  • Make Sure That Free Security Features Are Activated To activate shrinking for the release build, add the following to build.gradle:
android {
    buildTypes {
        release {
            // Enables code shrinking, obfuscation, and optimization for only
            // your project's release build type.
            minifyEnabled true

            // Includes the default ProGuard rules files that are packaged with
            // the Android Gradle plugin. To learn more, go to the section about
            // R8 configuration files.
            proguardFiles getDefaultProguardFile(
                    'proguard-android-optimize.txt'),
                    'proguard-rules.pro'
        }
    }
    
}

The file proguard-rules.pro is where you define custom ProGuard rules. With the flag -keep you can keep certain code that is not being removed by R8, which might otherwise produce errors. For example to keep common Android classes, as in our sample configuration proguard-rules.pro file:

-keep public class * extends android.app.Activity
-keep public class * extends android.app.Application
-keep public class * extends android.app.Service

You can define this more granularly on specific classes or libraries in your project with the following syntax:

-keep public class MyClass

M8: Code Tampering

  • It is recommended to implement an integrity check. This can be done using SafetyNet Verify Apps API provided by android.
  • Compliant Code to enable App verification
SafetyNet.getClient(this)
    .isVerifyAppsEnabled()
    .addOnCompleteListener(new OnCompleteListener<VerifyAppsUserResponse>() {
        @Override
        public void onComplete(Task<VerifyAppsUserResponse> task) {
            if (task.isSuccessful()) {
                VerifyAppsUserResponse result = task.getResult();
                if (result.isVerifyAppsEnabled()) {
                    Log.d("MY_APP_TAG", "The Verify Apps feature is enabled.");
                } else {
                    Log.d("MY_APP_TAG", "The Verify Apps feature is disabled.");
                }
            } else {
                Log.e("MY_APP_TAG", "A general error occurred.");
            }
        }
    });
  • Enable root detection in the application. Use Rootbear library for root detection and Prevent the application to run on a Rooted environment. Usages:-
RootBeer rootBeer = new RootBeer(context);
if (rootBeer.isRooted()) {
    //we found indication of root
} else {
    //we didn't find indication of root
}

  • Set android:exported=false in the manifest, for the components being used in the application.
  • Set android:backup=false in the manifest, for the components being used in the application

M9: Reverse Engineering

  • It is recommended to obfuscate the ANdroid Application Code using an obfuscation tool such as Proguard and R8.
  • Identify what methods / code segments to obfuscate.
  • Tune the degree of obfuscation to balance performance impact.
  • Withstand de-obfuscation from tools like IDA Pro and Hopper.
  • Obfuscate string tables as well as methods

M10: Extraneous Functionality

  • Ensure that there is no test code in final build
  • Ensure there is no hidden switch in the configuration settings
  • Logs must not contain any backend server process description
  • Ensure that the full system logs are not exposed to apps by the OEMs
  • The API endpoints must be well documented

References