I have an application, say ApplicationProvider, which holds a provider and a receiver.
Another application, say ApplicationCreator, creates an element on the provider through an intent. The ApplicationProvider get the intent and creates the element accordingly.
Now I would like ApplicationCreator to be the only one to hold the rights to access that element. So if another application tries to access that element, this generates some kind of error.
What is the best way to do so? I came up with two possible solutions, but I don't like any of them.
1) ApplicationProvider denies any direct access to the provider, instead uses the receiver to get the intents which are sent through intentForResult, so the receiver can check the caller, verify that is the original caller who created the element and grant access. Other applications would get denied because they hold a different uid. Since there is no easy way to forge the uid, this system works.
2) Any application can send intents to the provider but in the manifest i specify all the path-level permission to the single uris that are created. I manually issue an update to the application every time someone wants so create a new element.
=========================================
Additional information:
It would be ideal if different applications could put different elements and be able to retain the rights to modify those elements they created and also grant permission on those elements to whom requested said permission.
A---> +--------+ A creates content
| pro | <---B B asks permission
A<----| vid | the provider forward permission
A---->| er | A grant permission
| | --->B B is given permission and can access A's stuff
+--------+
Here are my thoughts on this topic. No code is provided, since I did not implement anything. Also, I'm not entirely sure that this solution is entirely secure.
First, authentication.
Every client who wants to use your content provider needs a key. Each client fetches one key from your application (maybe implement a content provider for that). A random sequence of letters / numbers should be fine. This key is stored within your application and the clients application, somewhere where noone else has access, i.e. a private database / preference property.
This key is used to authenticate a client application with your content provider. On each request, this key (maybe a hash of the key) is included within the request uri (much like API keys when using REST web services). Your application checks whether the provided key was previously generated. If that is the case, authentication is successful.
Next, authorization.
Each element in your database has a field ownerKey. When creating new elements, this field is filled with the key provided with the request. When accessing / modifying / deleting elements, your content provider should check whether the stored key matches the submitted key. Only perform the operation if the keys match.
Additional thoughts.
The whole point is that the random keys generated by your application stay private. I'm not sure whether it is possible for a third party to intercept interaction between a client application and a content provider. You might want to investigate this before writing any code.
Specifying a key on each request could be optional. When omitting a key during element creation, those elements do not have an ownerKey and thus are allowed to be accessed and modified by anyone without authentication/authorization.
I hope this helps somewhat.
I would suggest the uid approach, but there is no reason for this to go indirectly through a receiver. Just store the owning uid for each row in your database, and on incoming calls to the content provider use Binder.getCallingUid() to retrieve the uid of the current caller which you can validate against the row they are operating on.
Related
I am trying to determine how to access a ContentProvider which exists in another profile.
I have an app/service in the managed profile which implements a content provider.
I have a 2nd app/service in the primary profile and I want it to be able to access the managed app's content provider. i.e. cross-profile
Right now I am prototyping the implementation using a modified version of android-testdpc. I have added a ContentProvider to the testdpc source. I want to be able to access this ContentProvider, which is in the managed profile, from another app that is running in the primary profile.
I know that using DevicePolicyManager.AddCrossProfileIntentFilter() I can allow intents sent in the managed profile to also be resolved in the parent, or vice versa. The documentation states that only activity intents are supported. Using AddCrossProfileIntentFilter() I have successfully been able to pass data between the two apps by using intents and startActivity().
However, using activities is not what I want to do since in Q and later I cannot start an activity from a background app/service.
I have no problem accessing the ContentProvider if the two apps exists in the same profile but I have been unable to determine how to make this work cross-profile.
I finally got this working.
To do so I needed to use an activity in the content provider to give permission to the other app. The non-content provider app must send an intent to an activity in the content provider app, requesting permission to access the content provider, using startActivityForResult().
This activity returns an intent, setting the content provider URI with Intent.setData() and permissions with Intent.setFlags(). In my case I included Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION to make the permission persistent.
The non-content provider app gets the result intent in onActivityResult(), gets the URI from the result then calls ContentResolver.takePersistableUriPermission().
To make this work cross-profile I used DevicePolicyManager.addCrossProfileIntentFilter() to allow the permission request intent to cross profiles. When crossing profiles I noticed that the content provider URI returned in the result intent include the user id. i.e. “content://10#com.example.myprovider/test". Making a query using this URI will cross profiles.
This helped https://www.futurelearn.com/courses/secure-android-app-development/0/steps/21592
Is it possible to add a filter to a content provider to make it accept only requests coming from a set of known application IDs?
For example, if com.domain1.app1 and com.domain2.app2 are two app that I trust I'd like to make them read my data. Any other application should be kept outside.
Something like intent filter but related to incoming request from other applications.
I know about the same signing key but it's not what I need.
If a provider's application doesn't specify any permissions, then other applications have no access to the provider's data. However, components in the provider's application always have full read and write access, regardless of the specified permissions.
As seen here.
How can we ensure that certain applications are not able to access my data stored in content provider where in certain other applications can access that? Basically I need to allow some application of my interest to access my data stored in Content Provider but I do not want all the applications to be able to access that data. How can I achieve this?
Thanks.
The easiest way is to protect the content provider with a permission you define. Make it a signature a permission so only apps signed with your certificate are allowed to get it.
See:
http://developer.android.com/guide/topics/security/security.html
http://developer.android.com/reference/android/R.styleable.html#AndroidManifestProvider
http://developer.android.com/guide/topics/manifest/provider-element.html
If doing this based on certificates is not sufficient, you will need to write the permission checks yourself. This is done by calling Binder.getCallingUid() for incoming calls to your applications, and deciding whether the given uid has permission to access your provider. Actually implementing a different policy that is actually secure requires a lot of careful thought and design, though.
In the AndroidManifest.xml, at the screen with the properties of your ContentProvider, you have two fields:
Read Permission
WritePermission
So, you can define secure strings (also it may be path to some file) that are permissions for acces to your ContentProvider.
Applications that want to access your content provider must have that ones added in their UsesPermission elements.
I tried to search for this particular case but didn't find anything yet. Sharing data between two applications is possible with ContentProvider and you can set protectionLevel to signature, so applications with that signature can access the data (same publisher).
But what if the scenario is following:
Developer A and B publishes application.
Me, A and B decide to do some co-work together. I am to provide my own application, but I need to access both A and B private information.
Is this possible? A and B would need to grant my application to access their let's say ContentProvider
Thanks.
Setting protectionLevel to signature will only allow to access data between applications signed with the same key (in this case A, B and you should agree on using the same key to sign your published applications).
If this is not the case, then A and B should export their ContentProvider and (optionally) provide permissions to read/write data. If they define permissions, then your client application should specify this permission in the manifest.
References:
http://developer.android.com/guide/topics/manifest/provider-element.html
http://developer.android.com/training/articles/security-tips.html
I have multiple apps that have some sort of in-app currency (i.e gold).
The gold (Integer value) should be accessable through all of my apps. All of my own apps should be able to read/write the value.
It is important that only my apps are able to write the value, it doesn't matter if anyone else can read it.
It's also important that I do not know which of my apps is installed. There is no 'main'-app at all. It should be irrelevant which of my apps is installed and in which order.
The value does not have to be shared between the apps during runtime, I rather thought of something like sharedPreferences or an SQL database.
Where/How should I store this value?
Which is the preferred solution to this problem (if there is any)?
If you need to share data between two (or more) apps, you can set to them the same android:sharedUserId (in AndroidManifest.xml) so the other app can read/write in the private data of the other app.
android:sharedUserId
The name of a Linux user ID that will be shared
with other applications. By default, Android assigns each application
its own unique user ID. However, if this attribute is set to the same
value for two or more applications, they will all share the same ID —
provided that they are also signed by the same certificate.
Application with the same user ID can access each other's data and, if
desired, run in the same process.
Doc here.