How to stop cheating for scores and IAP

I have wrote a small article/tutorial on how it is possible to check if someone has cheated by modifying your apps saved data. Especially useful for IAP and high scores.

Take a look hereand let me know if you find it useful.

Seems like a good way to do it.

We actually encrypt the whole json file, which has the advantage of making the whole thing unreadable:

local openssl = require "plugin.openssl" local cipher = openssl.get\_cipher ( "aes-256-cbc" ) local crypto = require "crypto" local hash = crypto.digest( crypto.md5, "this\_key\_is\_some\_jumbled\_chars\_that\_I\_made\_up")) --to save local myTable = json.encode({score = 100, lives = 2, unlockedWholeGame = false}) local dataToWrite = mime.b64(cipher:encrypt (myTable, hash)) saveData(dataToWrite) --to load local rawData = loadData("myData.json") local jsonData = cipher:decrypt ( mime.unb64 ( rawData ), hash ) local myTable = json.decode(jsonData)

where loadData and saveData are obviously functions that I’ve made to load and save json data (like the loadSave ones). In both your example and mine, you just need to be careful that the key doesn’t get revealed anywhere.

I did think about encryption.

Do you have to tell Apple that your are using encryption when submitting via iTunes connect?

We were encrypting data going back and forth to our server as well, so we told them we were using encryption anyway.  

Not sure if you would have to for just local encryption.

This approach will expose your encryption key (and the used cipher) in plaintext in your APK file (or whatever it’s being packed into), making it very easy to break.

I’m sure you know that already, but just posting it here for anyone else who might not find it so obvious.

Edit: Oh, and the checksum approach can be broken by decompiling the lua files in resource.car – which is definitely a bit more cumbersome, but doable.

Any approach is crackable given the right time and effort. The point is the data is not there in plain text. I’d rather make them work for that impossible high score!

I agree that most things can be hacked given enough effort. However, the amount of effort is important. For someone with programming skills, breaking simple approaches like the ones listed here is a 5-minute job. When hackers have broken your security, they will build and distribute tools that anyone can use - no technical skills required. So you’re not really making anyone work for it - well, only the first one, who actually enjoys that work.

I’m not trying to bash your article, which I think is interesting and well-written. It’s just my opinion that this has no real effect in preventing hacking.

Seems like a good way to do it.

We actually encrypt the whole json file, which has the advantage of making the whole thing unreadable:

local openssl = require "plugin.openssl" local cipher = openssl.get\_cipher ( "aes-256-cbc" ) local crypto = require "crypto" local hash = crypto.digest( crypto.md5, "this\_key\_is\_some\_jumbled\_chars\_that\_I\_made\_up")) --to save local myTable = json.encode({score = 100, lives = 2, unlockedWholeGame = false}) local dataToWrite = mime.b64(cipher:encrypt (myTable, hash)) saveData(dataToWrite) --to load local rawData = loadData("myData.json") local jsonData = cipher:decrypt ( mime.unb64 ( rawData ), hash ) local myTable = json.decode(jsonData)

where loadData and saveData are obviously functions that I’ve made to load and save json data (like the loadSave ones). In both your example and mine, you just need to be careful that the key doesn’t get revealed anywhere.

I did think about encryption.

Do you have to tell Apple that your are using encryption when submitting via iTunes connect?

We were encrypting data going back and forth to our server as well, so we told them we were using encryption anyway.  

Not sure if you would have to for just local encryption.

This approach will expose your encryption key (and the used cipher) in plaintext in your APK file (or whatever it’s being packed into), making it very easy to break.

I’m sure you know that already, but just posting it here for anyone else who might not find it so obvious.

Edit: Oh, and the checksum approach can be broken by decompiling the lua files in resource.car – which is definitely a bit more cumbersome, but doable.

Any approach is crackable given the right time and effort. The point is the data is not there in plain text. I’d rather make them work for that impossible high score!

I agree that most things can be hacked given enough effort. However, the amount of effort is important. For someone with programming skills, breaking simple approaches like the ones listed here is a 5-minute job. When hackers have broken your security, they will build and distribute tools that anyone can use - no technical skills required. So you’re not really making anyone work for it - well, only the first one, who actually enjoys that work.

I’m not trying to bash your article, which I think is interesting and well-written. It’s just my opinion that this has no real effect in preventing hacking.

@_memo

I understand that there is probably no way to prevent cheating 100%, but can you propose a way around saving the encryption key in plain text? If it’s easy to decompile the app then isn’t it also possible to obtain other sensitive data such as your developer key for a third party service (like Parse), and thus allow that person to mimic calls from your app?

Just curious. Thanks

Vince:

Yep, anything you put in your config files can be fetched from them.

And yes, there is a way to avoid storing your encryption key in the app: store it in the mind of the user. If you encrypt your data with a password only the user knows, then the encryption key is not exposed. However, this requires the user to log into your app every time you need to fetch the encrypted data. Since there is still the issue of bruteforcing, this would require a relatively strong password (long, alphanumeric), which is cumbersome for a user to enter. So your user experience would suffer from the security. That’s how it usually is with security. :slight_smile:

@_memo

I understand that there is probably no way to prevent cheating 100%, but can you propose a way around saving the encryption key in plain text? If it’s easy to decompile the app then isn’t it also possible to obtain other sensitive data such as your developer key for a third party service (like Parse), and thus allow that person to mimic calls from your app?

Just curious. Thanks

Vince:

Yep, anything you put in your config files can be fetched from them.

And yes, there is a way to avoid storing your encryption key in the app: store it in the mind of the user. If you encrypt your data with a password only the user knows, then the encryption key is not exposed. However, this requires the user to log into your app every time you need to fetch the encrypted data. Since there is still the issue of bruteforcing, this would require a relatively strong password (long, alphanumeric), which is cumbersome for a user to enter. So your user experience would suffer from the security. That’s how it usually is with security. :slight_smile: