Android Exported Components: Security Review Worksheet
android:exported decides whether another application can reach an activity, service, broadcast receiver, or content provider. It is reachability control, not authorization. Review release merged manifest, installed package behavior, permission declarations, and every sensitive operation reached after IPC input. Hidden component names and undocumented actions are not security boundaries.
Map entry points
Start from merged release manifest rather than source manifest fragments. List component type, name, exported value, intent filters, android:permission, provider authority, URI grants, and reachable action. Components without an external contract should be android:exported="false". For apps targeting Android 12 or API level 31 and later, activities, services, and receivers with intent filters must explicitly declare android:exported; build fails when declaration is missing.
<activity android:name=".DeepLinkActivity" android:exported="true">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
</intent-filter>
</activity>
<provider android:name=".RecordsProvider"
android:authorities="com.example.app.records"
android:exported="false" />
An activity can require export for browser routing. That does not make every extra, URI, ClipData, or destination safe. Parse input as hostile, establish session, then enforce authorization at action and object level.
Protect required exposure
When external caller exists, define narrow contract and platform permission. Signature protection is suitable for access limited to applications signed with same certificate. Component permission reduces unsolicited calls but cannot prove a requested record belongs to caller or current user.
<permission android:name="com.example.app.ACCESS_ADMIN"
android:protectionLevel="signature" />
<service android:name=".AdminService" android:exported="true"
android:permission="com.example.app.ACCESS_ADMIN" />
Avoid relying on one broad application permission where read and write need different authority. Providers need separate readPermission and writePermission when policy differs. URI grants should share only specific content for limited duration, not whole authority by accident.
Validate provider access
Providers are high-value entry points because callers can query, insert, update, or delete through content:// URIs. Verify android:exported, authority uniqueness, read/write permissions, grantUriPermissions, path permissions, and URI matcher coverage. Validate URI path, projection, selection arguments, sort order, and values. Never concatenate caller-controlled selection text into SQL.
adb shell content query \
--uri content://com.example.app.records/private
adb shell am start -n com.example.app/.AdminActivity \
--es operation "reset" --ei user_id 42
Run commands from untrusted shell context or separate test app. Capture package version, device API level, command, response, logcat, and server effect. A provider query denied by permission is pass only when intended access remains functional. Sensitive data returned, write accepted, crash, or authorization bypass is fail.
Review record
| Artifact | Evidence | Owner | Result | Exception | Remediation |
|---|---|---|---|---|---|
| Release manifest | merged APK manifest | Android owner | pass/fail | documented public handler | set explicit value |
| Exported component | adb shell am result | app owner | pass/fail | approved integration | permission and auth |
| Provider | content query/write tests | data owner | pass/fail | scoped sharing | deny by default |
| Permission | signed entitlement review | security owner | pass/fail | partner contract | signature-level scope |
Decision rule: default component to non-exported. Export only documented cross-app entry point, protect it with least privilege, and authorize every sensitive request inside component and backend.
Remediate and retest
Remove unused filters and aliases. Make exported state explicit. Add narrow permissions where platform callers need access, then validate input and server-side ownership regardless of permission result. Test every supported Android version because behavior and install-time enforcement differ by target SDK. Mobile testing can compare APK manifest, IPC surface, provider access, and runtime evidence.
Evidence discipline
Retain review evidence: merged manifest, target SDK, build number, component name, exported value, permission definition, calling command, device API level, observed response, logcat reference, owner, result, exception, and remediation deadline. Repeat tests after manifest merge changes because library manifests can add filters or providers. A documented external integration remains fail until its permission, input checks, object authorization, and negative test all pass.
adb negative matrix and closure
Enumerate exported activities, services, receivers, and providers from merged release manifest. Test each externally reachable entry with caller that has no application permission. For an activity use adb shell am start -W -n com.example.shop/.AdminActivity; expected result is security denial or safe public screen, never administrative data. For service use adb shell am startservice -n com.example.shop/.SyncService; expected result is denial when service is not public. For provider use adb shell content query --uri content://com.example.shop.provider/private; expected result is permission denial or no accessible rows. Preserve command, exit text, build, and device API for each row.
Edge case: exported component may be intended for system or partner integration. Restrict with signature-level permission, narrow intent action, validated caller identity, and explicit data contract; package-name comparison alone is not a reliable authorization boundary. A component set non-exported can still be reachable indirectly through another exported relay, so trace intent forwarding and URI grants.
Close matrix only when every component has manifest evidence, negative result, exception owner, and retest trigger. Re-run after library update, manifest merge change, new provider, or target SDK change.
Sources
- Android 12: exported components
- Android: provider manifest element
- OWASP MASTG: Restrict Android App Components
Worked closure check
For exported Android receiver review, bind closure to the merged-manifest hash, package build, device API, tested action, and Android owner. Record whether an unprivileged caller reached the receiver, plus permission and logcat evidence. This worksheet documents that IPC result only; it cannot certify application security or answer legal questions. A manifest-library change invalidates affected rows until retested. A separate reviewer should replay one denied broadcast, inspect its authorization path, and leave any indirect relay exposure open.