Search code examples
androidin-app-purchasein-app-billing

Why verify Play Store purchases on a server?


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).


Solution

  • 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.