Recently I checked my server logs to see if I was getting any failed receipt validations. I was surprised to see about 20 invalid receipts today for cut the rope experiments being used to purchase You Doodle in-app purchases. The bundle id for the receipt is com.zeptolab.ctrexperiments.
It is a well known fact that it is easy to proxy an iOS device through a server which can intercept network requests and return something other than what the real server would return. Some developers naively think they can just validate from their iOS device to Apple’s servers. This is easily intercepted and the response faked as if the receipt is valid.
There are ways using cryptographic techniques to validate the receipt on the device using OpenSLL, but that was way too complicated for me. Instead, I use a dedicated server that can send a receipt request to Apple without danger of that request being intercepted.
Here is how this works (similar to OpenAuth 1.0):
– Client app generates a unique string every time a purchase is attempted
– Client takes that string, the receipt from the purchase and a secret key and some other secret data that shall not be revealed here and creates a token string using cryptographic algorithms
– Client sends the string, token and the receipt up to the server
– Server runs the string, and receipt through the same cryptographic algorithms
– If the token the server creates does not match the client token, server returns a failure status, otherwise…
– Server sends the receipt to Apple to decode it into a dictionary of data
– If receipt is not valid (lots of things in the receipt can be checked against well known expected values such as bundleId), server returns a failure status, otherwise…
– Server generates it’s own unique string and sends this along with a timestamp and a token it creates using the same cryptographic algorithm that involves this new string back to the client
– Client runs the server string and timestamp from the server through the same cryptographic algorithm and creates a token. If the token matches, the purchase succeeds, otherwise the purchase fails.
The nice thing about this method is that the client code to send and process the response is less than a page of code. The code on the server is a couple pages of code but still very manageable and re-usable.
If the client attempts to spoof the response, they will fail because a new unique string is generated each time on both the client and the server. A request and response are only valid once and cannot be re-used.
I’m not a cryptography expert, there may be holes in this method, but hopefully it turns back all but the most determined hacker. I thought I would share my learnings with the world and maybe this will help someone else who is doing receipt validation.