Kodeco Forums

How To Secure iOS User Data: The Keychain and Touch ID

In this tutorial, you'll be using the Keychain to store and verify login information and explore using Touch ID in your app.


This is a companion discussion topic for the original entry at https://www.raywenderlich.com/618-how-to-secure-ios-user-data-the-keychain-and-touch-id

If the device is ever compromised (jailbroken) it’s possible to then install a program to dump the items in the keychain, making passwords and data stored in the keychain visible.

If you’re using the keychain to store important credentials (e.g. bank account passwords, etc.) you may want to encrypt the data you’re putting into the keychain before its being stored in the keychain. Then a keychain dump only exposes an encrypted string and not the actual data.

Storing unencrypted data into the keychain can be flagged as a PEN test issue and security risk.

true
if you need a o-device custom encryption, you can use mine
it was written to become a tutorial on this site
but after I got rejected I finished it and uploaded it to GitHub

you can change/shuffle all arrays to make your encryption unreadable to hackers
and you can encrypt and decrypt with one line of code

here are 2 images of my encryption in action

You can test Touch ID using the simulator, but you need to enabled in hardware > touch id (it comes disable by default)

2 Likes

Hi, thanks to your great tutorial first.
I am confused at a point…why we removed [weak self] in the touchIDLoginAction(_ sender: UIButton) - line #136
As I am concerned, I think it would cause retain cycle in a following way:
LoginViewController Instance => touchMe => authenticateUser closure => LoginViewController Instance
// MARK: ‘=>’ stands for owning

Maybe I make mistakes in comprehension?Hoping your warm-hearted answer.

Thanks for great tutorial @timmitra. While going through it, I noticed a potential flaw in your code snippet. You forgot to dispatch the error completion with message back onto the main queue:

func authenticateUser(completion: @escaping (String?) → Void) {

guard canEvaluatePolicy() else {
  completion("Touch ID not available")
  return
}

context.evaluatePolicy(.deviceOwnerAuthenticationWithBiometrics,
  localizedReason: "Logging in with Touch ID") { (success, evaluateError) in
    if success {
      DispatchQueue.main.async {
        completion(nil)
      }
    } else {
                          
      let message: String
                          
      switch evaluateError {
      case LAError.authenticationFailed?:
        message = "There was a problem verifying your identity."
      case LAError.userCancel?:
        message = "You pressed cancel."
      case LAError.userFallback?:
        message = "You pressed password."
      default:
        message = "Touch ID may not be configured"
      }

      DispatchQueue.main.async {          
         completion(message)
      }
    }
}

}

New to the keychain. How would you go about having the user update there password.

I theory, yes. The keychain is shared by iOS itself in iCloud. I haven’t tried it myself.

Thanks. We’ll take a look

Thanks for the tutorial, but I have a couple observations.

  1. There seems to be no association between the user’s biometric identity and the password in the keychain.
  2. The password is not deleted from the keychain when the user logs out.

Am I missing something?

@timmitra Do you have any feedback regarding this? Thank you - much appreciated! :]

Hey nemanzone,
thanks for your question.

That is correct. There is no direct connection between the biometric data and the specific application data stored in the Keychain. Imagine that the Keychain is like a storage room, that is unlocked with your Touch ID info (as the key). Inside the room is a self where you have stored your app’s password and the associated username.

AFAIK the Touch ID access lets you open the Keychain so that you can access the whole store. In this case, you are associating the stored password against the App tokens in the code. More specifically the KeychainConfiguration.serviceName and the KeychainConfiguration.accessGroup.

let passwordItem = KeychainPasswordItem(service: KeychainConfiguration.serviceName,
                                          account: username,
                                          accessGroup: KeychainConfiguration.accessGroup)
  let keychainPassword = try passwordItem.readPassword()
  return password == keychainPassword

You’re also correct that the password is not deleted when the user logs out. You would have to write some code to delete the password on log out, if you wanted that behavior. The password does remain in the Keychain store unless you delete the app AFAIK. What we are doing is we are retrieving the stored password by authenticating with the keys above; username, accessGroup and serviceName.

Does that help?

This does make sense. The association is enforced by how you write the application.

There are some subtleties to consider. For example, what happens if the user changes her password on the web site? In the end, every application will have its own challenges.

Also, the keychain store is typically not deleted when you delete the app (hence my question).

Thanks for your reply.

That seems to be the case. The application specific data stored in the Keychain ins’t being removed, and some people had posted solutions on Stack Overflow to check for this case. It seems that Apple was going to make a change and delete the application specific data as on iOS 10.3 - https://forums.developer.apple.com/message/210531#210531
However it seems that Apple is not removing the data in the official iOS 10.3+ release

This is way beyond the scope of this tutorial, so you’ll have to investigate on your own. I am happy to chat with you about this.

With respect to user’s changing their passwords outside of the app, this is a scenario you will have to consider. I have come across this situation on some of the products I have been involved in. If it is possible to change a password outside of the app, the password in the iOS Keychain will no longer be correct and you have to sort out what to do in that case. Perhaps you can fail gracefully and ask the user to reenter the credential(s).

Hi @timmitra,
thanks for the tutorial. It’s very useful.
I got 2 questions though:
1 - Why com.apple.syncservices.Syncable is set to NO?
2 - Why codegen is disabled for Note entity?

I suspect it’s either due to old codebase you been updating every year,
or because it has some security vulnerabilities.

Would like to learn about this. Thanks

Good point. I’m coming from Android world and there even if you encrypt something, the key to decrypt has to be stored somewhere in code typically. Which means it can be discovered by reverse engineering the app. Does this also apply to iOS apps?

This tutorial is more than six months old so questions are no longer supported at the moment for it. We will update it as soon as possible. Thank you! :]