Whilst looking at Non-Renewing Subscriptions (see post here for more information) I did a lot of work with Keychain.
Storing Data Securely
When we want to store usernames and passwords, or perhaps subscription information, we want to do so securely.
Yes, we have access to NSUserDefaults, but whilst this is handy, it is a plist, and as such can be accessed relatively easily.
Apple provides us with access to Keychain via API’s, which allow us to store data securely on iOS.
Whilst Apple provide code to interface this, there are many wrappers that have been created to help with Keychain. I chose to use SSKeyChain by Sam Soffes.
Under the hood
As I got to using Keychain, I noticed some strange happenings.
Keychain keys that I knew had been set were disappearing. However, this was happening on a very occasional basis, with no apparent reason.
After placing copious amounts of logging with timestamps into the code, the issue occurred again. Thankfully I had included the timestamps, as what transpired was intriguing.
The app does run in the background to perform fetches, and thus I did not expect to see code called within application didFinishLaunchingWithOptions code running during this time. After all, when the app fires up in the background it should have called performFetchWithCompletionHandler.
Purged from Memory
However, whilst suspended, iOS may purge the app from memory if the OS experiences memory pressure. As a result, when the OS calls the app to run in the background, it will call application didFinishLaunchingWithOptions, as it has to start the app from scratch. This was causing Keychain queries to run whilst the device was locked.
By default, Keychain items will only be available whilst the device is unlocked. Makes sense! However, querying for a key whilst the device is locked will not return any data as it is protected.
If we then try to set some data, what happens is that Keychain will allow us to delete an entry, but the new entry cannot be created, again as we don’t have permissions under the default setup.
SSKeyChain will, if a key exists, delete that key before writing a key. This removed the need to look at matching, and then update keys, as to update a key requires knowledge of the existing data stored as well as the new data.
It turns out that when you request a key from keychain, whilst you may receive no data, the data could possibly be there, but be inaccessible at that time. If we capture the NSError, we will see an error code -25308 errSecInteractionNotAllowed when the device is locked.
If we want to check if a key exists and only write if it actually does not exist, it is worth reading the key and looking for error code -25300 errSecItemNotFound. If we receive this error it is safe to assume that the key does not exist, and therefore we can create it without issue.
As I mentioned, keychain items, by default, are only available when the device is unlocked.
There are however a number of options which Apple give us with kSecAttrAccessible to manage the security:-
- kSecAttrAccessibleWhenUnlocked (Default)
The difference between the first three and the ThisDeviceOnly options, is that the first three will be backed up and can be restored to another device, however those with ThisDeviceOnly will not be backed up and cannot be restored to another device.
I am not advocating wide open security here, selecting kSecAttrAccessibleAlways offers minimal security, and clearly the best position is to be in the strongest security position.
However, if you need access from the background, selecting kSecAttrAccessibleafterFirtstUnlock would allow this, assuming the user had unlocked the phone one after reboot.
Of course, if you can write your code so that it doesn’t need access to the Keychain from the background, so much the better.
In the meantime, why not subscribe for updates (see subscribe option at the top right of this page!).