Quick Explanation of the Receipt File

I was explaining the MAS receipt file to a friend and decided to write a blog post instead of an email, on the principle Scott Hanselman has brilliantly advocated that we should conserve and amplify the limited number of keystrokes we have left in us.

I'm purely a Mac developer at the moment, but as far as I know this high-level explanation applies to iOS as well. No idea about watchOS.

WHAT IS THE RECEIPT?

The receipt is a file on your computer used for DRM of purchases from the App Store. There is a separate receipt for each app.

The receipt file contains:

  • Identifying information about the computer on which the app is installed — specifically, the MAC address of the device's primary network interface. Note that "MAC address", with the "MAC" in all caps, is a networking term. Nothing to do with "Mac" as in "Macintosh".

  • Identifying information about the app that the receipt is for — specifically, the app's bundle ID and version number.

  • Purchase history. Information about the app purchase and any subsequent in-app purchases.

WHERE DOES THE RECEIPT LIVE?

The receipt file is located inside the application bundle. On the Mac, it is at Contents/_MASReceipt/receipt. (If you don't know what the application bundle is, I've written another quickie blog post explaining it.)

WHO IS RESPONSIBLE FOR DRM?

The system is responsible for creating the receipt. By "system" I mean the OS in conjunction with the App Store. The system code-signs the receipt to prevent tampering. It's the job of Apple's developers to have gotten this right.

The application is responsible for validating the receipt. It's the job of the app developers to get this right.

WHEN DOES THE RECEIPT GET WRITTEN?

The receipt gets created on your computer when you download the app from the App Store. The receipt is then updated in the following situations:

  • If a newer version of the app becomes available on the App Store, the receipt is updated when you upgrade to the newer version.

  • If the app supports in-app purchases, the receipt is updated whenever you do an in-app purchase.

  • When you "Restore Purchases", the receipt is updated.

Here's what I mean by "Restore Purchases". Certain types of in-app purchases apply to all computers on which you have installed the app, not just the computer on which you made the purchase. A typical example would be an IAP that unlocks premium features. If you made such a purchase on a different computer, you may need to explicitly "restore" that purchase on the computer you are using. Apple's App Store rules require the app to provide a way for you to do this. Usually the app provides a button or menu item with a name like "Restore Purchases".

HOW DOES THE APPLICATION USE THE RECEIPT?

The application does two things with the receipt:

  • It validates the file contents to confirm the receipt hasn't been tampered with.

  • It checks in-app purchases to see what enhanced functionality the user has purchased. In-app purchases may include permanent "unlocking" of features, or they may be subscription-based, in which case the app should check the expiration dates of the subscriptions.

Both of these steps are performed by parsing the receipt file, which is a science in itself. There's an excellent article on objc.io that explains what is required:

You may wonder why Apple hasn’t provided a simple API to validate the receipt. For the sake of demonstration, imagine that such a method exists (for example, [[NSBundle mainBundle] validateReceipt]). An attacker would simply look for this selector inside the binary and patch the code to skip the call. Since every developer would use the same validation method, hacking would be too easy.

Instead, Apple made the choice to use standard cryptography and encoding techniques, and to provide some help — in the form of documentation and WWDC sessions — for implementing your own receipt validation code. However, this is not an easy process, and it requires a good understanding of cryptography and of a variety of secure coding techniques.

Of course, there are several off-the-shelf implementations available (for example, on GitHub), but they are often just reference implementations and suffer from the same problem outlined above if everybody uses them: it becomes very easy for attackers to crack the validation code. So it’s important to develop a solution that is unique and secure enough to resist common attacks.

MORE LINKS

Leave a Reply

Your email address will not be published. Required fields are marked *