Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Security implications of storing a password in Settings.bundle and getting with CFPreferencesCopyAppValue

Apologies for the seeming obviousness of this question, but for whatever reason I haven't been able to find a definitive answer in the Apple documentation about where and how Settings.bundle password info is stored. My question: if I need to store some credentials for an app, and I use a Settings.bundle so that the password is entered into a PSTextFieldSpecifier textfield in Apple's Settings area with IsSecure = YES, and then I access the value from my app using CFPreferencesCopyAppValue, never writing it out to NSUserDefaults and only sending it over the network securely, how secure is that storage and retrieval method when compared to storing and retrieving the password using the keychain in my own app settings? Thanks for your input.

like image 266
Halle Avatar asked Aug 08 '09 11:08

Halle


3 Answers

CFPreferencesCopyAppValue is just the Core Foundation way of accessing the same information you get when using NSUserDefaults. In terms of security, the features are exactly the same. That is, it's not encrypted. It's secure only in the sense that it's obscured. The "correct" answer is to use the keychain.

The counter to that is that many applications use NSUserDefaults to store passwords. You could argue that unless the password controls access to information of any value then it's not worth the effort in trying to use the keychain. Which brings me to the second argument in favour of using a secure field in the Settings application: the keychain API is hideous and, in my experience at least, writing error-free code is tricky.

like image 87
Stephen Darlington Avatar answered Nov 15 '22 23:11

Stephen Darlington


Don't save a user's password in the settings bundle.
It isn't secure.

Remember, you don't need to know what the original password is, you need to know if the password the user enters matches the original password. The correct way to deal with passwords in iOS is to either

  • Use the keychain, as others have mentioned
  • Generate a cryptographic one-way hash function using SHA-512 or other encryption and store the resulting hash and salt in NSUserDefaults

Of these options, encrypting the password and storing the hash+salt is by far the easiest. Here's what you do to store the password:

  1. Grab the password from the user
  2. Create a random salt value
  3. Create a forward-only hash using SHA-512 and the random salt value
  4. Store the resulting hash and salt value in NSUserDefaults -- these values can't be used by hackers to determine the original password, so there is no need to store them in a secure place.

Now, when the user enters their password and you have to verify if it's correct, here's what you do:

  1. Grab the password from the user
  2. Grab the previously saved hash + salt value from NSUserDefaults
  3. Create a forward-only hash using the same one-way hash function that you used to encrypt the original password -- passing it the attempted password and the salt value from NSUserDefaults
  4. Compare the resulting hash with the one that was stored in NSUSerDefaults. If they are the same, then the user entered the correct password.

Here's the code to generate the salt and the forward-only hash:

NSString *FZARandomSalt(void) {
    uint8_t bytes[16] = {0};
    int status = SecRandomCopyBytes(kSecRandomDefault, 16, bytes);
    if (status == -1) {
        NSLog(@"Error using randomization services: %s", strerror(errno));
        return nil;
    }
    NSString *salt = [NSString stringWithFormat: @"%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x",
                      bytes[0],  bytes[1],  bytes[2],  bytes[3],
                      bytes[4],  bytes[5],  bytes[6],  bytes[7],
                      bytes[8],  bytes[9],  bytes[10], bytes[11],
                      bytes[12], bytes[13], bytes[14], bytes[15]];
    return salt;
}

NSData *FZAHashPassword(NSString *password, NSString *salt) {
    NSCParameterAssert([salt length] >= 32);
    uint8_t hashBuffer[64] = {0};
    NSString *saltedPassword = [[salt substringToIndex: 32] stringByAppendingString: password];
    const char *passwordBytes = [saltedPassword cStringUsingEncoding: NSUTF8StringEncoding];
    NSUInteger length = [saltedPassword lengthOfBytesUsingEncoding: NSUTF8StringEncoding];
    CC_SHA512(passwordBytes, length, hashBuffer);
    for (NSInteger i = 0; i < 4999; i++) {
        CC_SHA512(hashBuffer, 64, hashBuffer);
    }
    return [NSData dataWithBytes: hashBuffer length: 64];
}

Code for this example was found here: http://blog.securemacprogramming.com/2011/04/storing-and-testing-credentials-cocoa-touch-edition/

like image 34
memmons Avatar answered Nov 15 '22 23:11

memmons


Keychain on the iPhone is going to be the most secure, unless you're using custom encryption, which is very difficult to do (and export). NSUserDefaults isn't considered secure.

like image 21
Jordan Avatar answered Nov 15 '22 21:11

Jordan