DEV Community

OnaEiuspkz
OnaEiuspkz

Posted on

Android Exported Components Need More Than an Intent Filter

Android Exported Components Need More Than an Intent Filter

Android components are the boundary between an app and the rest of the device. An exported activity, service, receiver, or content provider can accept requests from another application. If the component performs a sensitive task and the caller has no effective authorization check, the component can become a privilege proxy for the calling app.

What makes the boundary unsafe

Android's guidance on permission-based access control lists four conditions for this class of vulnerability. The component has no android:permission, it performs a sensitive task, it is exported, and it has no manual permission check. A malicious app can then trigger the action through the exported component and borrow the vulnerable app's access to protected data or APIs.

An intent filter is routing information. It is not an authentication decision. If a component is internal, set android:exported="false" and verify the manifest in the built APK. If another app must call it, choose a permission that matches the trust relationship. A signature-level custom permission limits access to apps signed with the same certificate. A dangerous permission asks the user for a broad capability and is difficult to justify for a narrow inter-app contract.

<service
    android:name=".InternalSyncService"
    android:exported="false" />

<receiver
    android:name=".PartnerReceiver"
    android:exported="true"
    android:permission="com.example.app.permission.PARTNER_EVENT" />
Enter fullscreen mode Exit fullscreen mode

The receiver still needs input validation. Permission names, action strings, extras, and URI data should be treated as untrusted. A caller can be authorized for one event and still send malformed or excessive data, trigger expensive work, or reach a code path with a weaker assumption.

Review the merged manifest

Library manifests can contribute components and attributes. Inspect the merged manifest generated for each build variant rather than only the app source. Check debug, release, and product-flavor outputs separately. A component that is internal in release may be exported for testing, or a plugin may add an intent filter that changes the routing surface.

Use Android lint findings as a starting point, then trace the component's actual execution path. Identify the caller-controlled input, the permission check, the service identity, and the protected resource. Look for a second entrance, such as a content provider that exposes files used by the service or a broadcast receiver that changes the service's state.

Test the negative cases

Build a separate test APK and attempt calls without the required permission, with a normal app permission, and with a different action. Confirm that the component rejects each case before doing sensitive work. Test malformed extras and URI permissions. For providers, verify path grants and temporary URI access do not widen beyond the intended document or directory.

Review logs for attempted denials without recording sensitive payloads. Add an inventory of exported components to release review, with an owner, intended caller, permission, and test for every entry.

The goal is not to eliminate every exported component. Android needs explicit interfaces between apps. The goal is to make each one a deliberate, testable authorization boundary.

References

Top comments (0)