Android Secure Coding Standards

Android Secure Coding Standards

Android devices have become ubiquitous and also part of our day-to-day life. It is loaded with various apps that have access to personal and sensitive information and hence it is more prone to security attacks. As an Android developer, it is absolutely necessary to write secure apps. This gives an assurance to the customer that your application is not going to compromise their sensitive information. It also helps to retain your brand value and customers. So while writing Android code, it is important to keep security in mind and avoid any kind of Android vulnerabilities.  It is highly recommended to follow the  Android Secure Coding standards to mitigate Android vulnerabilities in a development phase. You can also use third party services to scan your source code to make sure you have not missed anything. We use HP Fortify to make sure our code is secure. It was an interesting learning curve. This blog is a collection of the most general security issues which developers often miss. This blog also assumes that you have a basic understanding of the Android framework.

Explicitly not export Activity

An Activity is an application component that provides a screen with which users can interact in order to do something. This activity can also be invoked by an implicit intent if this activity has intent filter. Declaring an intent filter for an activity in the AndroidManifest.xml file means that the activity may be exported to other apps. If the activity is intended solely for the internal use of the app and an intent filter is declared then any other app, including malware, can activate the activity for unintended use. It is strongly recommended to explicitly make the activity non-exportable by setting the exported flag to false. Remember in the software world, we should make things explicit instead of implicit.

<activity  android:name=".YourInternalActivity"
android:configChanges="keyboard|keyboardHidden|orientation" >           
    <intent-filter >
        <action android:name="com.some.vulnerable.ACTION_UPLOAD" />
        <category android:name="android.intent.category.DEFAULT" />
        <data android:mimeType="image/*" />          
    </intent-filter>        
</activity>
<activity  android:name=".YourInternalActivity" 
          android:configChanges="keyboard|keyboardHidden|orientation"
          android:exported="false" >           
    <intent-filter >
        <action android:name="com.some.vulnerable.ACTION_UPLOAD" />
        <category android:name="android.intent.category.DEFAULT" />
        <data android:mimeType="image/*" /> 
    </intent-filter>        
</activity>

Broadcast Receiver

BroadcastReceivers are used to receive an intent and take action on it. This intent is sent by a sender to request some action by the broadcast receiver. If the broadcast receiver is taking some requested action without checking the broadcaster permission, that can cause a security vulnerability.  For example, an application may listen for an SMS broadcast intent to authorize a user based on the SMS. A malicious application can impersonate itself as the system and send the broadcast intent. Since the broadcast receiver is not checking for the proper broadcast permission, it will trust the intent and authorize the user.

If you are exposing broadcast receiver externally, it is really necessary to check for broadcaster permission. In the case you are using a broadcast receiver for intra-app communication, use LocalBroadcastManager. It is also necessary to check the content of the intent before performing the action as the sender can send bad data which can cause your application to crash. 
If your sole purpose is to use a broadcast receiver for intra-app communication then we would recommend using the EventBus instead. It is lightweight and you don't have to deal with security stuff.  

 

context.registerReceiver(broadcastReceiver, intentFilter);
context.registerReceiver(broadcastReceiver, intentFilter, "permission.ALLOW_BROADCAST", null);
 # You can also use a local broadcast manager if you intend to broadcast the intent within the app.
LocalBroadcastManager.getInstance(this).registerReceiver(boradcastReceiver, intentFilter)

Intent Broadcast

In the Android system, an Intent is also used to broadcast some event to notify other components/apps. When broadcasting an intent for inter-application communication, it is necessary to mention the receiver permission, otherwise any malicious application can perform a man in the middle attack. The system makes sure that intent is delivered to that broadcast receiver that has the permission. This helps avoid a man in the middle attack.

context.sendBroadcast(intent);
context.sendBroadcast(intent, "permission.ALLOW");

Sticky Broadcast

Generally, intent receivers should be registered before the intent is broadcasted by other apps. This way, the system knows about the receivers and delivers the intent to the right receivers. Otherwise, the system drops the broadcasted intent like the case where the app is not ready but is interested in a broadcast intent which may get broadcasted even before the app is ready. To address this issue, Android has introduced Sticky Broadcast. These intents stick around after the broadcast is complete. Once the system notices that the app has registered its broadcast receiver, it delivers the intent to the app. The system uses this to broadcast system related events example Intent.ACTION_BATTERY_CHANGED. 

Sticky broadcasts cannot be secured with a permission and therefore are accessible to any receiver. if these broadcasts contain sensitive data or reach a malicious receiver, the application may be compromised. So don't use sendStickyBroadcast() method in your application. Just use LocalBroadcastManager if you want to do intra-application communication.

context.sendStickyBroadcast(intent);
context.sendBroadcast(intent, "permission.ALLOW");

Intent

Intents are mainly used for inter-process communication. Intent can be of two types, implicit and explicit. An implicit intent specifies an action that can invoke any app which can perform this action. It is quite possible that there could be many apps/components that can perform the same action. In that case, the system provides options to the user and the user can then select which application they want to perform the task.

An explicit intent can be used to launch any component of the app. In this case, you explicitly specify the component name while creating the intent. If your intention is to invoke your application component then it is highly recommended to use explicit intents since implicit intents can be intercepted by any other application if the correct receiver permission is not used. The best option is to use explicit intents with LocalBroadcastManager. This is efficient because the intent will never leave the application boundary and hence the system doesn't have to handle the overhead related with systemwide broadcasts.

Intent intent = new Intent("com.sample.action.authorize");
intent.putExtra("userid", userId);
intent.putExtra("password", password);
 sendBroadcast(intent);
Intent intent = new Intent(context, ConnectionManager.class);
intent.putExtra("userid", userId);
intent.putExtra("password", password);
LocalBroadcastManager.getInstance(this).sendBroadcast(intent);

Pending Intent

 

A PendingIntent is an intent that can be given to another application for it to use later. The application receiving the pending intent can perform the operation(s) specified in the pending intent with the same permissions and the same identity as the application that produced the pending intent. Consequently, the pending intent should be built with care and must always contain base intents that have the component name set explicitly to a component owned by the originating application. This ensures that the base intents are ultimately sent to appropriate locations and nowhere else. An implicit intent must never be included in a pending intent.

Intent intent = new Intent("implicit.ACTION");
PendingIntent pendingIntent = PendingIntent.getActivity(this, 1, intent, PendingIntent.FLAG_CANCEL_CURRENT);
try {
    pendingIntent.send();
} catch (PendingIntent.CanceledException e) {
    e.printStackTrace();
}
Intent intent = new Intent(this, LoginActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 1, intent, PendingIntent.FLAG_CANCEL_CURRENT);
try {
    pendingIntent.send();
} catch (PendingIntent.CanceledException e) {
    e.printStackTrace();
}

Confuse Deputy Attack

Confuse Deputy Attack is when another application misuses your permission. For instance, your application has delete SMS permission and it has exposed an interface through which SMS can be deleted. If your application is not checking the requester permission, it can delete the SMS even when other applications don't have this permission.

The checkCallingOrSelfPermission method can cause this issue so it is really important to check the caller permission before performing the privileged operation. 

checkCallingOrSelfPermission(this, "permission.ALLOW");
checkCallingPermission(this, "permission.ALLOW");

SQL Injection

Android applications can use SQLite or other third party embedded databases to persist information. ORM is used to interact with the database. Android ORM inherits the SQL Injection Attack that regular ORM has. Basically, your application can be prone to this vulnerability if you use raw query to access the database. It is highly recommended to use prepared SQL statements to access the database. This is the safest way to access it because prepared statements parse the passed argument for sub-query and make sure only that value is passed. This avoids any kind of SQL injection attack. 

db.rawQuery("Select * from user_table where user_name=" + user, null );
SQLiteDatabase db = dbHelper.getWritableDatabase(); 
SQLiteStatement stmt = db.compileStatement("SELECT * FROM Country WHERE code = ?"); 
stmt.bindString(1, "US"); 
stmt.execute();

WebView

The WebView class displays web pages as part of an activity layout. The behavior of a WebView object can be customized using the WebSettings object, which can be obtained from WebView.getSettings(). 

Major security concerns for WebView are from the setJavaScriptEnabled(), setPluginState(), and setAllowFileAccess() methods.

addJavascriptInterface() method

For API level JELLY_BEAN or below, allowing an app to use the addJavascriptInterface method with untrusted content in a WebView leaves the app vulnerable to scripting attacks using reflection to access public methods from JavaScript.  Untrusted content examples include content from any HTTP URL (as opposed to HTTPS) and user-provided content. The method addJavascriptInterface(Object, String) is called from the WebView class. Sensitive data and app control should not be exposed to scripting attacks.

WebView webView = new WebView(this);
setContentView(webView);
...
class JsObject {
     private String sensitiveInformation;
 
     ...
     public String toString() { return sensitiveInformation; }
 
}
 webView.addJavascriptInterface(new JsObject(), "injectedObject");
 webView.loadData("", "text/html", null);
 webView.loadUrl("http://www.example.com");
<manifest>
<uses-sdk android:minSdkVersion="17" />
...
 
</manifest>
# OR
WebView webView = new WebView(this);
setContentView(webView);

setJavaScriptEnable() method

 

WebView also provides a method to enable or disable javascript for the web pages. Since WebView displays external webpages, the application has less control over the content. Inherently javascript comes with cross site attack (XSS) vulnerabilities if not enough care has been taken while creating the web page.  Your code should not invoke setJavaScriptEnable if you are not sure that the web page really requires JavaScript support.

public class UnSecureBrowser extends Activity {
 @override
 public void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 setContentView(R.layout.main);
 
 WebView webView = (WebView) findViewById(R.id.webview);
 
 
 // turn on javascript
 WebSettings settings = webView.getSettings();
 settings.setJavaScriptEnabled(true);
 // Get the url from Intent and launch in the webview.
 webView.loadUrl(getIntent().getStringExtra("URL"););
 }
}
// If the above Activity is externally exposed, a malicious application can send an
// intent with a locally stored file which will be loaded in the webview.
  
String pkg = "some.unsecure.app";
String cls = pkg + ".UnSecureBrowser";
String filePath = "file:///<LOCALLY_STORED_FILE>";
Intent intent = new Intent();
intent.setClassName(pkg, cls);
intent.putExtra("URL", filePath);
this.startActivity(intent);
setJavaScriptEnabled(false);

If your application receives the url form outside a trust-boundary, it should be validated before rendering it with WebView. For example, the following code checks a received URI and uses it only when it is not a file: scheme URI. 

String intentUrl = getIntent().getStringExtra("url");
String localUrl = "about:blank";
if (!intentUrl.startsWith("file:")) {
 loadUrl = intentUrl;
}

Mishandled credentials

Most of our applications use the credentials of a user to authenticate itself so it is really necessary to ensure that we are correctly handling the sensitive data. The credentials should not be stored in plain text or written in any log files. Some of the older Android versions allow access of log files of other applications if they have the READ_LOGS permission. If a malicious application is installed on the device, it can gain log file access and parse the credential information. This way, hackers can get absolute control of a user's account. 

Log.w(id+":"+pass+":"+type+":"+tstamp);

Unencrypted Connections

Most of the Android applications are not stand-alone applications. They generally talk to a server over the network using some network protocol. The most general protocol is HTTP. It comes with two versions, HTTP and HTTPS. The HTTP communication happens over an un-secure network connection and all the data is transferred in plain text. Any hacker with a Network packet analyzer can intercept the data and steal confidential information.

HTTPS communication happens over a secure network connection and it also encrypts the network packet which is hard to intercept and decrypt. So always use an HTTPS url in a production environment.

http://www.thisIsNotSecure.com
https://www.thisIsSecure.com

Miscellaneous

Private Components

In Android apps, if the export value of a component (e.g BroadcastReceiver, Service etc.) is explicitly marked false in the app's manifest file, the component is made private. Any application can access components that are not explicitly assigned an access permission.

References

  1. Android Security tips
  2. Activity Hijacking
  3. Android Secure Coding standards
  4. Android Permissions Remystified
  5. WebView Vulnerabilities
Location Labs by Avast Hackday 2017

Location Labs by Avast Hackday 2017

Our Experience Architecting iOS Apps with Viper

Our Experience Architecting iOS Apps with Viper