Android Application Security Essentials — Save 50%
Write secure Android applications using the most up-to-date techniques and concepts with this book and ebook
This article created by Pragati Ogal Rai, the author of Android Application Security Essentials, will use the application components, Intents, and permissions and put them all together to define our application's policy file. This policy file is called AndroidManifest.xml and is by far the most important file of an application. As you will see, this file is the place to define access control policy for your application and components. This is also the place to define application and component level specifics that the Android system will use to interact with your application.
The article begins with a discussion of AndroidManfiest.xml. We will discuss the two important tags: <manifest> and <application> that we have not discussed so far. Next, we will discuss the actions that we can perform in the manifest file such as declaring permission, sharing a process with other applications, external storage, and managing component visibility. The article closes with a discussion of a checklist for your policy file before you release your application. You should adapt the checklist according to your use case. Once you have a comprehensive checklist, you can refer to it every time you are ready to make a new release.
(For more resources related to this topic, see here.)
The AndroidManifest.xml file
All Android applications need to have a manifest file. This file has to be named as AndroidManifest.xml and has to be placed in the application's root directory. This manifest file is the application's policy file. It declares the application components, their visibility, access rules, libraries, features, and the minimum Android version that the application runs against.
The Android system uses the manifest file for component resolution. Thus, the AndroidManfiest.xml file is by far the most important file in the entire application, and special care is required when defining it to tighten up the application's security.
The manifest file is not extensible, so applications cannot add their own attributes or tags. The complete list of tags with how these tags can be nested is as follows:
<uses-sdk><?xml version="1.0" encoding="utf-8"?> <manifest> <uses-permission /> <permission /> <permission-tree /> <permission-group /> <instrumentation /> <uses-sdk /> <uses-configuration /> <uses-feature /> <supports-screens /> <compatible-screens /> <supports-gl-texture /> <application> <activity> <intent-filter> <action /> <category /> <data /> </intent-filter> <meta-data /> </activity> <activity-alias> <intent-filter> </intent-filter> <meta-data /> </activity-alias> <service> <intent-filter> </intent-filter> <meta-data/> </service> <receiver> <intent-filter> </intent-filter> <meta-data /> </receiver> <provider> <grant-uri-permission /> <meta-data /> <path-permission /> </provider> <uses-library /> </application> </manifest>
Only two tags, <manifest> and <application>, are the required tags. There is no specific order to declare components.
The <manifest> tag declares the application specific attributes. It is declared as follows:
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="string" android:sharedUserId="string" android:sharedUserLabel="string resource" android:versionCode="integer" android:versionName="string" android:installLocation=["auto" | "internalOnly" | "preferExternal"] > </manifest>
An example of the <manifest> tag is shown in the following code snippet. In this example, the package is named com.android.example, the internal version is 10, and the user sees this version as 2.7.0. The install location is decided by the Android system based on where it has room to store the application.
<manifest package="com.android.example" android:versionCode="10" android:versionName="2.7.0" android:installLocation="auto" xmlns:android="http://schemas.android.com/apk/res/android">
The attributes of the <manifest> tag are as follows:
- package: This is the name of the package. This is the Java style namespace of your application, for example, com.android.example. This is the unique ID of your application. If you change the name of a published application, it is considered a new application and auto updates will not work.
- android:sharedUserId: This attribute is used if two or more applications share the same Linux ID. This attribute is discussed in detail in a later section.
- android:sharedUserLabel: This is the user readable name of the shared user ID and only makes sense if android:sharedUserId is set. It has to be a string resource.
- android:versionCode: This is the version code used internally by the application to track revisions. This code is referred to when updating an application with the more recent version.
- android:versionName: This is the version of the application shown to the user. It can be set as a raw string or as a reference, and is only used for display to users.
- android:installLocation: This attribute defines the location where an APK will be installed.
The application tag is defined as follows:
<application android:allowTaskReparenting=["true" | "false"] android:backupAgent="string" android:debuggable=["true" | "false"] android:description="string resource" android:enabled=["true" | "false"] android:hasCode=["true" | "false"] android:hardwareAccelerated=["true" | "false"] android:icon="drawable resource" android:killAfterRestore=["true" | "false"] android:largeHeap=["true" | "false"] android:label="string resource" android:logo="drawable resource" android:manageSpaceActivity="string" android:name="string" android:permission="string" android:persistent=["true" | "false"] android:process="string" android:restoreAnyVersion=["true" | "false"] android:supportsRtl=["true" | "false"] android:taskAffinity="string" android:theme="resource or theme" android:uiOptions=["none" | "splitActionBarWhenNarrow"] > </application>
An example of the <application> tag is shown in the following code snippet. In this example, the application name, description, icon, and label are set. The application is not debuggable and the Android system can instantiate the components.
<application android:label="@string/app_name" android:description="@string/app_desc" android:icon="@drawable/example_icon" android:enabled="true" android:debuggable="false"> </application>
Many attributes of the <application> tag serve as the default values for the components declared within the application. These tags include permission, process, icon, and label. Other attributes such as debuggable and enabled are set for the entire application. The attributes of the <application> tag are discussed as follows:
- android:allowTaskReparenting: This value can be overridden by the <activity> element. It allows an Activity to re-parent with the Activity it has affinity with, when it is brought to the foreground.
- android:backupAgent: This attribute contains the name of the backup agent for the application.
- android:debuggable: This attribute when set to true allows an application to be debugged. This value should always be set to false before releasing the app in the market.
- android:description: This is the user readable description of an application set as a reference to a string resource.
- android:enabled: This attribute if set to true, the Android system can instantiate application components. This attribute can be overridden by components.
- android:hasCode: This attribute if set to true, the application will try to load some code when launching the components.
- android:hardwareAccelerated: This attribute when set to true allows an application to support hardware accelerated rendering. It was introduced in the API level 11.
- android:icon: This is the application icon as a reference to a drawable resource.
- android:killAfterRestore: This attribute if set to true, the application will be terminated once its settings are restored during a full-system restore.
- android:largeHeap: This attribute lets the Android system create a large Dalvik heap for this application and increases the memory footprint of the application, so this should be used sparingly.
- android:label: This is the user readable label for the application.
- android:logo: This is the logo for the application.
- android:manageSpaceActivity: This value is the name of the Activity that manages the memory for the application.
- android:name: This attribute contains the fully qualified name of the subclass that will be instantiated before any other component is started.
- android:permission: This attribute can be overridden by a component and sets the permission that a client should have to interact with the application.
- android:persistent: Usually used by a system application, this attribute allows the application to be running all the time.
- android:process: This is the name of the process in which a component will run. This can be overridden by any component's android:process attribute.
- android:restoreAnyVersion: This attribute lets the backup agent attempt a restore even if the backup currently stored is by a newer application than what is attempting to restore now.
- android:supportsRtl: This attribute when set to true supports right-to-left layouts. It was added in the API level 17.
- android:taskAffinity: This attribute lets all activities have affinity with the package name, unless it is set by the Activity explicitly.
- android:theme: This is a reference to the style resource for the application.
- android:uiOptions: This attribute if set to none, there are no extra UI options; if set to splitActionBarWhenNarrow, a bar is set at the bottom if constrained for the screen.
In the following sections we will discuss how to handle specific requirements using the policy file.
Application policy use cases
This section discusses how to define the application policies using the manifest file. I have used use cases and we will discuss how to implement these use cases in the policy file.
Declaring application permissions
An application on the Android platform has to declare what resources it intends to use for proper functioning of the application. These are the permissions that are displayed to the user when they download the application. Application permissions should be descriptive so that users can understand them. Also, as is the general rule with security, it is important to request the minimum permissions required.
Application permissions are declared in the manifest file by using the tag <uses-permission>. An example of a location-based manifest file that uses the GPS for retrieving location is shown in the following code snippet:
<uses-permissionandroid:name="android. permission.ACCESS_COARSE_LOCATION" /> <uses-permissionandroid:name="android. permission.ACCESS_FINE_LOCATION" /> <uses-permissionandroid:name="android. permission.ACCESS_LOCATION_EXTRA_COMMANDS" /> <uses-permissionandroid:name="android. permission.ACCESS_MOCK_LOCATION" /> <uses-permissionandroid:name="android.permission.INTERNET" />
These permissions will be displayed to the users when they install the app, and can always be checked by going to Application under the settings menu. These permissions are seen in the following screenshot:
Declaring permissions for external applications
The manifest file also declares the permissions an external application (which does not run with the same Linux ID) needs to access the application components. This can be one of two places in the policy file: in the <application> tag or along with the component in the <activity>, <provider>, <receiver>, and <service> tag.
If there are permissions that all components of an application require, then it is easy to specify them in the <application> tag. If a component requires some specific permission, then those can be defined in the specific component tag. Remember, only one permission can be declared in any of the tags. If a component is protected by permission then the component permission overrides the permission declared in the <application> tag.
The following is an example of an application that requires external applications to have android.permission.ACCESS_COARSE_LOCATION to access its components and resources:
<application android:allowBackup="true" android:icon="@drawable/ic_launcher" android:label="@string/app_name" android:permission="android. permission.ACCESS_COARSE_LOCATION">
If a Service requires that any application component that accesses it should have access to the external storage, then it can be defined as follows:
<service android:enabled="true" android:name=".MyService" android:permission="android. permission.WRITE_EXTERNAL_STORAGE"> </service>
If a policy file has both the preceding tags then when an external component makes a request to this Service, it should have android.permission.WRITE_EXTERNAL_STORAGE, as this permission will override the permission declared by the application tag.
Applications running with the same Linux ID
Sharing data between applications is always tricky. It is not easy to maintain data confidentiality and integrity. Proper access control mechanisms have to be put in place based on who has access to how much data. In this section, we will discuss how to share application data with the internal applications (signed by the same developer key).
Android is a layered architecture with an application isolation enforced by the operating system itself. Whenever an application is installed on the Android device, the Android system gives it a unique user ID defined by the system. Notice that the two applications, example1 and example2, in the following screenshot are the applications run as separate user IDs, app_49 and app_50:
However, an application can request the system for a user ID of its choice. The other application can then request the same user ID as well. This creates tight coupling and does not require components to be made visible to the other application or to create shared content providers. This kind of tight coupling is done in the manifest tags of all applications that want to run in the same process.
The following is a snippet of manifest files of the two applications com.example.example1 and com.example.example2 that use the same user ID:
<manifest xmlns:android="http://schemas.android. com/apk/res/android" package="com.example.example1" android:versionCode="1" android:versionName="1.0" android:sharedUserId="com.sharedID.example"> <manifest xmlns:android="http://schemas.android. com/apk/res/android" package="com.example.example2" android:versionCode="1" android:versionName="1.0" android:sharedUserId="com.sharedID.example">
The following screenshot is displayed when these two applications are running on the device. Notice that the applications, com.example.example1 and com.example.example2, now have the app ID of app_113.
You will notice that the shared UID follows a certain format akin to a package name. Any other naming convention will result in an error such as an installation error: INSTALL_PARSE_FAILED_BAD_SHARED_USER_ID.
All applications that share the same UID should have the same certificate.
Starting with API Level 8, Android provides support to store Android applications (APK files) on external devices, such as an SD card. This helps to free up internal phone memory. Once the APK is moved to external storage, the only memory taken up by the app is the private data of the application stored on internal memory. It is important to note that even for the SD card resident APKs, the DEX (Dalvik Executable) files, private data directories, and native shared libraries remain on the internal storage.
Adding an optional attribute in the manifest file enables this feature. The application info screen for such an application either has a move to the SD card or move to a phone button depending on the current storage location of APK. The user then has an option to move the APK file accordingly.
If the external device is un-mounted or the USB mode is set to Mass Storage (where the device is used as a disk drive), all the running activities and services hosted on that external device are immediately killed.
The feature to enable storing APK on the external devices is enabled by adding the optional attribute android:installLocation in the application's manifest file in the <manifest> element. The attribute android:installLocation can have the following three values:
- InternalOnly: The Android system will install the application on the internal storage only. In case of insufficient internal memory, storage errors are returned.
- PreferExternal: The Android system will try to install the application on the external storage. In case there is not enough external storage, the application will be installed on the internal storage. The user will have the ability to move the app from external to internal storage and vice versa as desired.
- auto: This option lets the Android system decide the best install location for the application. The default system policy is to install the application on internal storage first. If the system is running low on internal memory, the application is then installed on the external storage. The user will have the ability to move the application from external to internal storage and vice versa as desired.
For example, if android:installLocation is set to Auto, then on devices running a version of Android less than 2.2, the system will ignore this feature and APK will only be installed on the internal memory. The following is the code snippet from an application's manifest file with this option:
<manifest package="com.example.android" android:versionCode="10" android:versionName="2.7.0" android:installLocation="auto" xmlns:android="http://schemas.android. com/apk/res/android">
The following is a screenshot of the application with the manifest file as specified previously. You will notice that Move to SD card is enabled in this case:
In another application, where android:installLocation is not set, the Move to SD Card is disabled as shown in the following screenshot:
Setting component visibility
Any of the application components namely, activities, services, providers, and receivers can be made discoverable to the external applications. This section discusses the nuances of such scenarios.
Any Activity or Service can be made private by setting android:exported=false. This is also the default value for an Activity. See the following two examples of a private Activity:
<activity android:name=".Activity1" android:exported="false" /> <activity android:name=".Activity2" />
However, if you add an Intent Filter to the Activity, then the Activity becomes discoverable for the Intent in the Intent Filter. Thus, the Intent Filter should never be relied upon as a security boundary. See the following examples for Intent Filter declaration:
<activity android:name=".Activity1" android:label="@string/app_name" > <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <activity android:name=".Activity2"> <intent-filter> <action android:name="com.example.android. intent.START_ACTIVITY2" /> </intent-filter> </activity>
Both activities and services can also be secured by an access permission required by the external component. This is done using the android:permission attribute of the component tag.
A Content Provider can be set up for private access by using android:exported=false. This is also the default value for a provider. In this case, only an application with the same ID can access the provider. This access can be limited even further by setting the android:permission attribute of the provider tag.
A Broadcast Receiver can be made private by using android:exported=false. This is the default value of the receiver if it does not contain any Intent Filters. In this case, only the components with the same ID can send a broadcast to the receiver. If the receiver contains Intent Filters then it becomes discoverable and the default value of android:exported is false.
During the development of an application, we usually set the application to be in the debug mode. This lets developers see the verbose logs and can get inside the application to check for errors. This is done in the <application> tag by setting android:debuggable to true. To avoid security leaks, it is very important to set this attribute to false before releasing the application.
An example of sensitive information that I have seen in my experience includes usernames and passwords, memory dumps, internal server errors, and even some funny personal notes state of a server and a developer's opinion about a piece of code.
The default value of android:debuggable is false.
Starting with API level 8, an application can choose a backup agent to back up the device to the cloud or server. This can be set up in the manifest file in the <application> tag by setting android:allowBackup to true and then setting android:backupAgent to a class name. The default value of android:allowBackup is set to true and the application can set it to false if it wants to opt out of the backup. There is no default value for android:backupAgent and a class name should be specified.
The security implications of such a backup are debatable as services used to back up the data are different and sensitive data, such as usernames and passwords can be compromised.
Putting it all together
The following example puts all the learning we have done so far to analyze AndroidManifest.xml provided with an Android SDK sample for RandomMusicPlayer.
The manifest file specifies that this is version 1 of the application com.example.android.musicplayer. It runs on SDK 14 but supports backwards up to SDK 7. The application uses two permissions namely, android.permission.INTERNET and android.permission.WAKE_LOCK. The application has one Activity that is the entry point for the application called MainActivity, one Service called MusicService, and one receiver called MusicIntentReceiver.
MusicService has defined custom actions called PLAY, REWIND, PAUSE, SKIP, STOP, and TOGGLE_PLAYBACK.
The receiver uses the action intent android.media.AUDIO_BECOMING_NOISY and android.media.MEDIA_BUTTON defined by the Android system.
None of the components are protected with permissions. An example of an AndroidManifst.xml file is shown in the following screenshot:
In this section, I have tried to put together an example list that I suggest you refer to whenever you are ready to release a version of your application. This is a very general version and you should adapt it according to your own use case and components. When creating a checklist think about issues that relate to the entire application, those that are specific to a component, and issues that might come up by setting the component and application specification together.
In this section, I have listed some questions that you should be asking yourself as you define the application specific preferences. They may affect how your application is viewed, stored, and perceived by users. Some application level questions that you may like to ask are as follows:
- Do you want to share resources with other applications that you have developed?
- Did you specify the unique user ID?
- Did you define this unique ID for another application either intentionally or unintentionally?
- Does your application require some capabilities such as camera, Bluetooth, and SMS?
- Does your application need all these permissions?
- Is there another permission that is more restrictive than the one you have defined? Remember the principle of least privilege
- Do all the components of your application need this permission or only a few?
- Check the spellings of all the permissions once again. The application may compile and work even if the permission spelling is incorrect.
- If you have defined this permission, is this the correct one that you need?
- At what API level does the application work?
- What is the minimum API level that your application can support?
- Are there any external libraries that your application needs?
- Did you remember to turn off the debug attribute before you release?
- If you are using a backup agent then remember to mention it here
- Did you remember to set a version number? This will help you during application upgrade
- Do you want to set an auto upgrade?
- Did you remember to sign the application with your release key?
- Sometimes setting a particular screen orientation will not allow your application to be visible on certain devices. For example, if your application only supports portrait mode then it might not appear for devices with landscape mode only.
- Where do you want to install the APK?
- Are there any services that might cease to work if the intent is not received in time?
- Do you want some other application level settings, such as the ability of the system to restore components?
- If defining a new permission, think twice if you really want them. Chances are there is already an existing permission that will cover your use case.
Some component level questions that you will want to think about in the policy are listed here. These are questions that you should be asking yourself for each component:
- Did you define all components?
- If using the third party libraries in your application, did you define all the components that you will use?
- Was there a particular setting that the third party library expects from your application?
- Do you want this component to be visible to other applications?
- Do you need to add some Intent Filters?
- If the component is not supposed to be visible, did you add Intent Filters? Remember as soon as you add Intent Filters, your component becomes visible.
- Do other components require some special permission to trigger this component?
- Verify the spelling of the permission name.
- Does your application require some capabilities such as camera, Bluetooth, and SMS?
In this article, we've learned how to define an applications policy file. The manifest file is the most important artifact of an application and should be defined with utmost care. This manifest file declares the permissions requested by an application and permissions that the external applications need to access its components. With the policy file we also define the storage location of the out APK and the minimum SDK against which the out application will run. The policy file exposes components that are not sensitive to the application. At the end of this article we discussed some sample issues that a developer should be aware of when writing a manifest file.
In this article, we've learned about an Android application structure.
Resources for Article:
- So, what is Spring for Android? [Article]
- Animating Properties and Tweening Pages in Android 3-0 [Article]
- New Connectivity APIs – Android Beam [Article]
|Write secure Android applications using the most up-to-date techniques and concepts with this book and ebook|
eBook Price: $26.99
Book Price: $44.99
About the Author :
Pragati Ogal Rai is a technologist with more than 14 years of experience in mobile operating systems, mobile security, mobile payments, and mobile commerce. From working as a platform security engineer with Motorola Mobility, to designing and developing PayPal's mobile offerings, she has an extensive end-to-end experience in all aspects of mobile technology.
Pragati has a dual Master's in Computer Science and has taught and trained computer science students at different levels. She is a recognized speaker at international technology events.