If using NSUserDefaults
is unsafe, using the keychain is unsafe too, so I would use the NSUserDefaults
, because it is easier to implement. Let me explain: If the user can just edit the user defaults and get access to the data, this means that the data is unencrypted. (Or at least, it's encrypted in a way that is easy to break if you have access to the source code of the app.) If the data is unencrypted and you use the keychain to store the boolean, the user can as well just patch the binary to omit the keychain-check. Or he may just find the unencrypted payload in the application binary and inspect it.
But now for something more constructive... :) The best way to implement Touch ID protection, is to use Touch ID to unlock a secret key, and encrypting the payload data with that secret key. If Touch ID is disabled, you just use a hardcoded key instead (and use some kind of basic runtime obfuscation, like base64_decode to make it not too easy for attackers.) You can store a boolean in the user defaults (e.g. "data_encryption_enabled")*. If it is YES
, you ask the system for the secret key for the "data_access" item. You then decrypt the data with that key (I suggest to use AES128). If "data_encryption_enabled" is NO
, you use "9fajw9e8" as the secret key instead. It's easy to see, that if an attacker manages to change "data_encryption_enabled" to NO
, this will not unlock the data.
A few more things to consider:
- When the payload is saved in a way that it can be backed up to iCloud, you should test if restoring the app from the backup to a different device makes the data unusable. If the data is stored only locally (e.g. either in "Caches", or in "Documents" with NSURLIsExcludedFromBackupKey set to
YES
/true
) this is not an issue.
- The algorithm that generates the secret key should be secure. (E.g. use
SecRandomCopyBytes
)
*) all strings in this example are made up by me, you can choose them however you like.