Getting Started
Securing iOS and Android apps is a challenge. In this guide, I will be addressing some platform specific security measures your organization should consider, to make your apps more secure. Many tips in the list below are general practices that you may recognize and be able to easily address while browsing through your apps’ source code. Future guides will detail secure development processes, methodologies, and additional resources.
Some Basic Reading
Apple and Google provide guides with recommendations for secure mobile development practices. These guides are valuable resources for gaining an understanding of iOS and Android security concerns.
1. Check out Apple’s Secure Coding Guide for iOS.
2. Take a look at Android’s Best Practices for Security & Privacy.
Android:
1. Validate all entered input:
a. Implement your own validation
1 2 3 4 |
EditText firstName = (EditText)findViewById(R.id.first_name); if( firstName.getText().toString().length() == 0 ) //Validate EditText input firstName.setError( "First name is required!" ); |
b. Or use a validation library such as Android Saripaar.
Android Saripaar allows you to define validation rules using annotations. Saripaar provides rules for most basic validations (passwords, emails, credit cards, checkboxes, etc) as well as custom validation.
1 2 3 4 5 6 7 8 9 10 11 |
//Implement validation rules using Android Saripaar @NotEmpty @Email private EditText emailEditText; @Password(min = 6, scheme = Password.Scheme.ALPHA_NUMERIC_MIXED_CASE_SYMBOLS) private EditText passwordEditText; @Checked(message = "You must agree to the terms.") private CheckBox iAgreeCheckBox; |
2. Create files and access SharedPreferences using Context.MODE_PRIVATE
or Context.MODE_APPEND
so that these files are only readable and writable for the application that created the file.
1 2 3 4 5 6 |
//File Operations outputStream = openFileOutput(filename, Context.MODE_PRIVATE); //SharedPreferences context.getSharedPreferences(KEY, Context.MODE_PRIVATE); |
Using MODE_WORLD_READABLE and MODE_WORLD_WRITABLE is dangerous and highly discouraged. It is recommended that you use ContentProvider, BroadcastReceiver, and/or Service functionality to share app information.
3. In your application’s manifest, mark ContentProviders, BroadcastRecievers, and Services as android:exported=false
when possible. This prevents other applications from accessing them.
By default: Android exports any Services containing intent filters. Explicitly declaring android:exported removes any ambiguity.
4. Obfuscate your app using Proguard.
Proguard is now integrated into Android Studio and can help reduce the size of your app and provide obfuscation in order to make reverse engineering more difficult. You can find Google’s guide to implementing Proguard here.
Notes:
1. Any integrated crash reporting tools like ACRA, Google Analytics, etc will report wrong line numbers and method names as your codebase.
2. You need to exclude all packages and classes which use annotations.
3. Proguard provides the ability to decipher obfuscated stacktraces.
5. Remove confidential console logs and comments before publishing your app.
a. Proguard can be configured to remove logs. Simply add the following into the Proguard configuration for your app:
1 2 3 4 5 6 7 8 9 |
-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(...); } |
Note: This configuration may make line numbers different for production stacktraces, as lines are removed.
b. Or as described in this Stackoverflow post, logging libraries such as Timber allow you to create logging statements that are only written to Logcat in debug builds of your app:
1 2 3 4 5 |
//Add Timber configuration in your Application onCreate() if (BuildConfig.DEBUG) { Timber.plant(new Timber.DebugTree()); } |
1 2 3 4 5 6 7 8 |
//Create log statements elsewhere in your app Timber.d("Downloading URL: %s", url); try { // ... } catch (IOException ioe) { Timber.e(ioe, "Bad things happened!"); } |
iOS:
1. Validate all entered input:
a. Implement your own validation.
1 2 3 4 5 6 7 8 9 |
//Check if textfield is empty -(IBAction) submitButton { if(self.textName == nil || [self.textName.text isEqualToString:@""]) { //show error message } } |
b. Or use a validation library such as Validator.
Validator is available as a Swift or Objective-C library through the ObjC branch. Validator has a wide range of validation rules and patterns (email, password constraints, URL, credit card, length, etc). The library also allows you to write your own validation rules.
1 2 3 4 5 6 7 8 9 10 11 |
//Example email validation using Validator let rule = ValidationRulePattern(pattern: .EmailAddress, failureError: someValidationErrorType) let result = "invalid@email,com".validate(rule: rule) // Note: the above is equivalent to Validator.validate(input: "invalid@email,com", rule: rule) switch result { case .Valid: print("") case .Invalid(let failures): print(failures.first?.message) } |
2. NSUserDefaults
should not be used to store sensitive data. Also note, NSManagedObects
are stored in an unencrypted DB file.
3. Data in plaintext should not be stored in the application’s sandbox.
“App Sandbox is designed to let you describe your app’s intended interactions with the system. The system then grants your app only the access your app needs to get its job done. If malicious code gains control of a properly sandboxed app, it is left with access to only the files and resources in the app’s sandbox.” -Apple’s App Sandbox Design Guide
The app sandbox has some basic protections but may be compromised. Consider using encrypted storage methods for sensitive data. Apple provides detailed documentation on using the app sandbox.
4. TextFields that have inputs as passwords should be used with Secure
option, and should have Correction
disabled. iOS typically caches all text entry in textfields, provided it doesn’t have the Secure
tag.
a. These properties can be set in Interface Builder.
b. Or set programmatically.
1 2 3 4 5 6 7 8 |
//Using Objective-C: yourTextField.secureTextEntry = YES; yourTextField.autocorrectionType = UITextAutocorrectionType.No; //Using Swift: yourTextField.secureTextEntry = true yourTextField.autocorrectionType = .No |
5. Remove confidential console logs and comments before publishing your app.
If you use NSLog, you can remove all instances of it in release builds by adding to following code to your -prefix.pch file
1 2 3 4 |
#ifndef DEBUG #define NSLog(...) /* suppress NSLog when in release mode */ #endif |