A content provider is how Android apps share structured data, and an exposed one can hand your app's data to other apps, or hand an attacker a way to query data they should not see. Two problems show up: a provider exported when it was meant to be internal, so other apps can read it, and a provider that builds its database queries from caller-supplied input, which opens it to SQL injection from another app. Both are avoidable with the same discipline you apply to any component that accepts external input. Here is what a content provider exposes and how to secure it.
Short answer
A content provider shares structured data through a content:// URI, and other apps can query it if it is exported. Per Android's guidance, the risks are exporting a provider that serves internal data, so other apps can read or modify it, and SQL injection when the provider builds its query from caller-supplied input by concatenating it into the selection. The defense is to set android:exported="false" for providers serving private data, and for any provider that must be exported, protect it with permissions, validate the caller's input, and use parameterized queries with selection arguments rather than string concatenation. Treat a content provider that accepts input from other apps as handling untrusted input.
What you should know
- Content providers share data via content:// URIs: across apps if exported.
- An exported provider exposes its data: other apps can query it.
- Query injection is a risk: concatenating caller input into SQL.
- Default to non-exported: for providers serving internal data.
- Use parameterized queries: selection arguments, not string concatenation.
What is a content provider, and what is the exposure?
It is a component that exposes a set of data, often backed by a database, to be queried and modified through a content:// URI. When a content provider is exported, other apps can call its query, insert, update, and delete methods, which is the point when you intend to share data, and a problem when you did not. The exposure is twofold: an exported provider serving your app's private data lets other apps reach that data, and a provider that accepts caller input, a selection clause, an identifier, a sort order, and uses it to build a database query can be attacked through that input. Because a content provider sits at the boundary between your app and others, anything it accepts from a caller is untrusted, and anything it exposes is reachable by whoever can call it.
What are the risks?
Data exposure and query injection. The table lists them.
| Risk | How it happens |
|---|---|
| Exposing internal data | A provider serving private data is exported |
| SQL injection | Caller input concatenated into the query selection |
| Unrestricted modification | Exported provider allows insert, update, or delete by others |
| Path or identifier abuse | Caller-supplied identifiers reach the data unchecked |
The query-injection risk is the one developers underestimate: if your provider takes a selection string or an identifier from the caller and concatenates it into the SQL it runs, another app can craft input that changes the query, extracting rows it should not see or affecting data it should not touch, the same SQL injection seen on the web but reachable from another app. Combined with an over-exported provider, that turns your data store into something other apps can probe.
How do you secure a content provider?
Limit exposure, enforce permissions, and parameterize queries. Set android:exported="false" on any provider that serves data only your app needs, so other apps cannot reach it at all. For a provider you genuinely must expose, protect it with permissions, ideally signature-level so only your own apps can call it, and consider granting temporary URI permissions for specific data rather than broad access. Treat everything the caller supplies as untrusted: validate identifiers and parameters, and build database queries with parameterized selection arguments rather than concatenating caller input into the SQL, which closes the injection path. Restrict which data and operations the provider exposes to the minimum the legitimate use needs. The principle is the same as for any external-facing component: do not expose more than necessary, and never trust caller input in a query.
What to watch out for
The first trap is an exported provider serving internal data, which lets other apps read or modify it; set it non-exported. The second is building the provider's query by concatenating a caller-supplied selection or identifier, which is SQL injection from another app; use parameterized selection arguments. The third is exposing insert, update, or delete to callers that should only read, or not at all. A pre-submission scan such as PTKD.com (https://ptkd.com) reads the compiled APK or AAB against OWASP MASVS and surfaces your exported components, including content providers, so you can confirm a provider is not exposing data it should not before you ship. The query handling and permissions you implement in the provider.
What to take away
- A content provider shares structured data via a content:// URI, and an exported one lets other apps query or modify it.
- The risks are exposing internal data through an exported provider and SQL injection when caller input is concatenated into the query.
- Set
android:exported="false"for internal providers, protect any exported one with permissions, validate caller input, and use parameterized selection arguments. - Use a pre-submission scan such as PTKD.com to confirm your content providers are not exposing data they should not.



