Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the best way to save highscores on the iPhone to prevent hacking?

I am currently using NSUserDefaults, but I've heard that Jailbreakers can easily change these values and cheat on Game Center. Should I encrypt the value I store? Should I use keychain instead? Should I store the value in binary in NSUserDefualts using BOOLs (probably not)? What is the best way to store a highscore to prevent hacking, and how is it done?

like image 594
jadengeller Avatar asked Feb 22 '12 08:02

jadengeller


2 Answers

Any data that needs to accessible only via code and should be secure would fit perfectly in the KeyChain.

If it becomes more that just a little bit of data, then encrypting it and storing it in the documents directory could also do the job. But if someone really wanted to, they disassemble your app and try to locate the encryption key. Not easy but can be done.

like image 137
rckoenes Avatar answered Oct 05 '22 23:10

rckoenes


This is the code I'm using, some of them are taken from internet

I took the idea from this one: https://github.com/matthiasplappert/Secure-NSUserDefaults


how to use

in your AppDelegate.m

#import "NSUserDefaults+SecureUserDefaults.h"
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    [NSUserDefaults setSecret:@"soem secret string"]; //Cracker can still read the secret string from the binary through
    // other initialize step
}

when you want to use

[[NSUserDefaults standardUserDefaults] arrayForKey:@"key" defaultValue:nil]; // if the content is modified or not exist will return default value that passed in
[[NSUserDefaults standardUserDefaults] setSecureObject:object forKey:@"key"];
// check more method in NSUserDefaults+SecureUserDefaults.h

code here

NSData+Encryption_AES256.h

#import <Foundation/Foundation.h>

@interface NSData (Encryption_AES256)

- (NSData *)encryptedDataWithKey:(NSData *)key;

- (NSData *)decryptedDataWithKey:(NSData *)key;

@end

NSData+Encryption_AES256.m

#import "NSData+Encryption_AES256.h"
#import <CommonCrypto/CommonCryptor.h>

// Key size is 32 bytes for AES256
#define kKeySize kCCKeySizeAES256

@implementation NSData (Encryption_AES256)

- (NSData*) makeCryptedVersionWithKeyData:(const void*) keyData ofLength:(int) keyLength decrypt:(bool) decrypt
{
    // Copy the key data, padding with zeroes if needed
    char key[kKeySize];
    bzero(key, sizeof(key));
    memcpy(key, keyData, keyLength > kKeySize ? kKeySize : keyLength);

    size_t bufferSize = [self length] + kCCBlockSizeAES128;
    void* buffer = malloc(bufferSize);

    size_t dataUsed;

    CCCryptorStatus status = CCCrypt(decrypt ? kCCDecrypt : kCCEncrypt,
                                     kCCAlgorithmAES128,
                                     kCCOptionPKCS7Padding | kCCOptionECBMode,
                                     key, kKeySize,
                                     NULL,
                                     [self bytes], [self length],
                                     buffer, bufferSize,
                                     &dataUsed);

    switch(status)
    {
        case kCCSuccess:
            return [NSData dataWithBytesNoCopy:buffer length:dataUsed];
        case kCCParamError:
            NSLog(@"Error: NSDataAES256: Could not %s data: Param error", decrypt ? "decrypt" : "encrypt");
            break;
        case kCCBufferTooSmall:
            NSLog(@"Error: NSDataAES256: Could not %s data: Buffer too small", decrypt ? "decrypt" : "encrypt");
            break;
        case kCCMemoryFailure:
            NSLog(@"Error: NSDataAES256: Could not %s data: Memory failure", decrypt ? "decrypt" : "encrypt");
            break;
        case kCCAlignmentError:
            NSLog(@"Error: NSDataAES256: Could not %s data: Alignment error", decrypt ? "decrypt" : "encrypt");
            break;
        case kCCDecodeError:
            NSLog(@"Error: NSDataAES256: Could not %s data: Decode error", decrypt ? "decrypt" : "encrypt");
            break;
        case kCCUnimplemented:
            NSLog(@"Error: NSDataAES256: Could not %s data: Unimplemented", decrypt ? "decrypt" : "encrypt");
            break;
        default:
            NSLog(@"Error: NSDataAES256: Could not %s data: Unknown error", decrypt ? "decrypt" : "encrypt");
    }

    free(buffer);
    return nil;
}

- (NSData* )encryptedDataWithKey:(NSData *)key
{
    return [self makeCryptedVersionWithKeyData:[key bytes] ofLength:[key length] decrypt:NO];
}

- (NSData* )decryptedDataWithKey:(NSData *)key
{
    return [self makeCryptedVersionWithKeyData:[key bytes] ofLength:[key length] decrypt:YES];
}

@end

NSUserDefaults+SecureUserDefaults.h

//
//  NSUserDefaults+SecureUserDefaults.h
//  PocketMoneyExchanger
//
//  Created by Xiliang Chen on 12-1-17.
//  Copyright (c) 2012年 Xiliang Chen. All rights reserved.
//

#import <Foundation/Foundation.h>

@interface NSUserDefaults (SecureUserDefaults)

+ (void)setSecret:(NSString *)secret;

- (id)objectForKey:(NSString *)defaultName defaultValue:(id)value;
- (void)setSecureObject:(id)value forKey:(NSString *)defaultName;

- (NSString *)stringForKey:(NSString *)defaultName defaultValue:(NSString *)value;
- (NSArray *)arrayForKey:(NSString *)defaultName defaultValue:(NSArray *)value;
- (NSDictionary *)dictionaryForKey:(NSString *)defaultName defaultValue:(NSDictionary *)value;
- (NSData *)dataForKey:(NSString *)defaultName defaultValue:(NSData *)value;
- (NSArray *)stringArrayForKey:(NSString *)defaultName defaultValue:(NSArray *)value;
- (NSInteger)integerForKey:(NSString *)defaultName defaultValue:(NSInteger)value;
- (float)floatForKey:(NSString *)defaultName defaultValue:(float)value;
- (double)doubleForKey:(NSString *)defaultName defaultValue:(double)value;
- (BOOL)boolForKey:(NSString *)defaultName defaultValue:(BOOL)value;

- (void)setSecureInteger:(NSInteger)value forKey:(NSString *)defaultName;
- (void)setSecureFloat:(float)value forKey:(NSString *)defaultName;
- (void)setSecureDouble:(double)value forKey:(NSString *)defaultName;
- (void)setSecureBool:(BOOL)value forKey:(NSString *)defaultName;

@end

NSUserDefaults+SecureUserDefaults.m

//
//  NSUserDefaults+SecureUserDefaults.m
//  PocketMoneyExchanger
//
//  Created by Xiliang Chen on 12-1-17.
//  Copyright (c) 2012年 Xiliang Chen. All rights reserved.
//

#import "NSUserDefaults+SecureUserDefaults.h"

#import "NSData+Encryption_AES256.h"

static NSData *secretData;

@implementation NSUserDefaults (SecureUserDefaults)

+ (void)setSecret:(NSString *)secret {
    secretData = [secret dataUsingEncoding:NSUnicodeStringEncoding];
}

- (id)objectForKey:(NSString *)defaultName defaultValue:(id)value {
    id obj = [self objectForKey:defaultName];
    if ([obj isKindOfClass:[NSData class]]) {
        NSData *secureData = obj;
        NSData *data = [secureData decryptedDataWithKey:secretData];
        if (data) {
            return [NSKeyedUnarchiver unarchiveObjectWithData:data];
        }
    }
    return value;
}

- (void)setSecureObject:(id)value forKey:(NSString *)defaultName {
    if (value == nil || defaultName == nil) {
        return [self setObject:value forKey:defaultName];
    }
    NSData *tobesaved = [NSKeyedArchiver archivedDataWithRootObject:value];
    NSData *secureData = [tobesaved encryptedDataWithKey:secretData];
    //NSAssert(secureData != nil, @"fail to encrpty data");
    [self setObject:secureData forKey:defaultName];
}

- (NSString *)stringForKey:(NSString *)defaultName defaultValue:(NSString *)value {
    id obj = [self objectForKey:defaultName defaultValue:value];
    if ([obj isKindOfClass:[NSString class]]) {
        return obj;
    }
    return value;
}

- (NSArray *)arrayForKey:(NSString *)defaultName defaultValue:(NSArray *)value {
    id obj = [self objectForKey:defaultName defaultValue:value];
    if ([obj isKindOfClass:[NSArray class]]) {
        return obj;
    }
    return value;
}

- (NSDictionary *)dictionaryForKey:(NSString *)defaultName defaultValue:(NSDictionary *)value {
    id obj = [self objectForKey:defaultName defaultValue:value];
    if ([obj isKindOfClass:[NSDictionary class]]) {
        return obj;
    }
    return value;
}

- (NSData *)dataForKey:(NSString *)defaultName defaultValue:(NSData *)value {
    id obj = [self objectForKey:defaultName defaultValue:value];
    if ([obj isKindOfClass:[NSData class]]) {
        return obj;
    }
    return value;
}

- (NSArray *)stringArrayForKey:(NSString *)defaultName defaultValue:(NSArray *)value {
    id obj = [self objectForKey:defaultName defaultValue:value];
    if ([obj isKindOfClass:[NSArray class]]) {
        for (id item in obj) {
            if (![item isKindOfClass:[NSString class]]) {
                return value;
            }
        }
        return obj;
    }
    return value;
}

- (NSInteger)integerForKey:(NSString *)defaultName defaultValue:(NSInteger)value {
    id obj = [self objectForKey:defaultName defaultValue:[NSNumber numberWithInteger:value]];
    if ([obj isKindOfClass:[NSNumber class]]) {
        return [obj integerValue];
    }
    return value;
}

- (float)floatForKey:(NSString *)defaultName defaultValue:(float)value {
    id obj = [self objectForKey:defaultName defaultValue:[NSNumber numberWithFloat:value]];
    if ([obj isKindOfClass:[NSNumber class]]) {
        return [obj floatValue];
    }
    return value;
}

- (double)doubleForKey:(NSString *)defaultName defaultValue:(double)value {
    id obj = [self objectForKey:defaultName defaultValue:[NSNumber numberWithDouble:value]];
    if ([obj isKindOfClass:[NSNumber class]]) {
        return [obj doubleValue];
    }
    return value;

}

- (BOOL)boolForKey:(NSString *)defaultName defaultValue:(BOOL)value {
    id obj = [self objectForKey:defaultName defaultValue:[NSNumber numberWithBool:value]];
    if ([obj isKindOfClass:[NSNumber class]]) {
        return [obj boolValue];
    }
    return value;
}

- (void)setSecureInteger:(NSInteger)value forKey:(NSString *)defaultName {
    [self setSecureObject:[NSNumber numberWithInteger:value] forKey:defaultName];
}

- (void)setSecureFloat:(float)value forKey:(NSString *)defaultName {
    [self setSecureObject:[NSNumber numberWithFloat:value] forKey:defaultName];
}

- (void)setSecureDouble:(double)value forKey:(NSString *)defaultName {
    [self setSecureObject:[NSNumber numberWithDouble:value] forKey:defaultName];
}

- (void)setSecureBool:(BOOL)value forKey:(NSString *)defaultName {
    [self setSecureObject:[NSNumber numberWithBool:value] forKey:defaultName];
}

@end
like image 27
Bryan Chen Avatar answered Oct 06 '22 01:10

Bryan Chen