It seems like there has been a lot in the “developer” news lately about the secure storage of user data in iOS apps, as well as related topics around securely replacing the venerable but now deprecated device unique identifier, or UDID. For the latter, more than a few replacement schemes have been offered up, most with a “secure” storage element. This all got me thinking about other reasons one might want to store something securely in an app, and how to do it.
There is a good chance that you, like me, and like many developers, have stored user configuration or settings data in NSUserDefaults. Why not, right? It’s easy to access and oh so convenient. But has that saved data contained usernames? Passwords? Or worse? That’s too bad, because NSUserDefaults is completely insecure! On a per-app basis, it is saved as a property list, in the clear (meaning: you can read it without any kind of decoding or password) in the app’s /Library/Preferences directory. On a jailbroken device, anyone can read any app’s sandboxed data. You can even extract it from an iPhone backup. Oops.
What to do then? The “obvious” solution is to store it securely, and presumably not in the app’s sandbox. You could write your own encryption algorithm, or use a third-party library, or use the SDK’s Crypto APIs. But all of those solutions require writing (and understanding) some fairly arcane APIs, at best.
There is a simpler way, and that is to use the built-in iOS Keychain. The Keychain also has a set of APIs available. While they are too a little arcane, using them is pretty easy once you understand how the keychain works.
What kind of data would be good to store in the keychain?
- Usernames & Passwords
- In-App purchase flags for purchased content or enabled features
- Anything else that is not too large that could be considered sensitive user data or sensitive to your app
I cannot stress enough that the Keychain is NOT the place to store large dictionaries or large blobs of binary data. It is however, perfect for things like usernames or passwords. And it is also useful for other small bits of data such as in-app purchase upgrade state.
The latter is a good example of something to protect. Imagine that you were storing upgrade state as well as available upgrades in NSUserDefaults and somehow that got hacked. Now your potential revenue stream from selling those upgrades is in jeopardy because instead of paying, customers could potentially just set the right values in the right NSUserDefaults keys, and they have bypassed your whole in-app store: Profits lost.
Storing all these kind of data in the keychain has another advantage. If your app is deleted, and then re-installed, the keychain values set previously remain! This is because the keychain for an app is not stored with the app; the system manages the keychains for all apps, elsewhere…
One caveat about storing data in the keychain though. You’ll want to make sure your keys are namespaced. Using your app’s bundle id is a good namespace to choose. So rather than using a key like “MySecretKey”, use instead something like “com.mycompany.myapp.MySecretKey”.
You may be wondering where all this is leading… I had reason recently do deal with this issue of saving sensitive data securely. So I wrote a utility class that will save or retrieve an NSString, NSArray, or NSSet using simple methods. The project is called Lockbox and can be found on github. The repo includes the 2 source files, some documentation, and a sample Xcode project.
Supporting additional data types would be easy, of course. I specifically did not implement support for dictionaries because, to my mind, a dictionary is likely going to be larger than you really want to store in the keychain. The class lets you specify bare keys like “MyKey” for convenience but, behind the scenes, prefixes them with your app’s bundle id.
So run, don’t walk, and secure your sensitive data! What other kinds of sensitive data have you stored (or will you now store) in the keychain?