Search Unity

[iOS] Limiting subscription restoration?

Discussion in 'Unity IAP' started by Harrishun, Jun 8, 2020.

  1. Harrishun

    Harrishun

    Joined:
    Apr 13, 2016
    Posts:
    59
    Hi all,

    We just discovered a bit of an exploit in our app. It appears that a user of our app could potentially subscribe once, log into other accounts and restore the subscription to apply the subscription to the new account. I was doing a bit of searching and couldn't find any discussion about something like this.

    Now, I assume that this isn't an issue that Unity IAP is equipped to deal with, but I just wanted to make sure that I hadn't missed anything in my investigation. In the case that Unity IAP is confirmed to not prevent this exploit, we have a plan to fix this within our subscription validation service.

    Thanks for reading, hope to get some confirmation soon. :)
     
  2. SamOYUnity3D

    SamOYUnity3D

    Unity Technologies

    Joined:
    May 12, 2019
    Posts:
    626
    Are you restoring the subscription on the same device? If you use another account to restore the subscription, can you use this account to restore the subscription on another device?
     
  3. JeffDUnity3D

    JeffDUnity3D

    Joined:
    May 2, 2017
    Posts:
    14,446
    Keep in mind that "log into other accounts" doesn't refer to another email account on the device. It depends on the user logged into Google Play on the device.
     
  4. Harrishun

    Harrishun

    Joined:
    Apr 13, 2016
    Posts:
    59
    Ah apologies, I don't think I explained myself well enough.

    What's happening is that one Apple ID is purchasing a subscription successfully, and the logged-in account in our app is being registered as subscribed on our backend. However, after this, the user can log out of the account and log into another account in our app, and then Restore Purchases, allowing this other account to be registered as subscribed on our backend (as ProcessPurchase is called again). It seems that this could be done an unlimited amount of times. Only one Apple ID is used in this entire process, the one that originally bought the subscription.

    Again, I'm mostly just double checking, as I assume that Unity IAP does not account for this exploit since it's simply a passthrough service, and this is more an implementation issue for us as developers anyway.
     
  5. JeffDUnity3D

    JeffDUnity3D

    Joined:
    May 2, 2017
    Posts:
    14,446
    Yes, Unity IAP (and Apple) use the Apple ID (only). Question - how could this user log into multiple accounts on your system, are you assuming they know both passwords?
     
  6. Harrishun

    Harrishun

    Joined:
    Apr 13, 2016
    Posts:
    59
    We have account log-in implemented via Firebase. Users are free to create as many accounts as they like, as long as they have multiple email addresses.
     
  7. JeffDUnity3D

    JeffDUnity3D

    Joined:
    May 2, 2017
    Posts:
    14,446
    Then it is indeed the same person, and they purchased the products they own. They should be able to restore all products on all devices, regardless of your login process (which seems possibly inconsistent). ProcessPurchase would not be triggered across AppleID's, which also implies the same process. And you mentioned "an unlimited amount of times", can you elaborate? Yes, each time they purchase, they should be able to restore as many times as they want for their Apple ID, this is the Apple default behavior for non-consumables and subscriptions. If my AppleID was UserA and I created AccountsA and AccountsB in your system, I would expect UserA to be able to restore all products that I purchased for "AccountA" and "AccountB" in either/both accounts.
     
  8. Harrishun

    Harrishun

    Joined:
    Apr 13, 2016
    Posts:
    59
    The main issue here stems from one user being able to get the app-specific login details of another user (say, a friend), and then restore the subscription while logged into that account, thus exploiting the system.
    They could do this with an unlimited number of friend's accounts, allowing a group of friends to essentially get full access to the app for only the cost of one subscription (which is problematic for our particular business model).

    From what you're saying, this is the by-product of the intended behaviour from Apple that would be present on every app released via the App Store? At this stage I can see that Unity IAP does not have any control over this exploit (which was mainly what I was double checking), but it's surprising that Apple would allow something like this to take place...
     
  9. JeffDUnity3D

    JeffDUnity3D

    Joined:
    May 2, 2017
    Posts:
    14,446
    Sorry I don't follow. The exploit you suggest is not possible. You mean they have the other person's AppleID and password? Yes, that would be a problem. If the other person purchased the IAPs using their AppleID, then only they could restore, on any device. If you used their Firebase login (or whatever secondary login you are using), you couldn't restore "their" purchases on your device (or any device). Have you tested this? What do you mean "being able to get the app-specific login details". Logging into their Firebase game-specific login doesn't give you access to their Apple purchases nor provide you the ability to restore them. You won't be able to restore their purchases. The restore is securely tied to the AppleID that made the original purchase, not any secondary game login you might be using. Have you actually seen this behavior, or are you just theorizing? To be clear, you call this method to Restore on iOS https://docs.unity3d.com/Manual/UnityIAPRestoringTransactions.html which states "On Apple platforms users must enter their password to retrieve previous transactions so your application must provide users with a button letting them do so"
     
    Last edited: Jun 10, 2020
  10. Harrishun

    Harrishun

    Joined:
    Apr 13, 2016
    Posts:
    59
    Sorry, I feel like there is some unfortunate miscommunication on my part, I'll try to be a bit more explicit.
    Currently a single user of our app can:
    • Register an account for our app via our Firebase implementation
    • Log in to this newly registered account
    • Subscribe from the in-app store (registering the subscription purchase to their Apple ID)
    • [this account is registered as subscribed and the appropriate information (receipt, date purchased, etc.) is also recorded on our back-end]
    • Log out from this initial account
    • Log in to a different account (still logged into the same Apple ID on the same device) - for instance, a friend's account
    • Go to our store and press the Restore Purchases button
    • [this second (friend's) account is registered as subscribed and the appropriate information is sent to the back-end]
    If the above (specifically logging into a friend's account and restoring the subscription to register the friend's account as a subscriber) is done, then when the subscription renews, our server-side receipt validation will successfully validate the identical receipts tied to both users' app-specific accounts, meaning two users could continue to pay one price indefinitely, as long as they continue to renew the subscription. I have seen this behaviour.

    I think some of the confusion is coming from the distinction between our account service, and Apple's ID system. In all scenarios, the user who has purchased the subscription is using one device, and any extra account for our app specifically (not Apple IDs) are logging into the app on that same device.

    I have done some research into Apple receipts specifically, and have found in their documentation that they specifically state the best practice in regards to managing subscribers:
    https://developer.apple.com/documentation/appstorereceipts/transaction_id

    I can see that Unity IAP provides the transaction id at time of purchase along with the payload, so I think my team's best bet here is to be storing that id and cross-referencing the stored id's whenever a new purchase is processed, to find out if the subscription is legitimate.
     
  11. JeffDUnity3D

    JeffDUnity3D

    Joined:
    May 2, 2017
    Posts:
    14,446
    This is not possible. You would not be able to restore purchases made by another AppleID. If so, I'm sure Apple would be interested in your exploit, you would need to contact them. We are just a pass through service for their Storekit API.
     
    Last edited: Jun 10, 2020
  12. Harrishun

    Harrishun

    Joined:
    Apr 13, 2016
    Posts:
    59
    Sorry, I'm not sure where I am stating that the purchases of a different Apple ID are being restored.
     
  13. JeffDUnity3D

    JeffDUnity3D

    Joined:
    May 2, 2017
    Posts:
    14,446
    The "other id" is the Apple ID of other user who made the purchase that you claim you can restore. A purchase is always securely tied to an Apple ID. You mention "Go to our store and press the Restore Purchases button". The Restore button on Apple must be in the app, on the device and visible. You said "Subscribe from the in-app store (registering the subscription purchase to their Apple ID)" . You just mentioned "their AppleID", you can't do that. You can ONLY restore products that you personally, with your AppleID, have purchased previously. Sure you can be logged into any Firebase login you want, but you can't restore purchases that anyone else has made with their credit card. Nor can anyone else restore yours. However, if you are somehow going around Apple security by returning a receipt from your server associated with another AppleID based on their Firebase login alone, you might want to contact Apple, we would have no control over that process. And if you are suggesting that users can share an AppleID and password to restore across devices given a single purchase, yes that would be a problem. You basically have to reset your phone to use another AppleID. You've said you can reproduce so you could get the logs when running with XCode. Please provide steps to reproduce, along with the logs from the original purchase and also the restore . Please add additional Debug.Log statements that will show which IAP callbacks are fired, and add one to your restore script just prior to extensions.GetExtension<IAppleExtensions>().RestoreTransactions, they will show in the logs https://forum.unity.com/threads/how-to-capturing-device-logs-on-ios.529920/ There is also the chance that you are seeing an artifact of the Sandbox/TestFlight environment if you are able to restore from another Apple ID (or they from yours) if they are configured as testers. Also, you don't get to "choose" which products to restore, the Apple restore process restores all non-consumable and subscriptions that this AppleID has purchased (no matter which Firebase login they use), as it should.
     
    Last edited: Jun 11, 2020