OWASP category: MASVS-PLATFORM: Platform Interaction
Overview
An Android permission is a string identifier declared in the app's manifest to request access to restricted data or actions, enforced at runtime by the Android framework.
Android permission levels indicate the potential risk associated with the permission:
- Normal: Low-risk permissions, automatically granted at install time
- Dangerous: High-risk permissions that could allow access to sensitive user data, requiring explicit user approval at runtime
- Signature: Granted only to apps signed with the same certificate as the app declaring the permission, typically used for system apps or interactions between apps from the same developer
Vulnerabilities related to permission-based access controls occur when an app's component (such as activity, receiver, content provider, or service) meets all of the following criteria:
- The component is not associated with any
android:permission
in theManifest
; - The component performs a sensitive task for which a permission exists that the user has already approved;
- The component is exported;
- The component does not perform any manual (manifest or code-level) permission checks;
When this happens, a malicious app can perform sensitive actions by abusing the privileges of the vulnerable component, proxying the vulnerable app's privileges to the malicious app.
Impact
Exporting vulnerable components can be used to gain access to sensitive resources or to perform sensitive actions. The impact of this unwanted behavior depends on the context of the vulnerable component and its privileges.
Mitigations
Require permissions for sensitive tasks
When exporting a component with sensitive permissions, require those same permissions for any incoming request. The Android Studio IDE has lint checks for receivers and services to spot this vulnerability and recommend requiring the appropriate permissions.
Developers can require permissions for incoming requests either by declaring
them in the Manifest
file or at code-level when implementing the service, as
in the following examples.
Xml
<manifest ...>
<uses-permission android:name="android.permission.READ_CONTACTS" />
<application ...>
<service android:name=".MyExportService"
android:exported="true"
android:permission="android.permission.READ_CONTACTS" />
</application>
</manifest>
Kotlin
class MyExportService : Service() {
private val binder = MyExportBinder()
override fun onBind(intent: Intent): IBinder? {
// Enforce calling app has the required permission
enforceCallingPermission(Manifest.permission.READ_CONTACTS, "Calling app doesn't have READ_CONTACTS permission.")
// Permission is enforced, proceed with export logic
return binder
}
// Inner class for your Binder implementation
private inner class MyExportBinder : Binder() {
// Permission is enforced, proceed with export logic
}
}
Java
public class MyExportService extends Service {
@Override
public IBinder onBind(Intent intent) {
// Enforce calling app has the required permission
enforceCallingPermission(Manifest.permission.READ_CONTACTS, "Calling app doesn't have READ_CONTACTS permission.");
return binder;
}
// Inner class for your Binder implementation
private class MyExportBinder extends Binder {
// Permission is enforced, proceed with export logic
}
}
Don't export the component
Avoid exporting components with access to sensitive resources unless absolutely
necessary. You can achieve this by setting the android:exported
in the
Manifest
file to false
for your component. From API level 31 and
beyond, this attribute is set to false
by default.
Xml
<activity
android:name=".MyActivity"
android:exported="false"/>
Apply signature-based permissions
When sharing data between two apps that you control or own, use signature-based permissions. These permissions don't require user confirmation and, instead, check that the apps accessing the data is signed using the same signing key. This setup offers a more streamlined, secure user experience. If you declare custom permissions do consider the corresponding security guidelines.
Xml
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.myapp">
<permission android:name="my_custom_permission_name"
android:protectionLevel="signature" />
Single-task endpoints
Implement your app by following the Separation of Concerns design principle. Each endpoint should only perform a small set of specific tasks with specific privileges. This good design practice also allows the developer to apply granular permissions for each endpoint. For example, avoid creating a single endpoint that serves both calendar and contacts.
Resources
- Android Access to app protected components from the Oversecured blog
- Content Provider Best Practices
- Runtime (Dangerous) Permissions
- Separation of Concerns design principle
- Android permissions documentation
- Android broadcast receivers security tips
- Android services security tips
- Android 12 (API 31) exported default set to "false"
- Lint Check: Exported PreferenceActivity shouldn't be exported
- Lint Check: Exported Receiver doesn't require permission
- Lint Check: Exported Service doesn't require permission