Best way to save values to disk and prevent tampering? (Without over-the-top encryption)
I am programming a game, and I wan开发者_如何学Pythont highscores to be able to be saved locally, but I dont want the user to be able to go into a plist file and change the values. What is the best way to make it difficult for users to edit the highscores easily. Would making the value an NSNumber that I then write as NSData be sufficient?
AES is probably more trouble than it's worth here. Much as I respect Jeff LaMarche, this implementation is not good. It's adequate for this purpose, but it's not something to copy around in my opinion unless you understand what's right and what's wrong with it. I talk about this at length in Properly encrypting with AES with CommonCrypto and include how to do AES correctly; but you probably don't want the hassle of doing AES correctly here, and it wouldn't buy you much if you did.
My recommendation is a simple checksum hash:
- (NSUInteger)checksumForScore:(NSUInteger)score player:(GKPlayer *)player {
NSString *string = [NSString stringWithFormat:@"%d%@%@", score, [player playerID], kLongRandomPassword];
return [string hash];
}
You then store the checksum along with the score. You validate it like:
- (BOOL)isValidChecksum:(NSUInteger)checksum forScore:(NSUInteger)score player:(GKPlayer *)player {
return (checksum == [checksumForScore:score player:player]);
}
I've used GKPlayer playerID
here to pick something that's easy for you to use, but not trivial for the device owner to change while playing. Doing it this way makes replay attack hard. I can't just copy my friend's plist entry with it's super-highscore to my plist. It's also nicely persistent when the user changes devices or restores this device. If you're not using GameKit, first, you probably should :D and second, you'll have to figure out if there's some other key you want to use. A "user handle" would be fine too if you have the user enter that at some point. Anything that's unique-enough to the user that he wouldn't just copy his friend's.
Keychain is interesting as an obfuscation technique. It's a royal pain to read even legitimately, so the official SDK provides its own obfuscation :D I doubt it's worth the trouble, but it is a decent place to "hide" small amounts of data.
NSCoding
isn't even obfuscation. Anyone who's going to bother to open up the plist can read and write NSCoding
in less than a minute. Same for turning this into an NSData
. Same for storing it in SQLite or Core Data.
@CocoaFu's suggestion of NSDataWritingFileProtectionComplete
is clever, but I doubt it will have any real impact on a jailbroken device. Once the device is unlocked, the OS will will decrypt any file for you, so it's no barrier at all to someone who knows the device's PIN.
Save the data with NSData built-in encryption:
- (BOOL)writeToFile:(NSString *)path options:(NSDataWritingOptions)mask error:(NSError **)errorPtr
with the option:
NSDataWritingFileProtectionComplete
Available in iOS 4.0 and later.
How would a user edit a plist? They would have to jailbreak to get access to the documents folder of your app. Even if I wrote an app to try to access your plist, the sandbox prevents me unless the device is jailbroken. I wouldn't worry about this for a game score and spend my time elsewhere.
If you are really concerned, you code encrypt the data. Here is a tutorial on how to do that.
I think that NSCoding is probably the easiest bet though. It formats the save file in a non-obvious way, and you would have to know what you are doing in order to change it.
Hope that helps!
Look here too:
1) Keychain services http://developer.apple.com/library/ios/documentation/Security/Conceptual/keychainServConcepts/iPhoneTasks/iPhoneTasks.html
2) or save data in sqlite db using CoreData.
精彩评论