1
votes

The Android in app purchase documentation says to verify purchases on a server and not on the device, because otherwise an attacker could decompile the app and auto-verify purchases. Well, how does verifying on the server help? I'll illustrate with pseudocode. Here's scenario 1 with on device verification.

public boolean verify(data){ return Security.verifyPurchase(data); }

Attacker replaces with this:

public boolean verify(data){ return true; }

Scenario 2, verification on the server:

public boolean verify(data) { return verify("https://verify.server.com",data); }

Attacker replaces with this:

public boolean verify(data){ return true; }

So the only way this makes sense is if the purchased product is also provisioned from the server, right? If it's unlocking a feature, you would have to download something from the server that the feature can't work without, otherwise the attacker can just decompile and verify the purchase (or decompile and turn on the feature, skipping the purchase).

1
The reason is because your pseudocode isn't entirely how the purchase verification flow works. Let's consider username/password authentication for a moment- why doesn't a hacker just change the code that verifies a user to "true" as in your example? Normally it's because there isnt' a verification function that returns a boolean- normally the server is actually returning data such as the contents of the purchase upon verification. This data is not stored on the device and a hacker would have no means of accessing it. Try looking here as an example: oauth.net/articles/authenticationShn_Android_Dev
Well that's kind of what I'm saying - if all the server does is verify the purchase, this doesn't work. The server has to also return something that the client needs in order to provide the purchased good to the user. If your purchase doesn't naturally lend itself to that, you will have to make up some way for it to work that way. I'm just making sure I understand this correctly and I'm not missing something.nasch
I updated my comment. Check into OAuth if you're not already familiar with it. It provides great detail into how it works.Shn_Android_Dev

1 Answers

4
votes

The reason Play Store purchases should be verified on a separate server is that an "attacker" can reverse engineer your apk relatively easily, but the same is (very likely) not true for a server.

It is also explained in the documentation how to achieve this

Validate on a server

By implementing your signature verification logic on a server, you make it difficult for attackers to reverse-engineer your APK file. This preserves the integrity of the signatures that your logic checks.

To validate purchase details on a trusted server, complete the following steps:

  • Ensure that the device-server handshake is secure.
  • Check the returned data signature and the orderId, and verify that the orderId is a unique value that you have not previously processed.
  • Verify that your app's key has signed the INAPP_PURCHASE_DATA that you process.
  • Validate purchase responses using the ProductPurchase resource (for in-app products) or the SubscriptionPurchase resource (for subscriptions) from the Google Play Developer API. This step is particularly useful because attackers cannot create mock responses to your Play Store purchase requests.

If it's unlocking a feature, you would have to download something from the server that the feature can't work without

Yes, exactly, see further down:

Protecting your unlocked content

To prevent malicious users from redistributing your unlocked content, do not bundle it in your APK file. Instead, do one of the following:

  • Use a real-time service to deliver your content, such as a content feed. Delivering content through a real-time service allows you to keep your content fresh.
  • Use a remote server to deliver your content.