I'm currently trying to get the hang of in-app purchases on Android. I want to implement a non-consumable one-time product, i.e. allow perpetual access to all content in the app after purchasing the product.
In the Android developer guide for In-App-Billing there is a "Best practices" section (see https://developer.android.com/google/play/billing/billing_best_practices ).
It states: "It's highly recommended to validate purchase details on a server that you trust. [...] By implementing your signature verification logic on a server, you make it difficult for attackers to reverse-engineer your APK file."
I do not understand the logic behind this claim - assuming one does not have user accounts in the app.
Both methods seem to implement a signature check - either on the device or on a remote server.
I guess that the cracking tools that are available are able to scan for and disable the on device security check - so maybe this is meant by "making it difficult".
Otherwise I don't see a difference whether a reverse-engineer looks for the on-device check vs. looking for the server-roundtrip. Both calls can be be removed or replaced by an "always true" return value.
The best practices document also states: "verify that the orderId is a unique value that you have not previously processed".
Even this seems questionable to (for?) me.
Assuming that the reverse engineer is not able to find and replace the verification round-trip to the server and you keep track of all order ids on a remote server - they still are not tied to any user id. So how can you decide, whether
a user has switched devices (or lost his data) and is restoring purchases or
someone tries to gain access to paid content illegitimately?
(I guess you could check via heuristics - say allow 3 same order ids in X months, but there would always be the risk of blocking legitimate buyers.)
These questions appear to be rather basic to me - therefore I assume that I'm simply not getting the whole picture. I would be very thankful, if someone could shed some light on this.
Related
I am creating an iOS and Android App and I want to create a screen where the player can start with a guest account or can connect his account with our own accountsystem.
But my question is: Can I detect a user after the app was uninstalled and installed again?
I know that there is something like the vendor. But this will change.
I know that other apps also can do this.
With the user's permission- have them log into an account. Or provide you with their google of facebook account info. So far as hardware ids, those are discouraged and actively being removed to prevent people from the API to prevent this.
Also remember- that unless the user logs in with an account, you don't really know whether it is the same person. You could know its the same phone, but you don't know if he gave it to his kid sister to play on. Or sold it when he got a new one, and now you've given the new owner access to someone else's account. Also, if you rely on hardware ids you won't know its me when I buy a new phone and download it on that.
So yeah- either have him log in with a username and password, or use a 3rd party signon mechanism like Google or Facebook.
For iOS, there is a recommended approach to do that: by using the DeviceCheck framework. The idea here is that it allows you to persist 2 bits of data across app installations on each device. You can set the first bit to 1 if the user has already installed the app or 0 otherwise. And use the second bit, for example, to check if the user has signed in or not.
The official documentation is pretty good, please check it out.
The downside of this approach is that you will also have to do some work on the backend side.
UPDATE:
If you specifically want to detect the account, there is no reliable approach. One of the options is to use identifierForVendor or generate some kind of device fingerprint (for example, by combining the device model, timezone, locale, etc.), but of course, this will not work every time.
Uniquely identifying a device is a security leak, and all platforms are putting serious restrictions on unique persistent identifiers because of privacy concerns.
I know that they are possible since I implemented consumable purchases and they work but:
If I upload a new version of the app or re install it the consumable purchases are gone. Does not matter if for iOS or android. I used the device ID but that is not reliable and does indeed change in some scenarios
I would like to offer inapp purchases (consumable) and keep track how many a user has. Ideally over multiple devices but for sure for multiple installations (meaning they keep them after updates or reinstalls)
Is this even possible without some kind of login? Is it possible to implement subscriptions without this problem? I use revenuecat if that matters but asking more generally
In case of consumable in app purchase it is definitely lost and cannot be recovered across devices without saving them for the relevant user. You may have to create your own saving and recovery system if you wish to make it available across devices.. maybe you can take a look at non consumable products or subscription
Good afternoon
Theoreticaly Possibly, but there are several ways that you need to use all together in order for everything to work correctly.
For example - It is necessary to store the user_id in the keyChain, then the saved id will not change when reinstalling (this only applies to iOS). It is also necessary to get information from the Receipt for different users (with different devices) but having one check and merge them. Therefore, it is desirable to have 2 ids for the user user_id and device_id. You need to receive and process webhooks from Apple/Google to understand the current status of a particular product. Doing this on the client is not the most pleasant experience. Some of this is generally impossible to do only on the client side.
Better use the ready-made solution Apphud, RC ...
If you have a free time and a team of backend developers, then you can implement all this yourself.
Look at the internal implementation of the services above (the code is open) how they work with Receipts and ids. You will understand that without the usage of unique identifiers you will encounter a lot of problem cases.
My app is free and, using in-app purchases, I'd like to enable additional functionality. To do so, I am envisioning generating an unlock code that is dependent on the current user and the current app version. I need the first so that the same key won't work with someone else's installation (but will work for multiple devices owned by the same user). I may not need the second, but it would give a bit more future flexibility.
To do this, I need some sort of google user id. Is there such a thing? If so, how do I get it?
Thanks.
Google takes care of some of this for you. It will deliver a device dependent key to each device for a user. Your job is
1) Validate the key properly on your server
2) Make sure you return a device-specific authorization value from the server so users cannot just copy data files around.
3) On the device, use the authorization value to enable things.
Validating the key is easy but do make sure you do it on a server. The problem is identifying the device. Google and its partners dropped the ball a bit on that. Tim Bray has a blog article at http://android-developers.blogspot.ca/2011/03/identifying-app-installations.html that discusses the problem. I recommend using the ANDROID_ID despite his reservations. I have doubts about the security of his other proposal but haven't done a full analysis. Definitely stay away from anything network related (IMSI, IMEI, ESN, ...). I also cache the device identifier used and make sure it doesn't change.
Personally, I scramble the data needed to unlock my features in my app. When I get a valid transaction, I compute a key that can be combined with the device id to unlock the data. It is far from perfect but seems to work. I accept that I will be hacked - I just don't want it to be obvious.
If you want a per-version key, then you need to do a RESTORE_TRANSACTIONS and re-validate at the server any time the version changes.
I'd like to suggest another approach - no better, just different. Use a flag to indicate whether to allow or not running the upgrade features. Periodically validate with the in-app billing apis whether the user has purchased the upgrade. If not, reset the flag.
The user can hack the flag, but sooner or later it will be reset, blocking use of the features.
To truely break this method, the user would have to reverse engineer the code and bypass checking the flag. I doubt most people would bother with that, but what do I know?
I would like to use in-app billing in my android application (instead of creation 2 versions of application - free and pro). If user paid, then additional options in Preferences should be available.
My application synchronizes data with website (not my).
Each time synchronization happens, I would like to check if user paid or not.
How should I do it?
I think the easiest means for you would be to use Managed purchase. More on it is at http://developer.android.com/guide/market/billing/billing_admin.html#billing-purchase-type
The "manage by user account" purchase
type is useful if you are selling
items such as game levels or
application features. These items are
not transient and usually need to be
restored whenever a user reinstalls
your application, wipes the data on
their device, or installs your
application on a new device.
Update: website and API updated refer this now https://developer.android.com/google/play/billing/api.html
There are to many ways to do that, i will try and give you some of the most used
what about user accounts? can your application support users? if it does, its practically solved with 1 user per 1 account. just add a flag to your DB for each user. this is the most secure way, its very easy to know if someone is stealing from you thats why all MMO's (like WOW) use this type of check. you can easily know if theres more then one user on an account as well
the downside is that it requires more processing and some support is case someone's account gets stolen or other user support of that kind.
you can save a sort of key inside your application. and some other key on the server. if the key matches (either 1 to 1 or after some manipulation) then you have yourself a paid user, other you dont.when someone buys the application then change the key on the device.
just do what you dont want to do and have 2 applications for free and paid it will probably be less work then the other 2 list above.
there are many ways of doing what you want. but i'll tell you this:
most applications use method 2 but on the opposite direction, meaning serial key.
i dont think your gonna create an mmo so theres no need for option 1 unless you really dont want anyone hacking your application (which i assume no matter what you do it will be hacked in todays world. i'll advice you to trust those who pay and accept those who dont)
method 3 is just easy in my opinion especialy when you want to just remove menu options, but it also creates duplicate repositories for code.
good luck. what ever you decide is good, make a theft protection, if someone wants to steal it, they will, dont fight it (even PS3 got hacked in the end)
As Google stores your purchases of Android apps, I was wondering if they are somehow offering a webservice that can be used to check if a certain app has been purchased. To me this seems the most secure way of distinguishing free and paid users from within my app.
It would not only defeat piracy, but would also allow for managing a database of legally registered users, by a one-time check through this service.
What are your solutions to this matter?
Google provides a library for you. See here.
Note that this has been compromised in unprotected apps, so you should use something like ProGuard. The link above contains more information.
Finally, keep in mind that the Android Market is not the only app market for Android out there. Amazon has their own DRM.
At this particular moment there is one way to check.
The Android Market authenticates purchases through Google Checkout, but Google has not implemented the Checkout API to synchronize Market purchases. So calls like that check the status of an order do not work. Maybe Google doesn't want dev's to get a hold of customer emails? Either way, I do not know why it does not exist, it seems to me that if Google simply made this service available we could implement a far more superior security system that would better utilize the methods of obfuscation. As it is at the moment, a hacker can simply look for a specific class like the ILicenceService and hook it.
The only way at the current moment is to download a list of current purchases. You can do this using an authenticated http call. The drawbacks to doing this are pretty large though. First, the list it provides only extends back 31 days (so you've got to make sure you keep everything). Second, you would have to call and parse at least every ten minutes. Actually faster than that, most users want to play their games when they buy them. Third, if your service, or server goes down, that is precious information that is being requested almost instantly by your users.
I don't know how many dev's are currently utilizing this process, I considered it, but am just going to keep complaining to Google for a better method.
You could piggy back off of the LVL. I have just implemented a similar system.
Perform Check via LVL.
On success make a post to your own web server and store whatever details you need, i.e. DeviceID etc.
You could also perform checks to your own server even when the LVL check fails and allow things like trial periods etc.