Exported Components Lockdown
| Plan | Platforms | MASVS |
|---|---|---|
| Team | Android | MASVS-PLATFORM-1 |
Overview
Exported Components Lockdown (ExportedComponentsPrevention) is a build-time manifest hardening control that enforces android:exported="false" on all activities, services, broadcast receivers, and content providers in the compiled application unless explicitly marked as intentionally public. This prevents unauthorized inter-process communication (IPC), intent injection attacks, and data exfiltration via content providers by other applications installed on the device.
Unlike runtime security controls, this enforcement happens during the APK build process by rewriting the merged AndroidManifest.xml before final packaging. Once compiled, the locked-down components are permanently inaccessible to other apps.
How It Works
Build-Time Enforcement
During the MobileDefender integration process (AppTego Android protected build pipeline), the manifest manipulation engine:
- Parses the merged
AndroidManifest.xmlafter all library manifests have been merged - Identifies all
<activity>,<service>,<receiver>, and<provider>elements - Applies lockdown rules to each component:
- No intent-filter declared AND exported not set or exported=true → Sets
android:exported="false" - Intent-filter present but component not in allowlist → Logs warning and sets
android:exported="false"(unless LAUNCHER activity or BOOT_COMPLETED receiver) - Component in allowlist → Skips modification (remains exported)
- LAUNCHER activity → Skips modification (must remain exported for device launcher)
- BOOT_COMPLETED receiver → Skips modification (system-required)
- Writes the modified manifest back to disk
- Continues with APK signing and packaging
The transformed APK ships with components locked to your app's process boundary unless you explicitly opted them out via allowlist.
Allowlist Mechanism
Developers may need certain components to remain exported for legitimate functionality:
- Deep link handlers (
VIEWintent withhttp/httpsschemes) - Share targets (
SEND/SEND_MULTIPLEintents) - Widget providers (
APPWIDGET_UPDATEreceivers) - Custom content providers for inter-app data sharing
The allowlist array accepts fully qualified component class names (e.g., "com.example.myapp.ShareActivity") or relative names as they appear in the manifest (e.g., ".ShareActivity"). Any component matching an allowlist entry is left untouched.
Threats Mitigated
Intent Injection Attacks
Exported activities and services can be invoked by any app on the device via startActivity() or startService() with crafted intents. If your component does not validate intent extras, an attacker can:
- Trigger unintended code paths
- Bypass authentication screens
- Supply malicious file URIs or data payloads
- Escalate privileges by invoking internal-only features
Example: An exported internal admin activity could be directly launched by a malicious app, bypassing the normal authentication flow.
Broadcast Receiver Exploitation
Exported receivers can be triggered by any app via sendBroadcast(). Without explicit permission guards, attackers can:
- Trigger side effects (e.g., data deletion, configuration changes)
- Inject spoofed system broadcasts
- Repeatedly invoke expensive operations to degrade performance
Example: An exported receiver handling database cleanup could be spammed by a malicious app to cause battery drain or data loss.
Content Provider Data Exfiltration
Exported content providers are queryable by any app. If not protected by read/write permissions, attackers can:
- Enumerate and exfiltrate all rows in your database
- Inject or modify sensitive records
- Bypass normal data access controls
Example: An exported provider backing a notes app could leak all user notes to any installed app without user consent.
IPC Abuse Surface Reduction
Even if your components validate inputs, every exported component increases your app's attack surface. By default-denying IPC access, this control implements a "secure by default" posture and forces developers to consciously opt into public component exposure.
How to Enable the Control
Navigate to Preventative Controls from the AppTego portal, and expand the App Component Security section. Under this section you will find the Exported Component Lockdown control. Click Enable to enable it for the next build or for it to be applied with a live push (if enabled).
API Configuration Example
Minimal Configuration (Lock Down Everything)
{
"ExportedComponentsPrevention": {
"protection": true,
"allowlist": []
}
}
All components except the launcher activity and BOOT_COMPLETED receivers will be set to android:exported="false".
With Allowlist (Preserve Legitimate Public Components)
{
"ExportedComponentsPrevention": {
"protection": true,
"allowlist": [
"com.example.myapp.DeepLinkActivity",
"com.example.myapp.ShareActivity",
"com.example.myapp.WidgetProvider"
]
}
}
The three allowlisted components remain exported; all others are locked down.
Disabled
{
"ExportedComponentsPrevention": {
"protection": false
}
}
No manifest modifications are made.
| Field | Purpose |
|---|---|
protection | Enables exported component lockdown for protected Android apps. |
allowlist | Optional list of app-owned components that should remain exported after compatibility testing. |
Opting Out Components for Legitimate Public Functionality
If your app legitimately needs to expose components to other apps, add them to the allowlist:
- Deep Links / App Links: If your activity handles
VIEWintents withhttp/httpsschemes (for web-to-app navigation), add it to the allowlist:
"allowlist": ["com.example.myapp.DeepLinkActivity"]
- Share Targets: If your activity handles
SENDorSEND_MULTIPLEintents (appears in Android's share sheet), add it to the allowlist:
"allowlist": ["com.example.myapp.ShareActivity"]
- Custom Content Providers: If you provide a public API for other apps to query your data (e.g., a keyboard app accessing dictionary data), add the provider to the allowlist:
"allowlist": ["com.example.myapp.DictionaryProvider"]
- Home Screen Widgets: Widget providers (
AppWidgetProvidersubclasses) must remain exported to receive system broadcasts:
"allowlist": ["com.example.myapp.MyWidgetProvider"]
Component Name Resolution:
- Use the fully qualified name as it appears in your source code (e.g.,
com.example.myapp.ShareActivity) - Or use the manifest-relative name (e.g.,
.ShareActivityif your manifest haspackage="com.example.myapp") - Both forms are matched; the build pipeline resolves shorthand names automatically
Caveats
Automatic Exceptions
The following components are never locked down, even if not in the allowlist:
- Launcher activities: Any
<activity>with an<intent-filter>containing<category android:name="android.intent.category.LAUNCHER" />. Locking these would make your app invisible in the device launcher. - BOOT_COMPLETED receivers: Any
<receiver>with an<intent-filter>containing<action android:name="android.intent.action.BOOT_COMPLETED" />. The system requires these to be exported to deliver the boot broadcast.
Components with Intent Filters
Any component that declares an <intent-filter> but is not in the allowlist will be locked down with a warning logged during build. Review these warnings carefully:
- If the component should be public, add it to the allowlist
- If the intent-filter is a mistake or legacy code, consider removing it
Third-Party Library Components
Libraries included via Gradle dependencies may declare exported components in their manifests. After manifest merging, these components will also be locked down unless allowlisted. If a library legitimately needs an exported component (e.g., a payment SDK's activity), add it to the allowlist or the library may malfunction.
Testing After Enablement
After enabling this control:
- Test all deep links and app links from other apps (browser, email client, etc.)
- Test share functionality (share from other apps into yours)
- Test home screen widgets
- Test any inter-app integrations
- Review build logs for lockdown warnings and verify each is intentional
Build-Time Only
This control operates only during APK compilation. It cannot be toggled at runtime. If you need to change the allowlist or disable enforcement, you must rebuild the APK with updated configuration.
Support Matrix
| Platform | Minimum Version | Status |
|---|---|---|
| Android | All API levels | ✅ Supported |
| iOS | N/A | ❌ Not applicable |
Note: While android:exported was made mandatory in Android 12 (API 31+) for components with intent filters, this control also locks down components without intent filters on all API levels, providing defense-in-depth across the entire Android ecosystem.
Relationship to Other Controls
ImmutablePendingIntentPrevention
ImmutablePendingIntentPrevention forces FLAG_IMMUTABLE on all PendingIntent constructions to prevent other apps from mutating the wrapped intent's extras. ExportedComponentsPrevention prevents other apps from directly invoking your components. Use both controls together for comprehensive IPC hardening:
- ExportedComponentsPrevention → "Other apps cannot talk to me"
- ImmutablePendingIntentPrevention → "If I hand an intent to another app via PendingIntent, they cannot tamper with it"
Deployment Checklist
- ✅ Audit your app's
AndroidManifest.xmlfor all<activity>,<service>,<receiver>, and<provider>elements - ✅ Identify which components legitimately need to remain exported (deep links, share targets, widgets, etc.)
- ✅ Add those components to the
allowlist - ✅ Enable the control with
enabled: true - ✅ Build the APK and review build logs for lockdown warnings
- ✅ Test all public-facing functionality (deep links, sharing, widgets, etc.) on a device
- ✅ If a feature breaks, add the responsible component to the allowlist and rebuild
- ✅ Deploy to staging and verify no third-party SDK components were inadvertently broken
- ✅ Monitor crash reports post-deployment for
ActivityNotFoundExceptionor similar IPC-related exceptions