
Use explicit intents whenever you can to keep your app’s data away from prying eyes. Limit intent filters in your AndroidManifest and never use wildcards unless it’s absolutely necessary. Only request essential permissions, and always validate where intents come from before trusting their data.
Key Takeaway
- Explicit intents and minimized filters reduce attack surface for intent hijacking.
- Request only necessary permissions and validate all incoming intent data.
- Always authenticate intent senders and never trust extras blindly.
Managing Intent Filters Securely
The first time we built a student project at our bootcamp, the manifest had a dozen intent filters, most with wildcards. Back then, we didn’t realize that every extra filter was a door left open. Attackers love when developers overexpose components. Android’s intent filters control which app components can be triggered by the system or other apps. When we audit our apps, we look for loose intent filters first.
A secure mobile coding approach means:
- Only defining intent filters for actions and data types your app actually uses.
- Never using a wildcard for android:scheme or android:host unless it’s completely required.
- Using explicit intents for internal app communication, so no other app can intercept or trigger it.
We’ve seen that apps with broad or generic intent filters (especially those with wildcards or missing categories) get targeted by malware looking to hijack or snoop on data. It’s almost always safer to start minimal and add as you prove need.
Intent Filter Configuration Best Practices
We recommend thinking of the AndroidManifest as a security perimeter. Each intent filter is like a checkpoint. Our instructors have reviewed hundreds of manifests, and the most secure ones do three things:
- Specify exact actions and categories.
- Avoid wildcards for actions, categories, schemes, and hosts.
- Prefer not to declare data elements unless your component truly processes that data.
For example, if your activity only needs to handle a single custom action, declare just that action. Add only the DEFAULT category unless you have a strong reason to include more. The less you expose, the safer your app.
<activity android:name=”.MyActivity” android:exported=”false”>
<intent-filter>
<action android:name=”com.example.ACTION_VIEW” />
<category android:name=”android.intent.category.DEFAULT” />
</intent-filter>
</activity>
We see that specifying intent filters this way (tight, explicit, minimal) is the best way to avoid accidental exposure.
Minimizing Intent Filters and Avoiding Wildcards
Wildcards are tempting. They make testing easier, at least at first. But we learned quickly that wildcards let malicious apps slip through. If you use patterns like android:scheme=”*”, you’re inviting trouble. Malware can register to handle those schemes and intercept data or actions meant for your app.
Instead:
- List only the schemes and hosts your app actually uses.
- Don’t accept every MIME type unless you process all types safely.
- Remove any test filters before release.
We’ve seen security audits flag apps for a single careless wildcard. It’s worth the time to review every filter and ask, “Do we need this?”
Specifying Only Required Actions and Categories
Our instructors stress that each additional action or category in an intent filter is another potential entry point. The best manifest configurations are boring. They list only what’s needed.
- Use only android.intent.action.VIEW if your component just displays data.
- Add additional categories only if your component requires them for specific system interactions.
- Never add custom actions or categories unless you control both the sender and receiver.
Specificity is security. When in doubt, leave it out.
Managing Exported Components
Exported components are the gates into your app. If you leave one open by accident, attackers may find it. Since Android 12, you must declare android:exported for every activity with an intent filter. We train our students to set android:exported=”false” by default, then change it only if the component needs to be accessible to other apps.
- Activities, services, or receivers that don’t need external access should always be exported=”false”.
- For components that must be accessible, restrict them as much as possible with permissions.
We’ve caught more than one app where a forgotten exported service led to a security incident.
Setting android:exported Attribute Correctly for Android 12+
It’s easy to get tripped up by this requirement on newer Android versions. If you forget to set android:exported on a component with an intent filter, your app won’t even install. We always remind teams:
- Review every component in your manifest.
- Set android:exported=”false” unless it genuinely needs to be called from outside your app.
- Document why any component is exported.
This habit saves headaches, and it’s a basic defense against intent-based attacks. [1]
Examples of Secure Manifest Configurations
We keep a library of manifest examples for students. The safest patterns look like this:
- Internal-only activity:
<activity android:name=”.InternalActivity” android:exported=”false” />
- Activity that handles a specific external action, but only for your app’s data:
<activity android:name=”.ImportActivity” android:exported=”true” android:permission=”com.example.MY_PERMISSION”>
<intent-filter>
<action android:name=”com.example.ACTION_IMPORT” />
<category android:name=”android.intent.category.DEFAULT” />
</intent-filter>
</activity>
- BroadcastReceiver limited by signature permission:
<receiver android:name=”.MyReceiver” android:exported=”true” android:permission=”com.example.SIGNATURE_PERMISSION”>
<intent-filter>
<action android:name=”com.example.ACTION_SYNC” />
</intent-filter>
</receiver>
Students often ask if these patterns are too strict. Our answer: you can always loosen controls later, but you can’t undo data leaks.
Permission Strategies for Intent Security
Credits: MalwareFox
Permissions are your next line of defense. We’ve all seen apps that ask for every permission under the sun. That’s a liability. Every unneeded permission is a risk. At our bootcamp, we teach devs to follow secure coding principles:
- Request only essential permissions.
- Understand what each permission unlocks for attackers.
- Use custom permissions and signature-level protection wherever possible.
Minimizing Permission Requests
Before adding a permission to the manifest, we ask, “What breaks if we remove this?” If the answer is “nothing important,” it doesn’t belong. The fewer permissions your app has, the less an attacker can do, even if they find a way in.
Requesting Only Essential Permissions
Sometimes, a library or SDK will prompt you to add permissions you don’t actually use. We audit all external dependencies and strip anything that’s not strictly required. If our app doesn’t use the camera, why let a library sneak that permission in?
- Review the permission list before every release.
- Challenge each one.
Users notice when apps ask for too much, and so do attackers.
Reviewing Permissions Required by External Libraries
We’ve seen popular libraries request dangerous permissions, like READ_CONTACTS or SEND_SMS, without a clear reason. Our instructors insist on reading the documentation and code (when possible) for every third-party dependency. If the library needs a risky permission, we either sandbox it, find an alternative, or remove it.
Utilizing Permission Protection Levels
Android’s permission levels, normal, dangerous, signature, let us fine-tune access. For sensitive actions (like sending broadcasts or accessing private data), we always:
- Use signature permissions for internal-only communication between our own apps.
- Apply custom permissions when sharing data with trusted partners.
This way, random apps can’t just declare an intent filter and start receiving or sending sensitive data.
Applying Signature-Level Permissions
Signature-level permissions are powerful. Only apps signed with the same certificate as ours can interact with protected components. We use this for internal services and receivers. It’s like having a secret handshake between apps we control.
Using android:permission Attribute for Components
We always add android:permission to services, receivers, and activities that handle sensitive data or actions. This forces other apps to have our permission before talking to these components. It’s a simple line in the manifest, but it stops a lot of attacks cold. [2]
Runtime Permission Handling
Android’s runtime permission model helps users, but also helps us. We train our students to ask for permissions only when the user actually needs the feature. This builds trust and keeps the app off security radars.
Requesting Permissions at User Action Time
No one wants an app that asks for everything up front. We only request permissions when the user presses a button that really needs it. If they never use the feature, we never ask.
Enhancing Permission Transparency to Users
We add clear explanations in our UI for every permission prompt. Users should know why you’re asking. If you can’t explain it, maybe you don’t need it.
Validating Intents and Authenticating Sources
Too many apps treat all incoming intents as friendly. That’s a mistake. We always check:
- That the intent data matches expected patterns (e.g., correct URI, MIME type).
- That the sender is who we expect, using package name checks or permissions.
- That intent extras don’t contain malicious payloads.
Validating Incoming Intent Data
It’s easy to assume that intents from the system or other apps are safe. We don’t. Before acting on any intent, we validate its data. If it contains a URI, we check it’s a well-formed URI. If it has extras, we check their type and value.
Techniques to Prevent Injection Attacks
Intent injection is real. We sanitize all incoming data. If an intent extra is supposed to be a number, we parse and verify. If it’s text, we escape it before displaying or processing.
Sanitizing Intent Extras Before Use
We never use intent extras directly without checking them. For example, we parse integers safely and strip out dangerous characters from strings. If an extra controls a sensitive action, we double-check its value against a whitelist.
Verifying Sender Identity
Android doesn’t make sender verification easy. When possible, we check the sender’s package name or UID. For extra security, we use custom permissions or signature checks. If we can’t verify the sender, we treat the data as suspect.
Implementing Custom Permissions and Signature Checks
We define custom permissions for key app features. Only apps we trust, signed with our certificate, can access them. This stops most attacks before they start.
Whitelisting Trusted Apps and Restricting System Broadcasts
For system broadcasts, we whitelist trusted apps and restrict sensitive broadcasts with permissions. If only our app should receive or send a broadcast, we make it explicit in the manifest.
Secure Handling of Result Intents
Returning data via setResult can be risky. We never pass untrusted intent objects to setResult. If we must, we strip all URI permission flags before returning. Otherwise, we risk leaking data to the wrong app.
Avoiding Passing Untrusted Intents to setResult
Whenever we get an intent from another app, we build a new, clean intent for the result. We never just forward what we received. That’s how data leaks happen.
Removing URI Permission Flags Before Returning

If we share a URI, we always remove permission flags. That way, the receiving app can’t access data it shouldn’t, just another habit that supports secure data storage and intent protection.
FAQ
How can improper use of intent filters lead to Android intent hijacking or spoofing?
When intent filters in your AndroidManifest.xml are too broad or lack proper specificity, malicious apps can intercept or hijack intents not meant for them. This opens up your app to intent spoofing Android attacks. To avoid this, follow intent filter best practices like using exact action, category, and data fields.
Also, validate all incoming intents, especially those sent to exported components. Intent spoofing can bypass Android app permission security if you don’t enforce proper Android component security checks and intent sender validation. Use the exported attribute Android has added for more clarity in Android manifest permissions.
What’s the risk of using implicit intents without intent sender verification or package filtering?
Implicit intents without sender verification or intent package filtering Android settings can be intercepted by any app with a matching filter. That means an unintended or malicious app could claim the intent and misuse it. This compromises Android inter-app communication security and makes Android intent interception much easier.
Always use explicit vs implicit intents carefully, prefer explicit ones when passing sensitive data. Use package name filtering Android techniques to narrow the intent target. For Android sensitive data protection, secure intent communication is non-negotiable.
How can I prevent untrusted apps from abusing my Android broadcast receivers?
To build a secure Android broadcast receiver, use Android signature permissions when only trusted apps should trigger it. Do not mark receivers as exported unless absolutely necessary. For local-only communication, use LocalBroadcastManager. Implement intent permission protection and verify the sender in code.
Avoid dynamic registration in exported components unless proper Android permission enforcement and validation is done. Protecting Android activities and receivers means controlling what gets triggered and by whom, otherwise, you open yourself up to intent-based attacks Android can’t prevent on its own.
Why should I sanitize intent extras, and how does that relate to Android permission validation?
Unvalidated or unsanitized intent extras can introduce vulnerabilities, especially if the receiving component assumes the data is safe. This oversight leads to Android data leakage prevention failures and can cause unexpected crashes or security flaws. Intent extras sanitization should include null checks, type checks, and value constraints.
Pair this with strong Android permission validation to make sure sensitive actions only proceed when proper Android runtime permissions are granted. This helps detect Android app security vulnerabilities early in development and avoids accidental permission misuse Android apps are prone to.
What strategies can help detect malicious use of exported components in Android apps?
First, review Android exported components control using static analysis tools or Android APK permissions analysis. Check if exported activities, services, or receivers need to be public, many don’t. Use Android dynamic permissions carefully and limit permission scopes. Detect intent spoofing Android attempts by validating all intent data.
Enforce permission-based intent restriction and avoid relying solely on Android system permissions. To detect malicious app detection Android issues, run Android security testing intents under real conditions. This helps reveal apps attempting to bypass Android security model intents.
Conclusion
We’ve seen it all, exposed components, unchecked intent filters, and apps that trust way too much. The fix? Start strict. Review often. Assume every bit of data is hostile until proven safe. It’s far easier to loosen security than clean up a breach. If you’re serious about protecting your Android apps, there’s a smarter way to code.
Join the Secure Coding Practices Bootcamp for hands-on Android security training
References
- https://cafonsomota.medium.com/android-12-dont-forget-to-set-android-exported-on-your-activities-services-and-receivers-3bee33f37beb
- https://developer.android.com/guide/topics/manifest/permission-element