Dec 26, 2018 by Florin
The Missing Step When Using Face ID or Touch ID in Your iOS App
A Crucial Tip for App Developers
Nowadays smartphones are at the center of all our activities: phone calls, messages, maps and navigation, calendar meetings, etc. Having all this personal information about us on one tiny device, it’s crucial to restrict outside access as much as possible the access.
Starting with iPhone 5s, in 2013, Apple made an important step toward helping its users keep their phone protected by introducing Touch ID. Touch ID relies on a sensor that uses capacitive touch to detect a user’s fingerprint, which passes a small current through one’s finger to create a “fingerprint map.” A user could add up to five fingerprints
However, Touch ID was not perfect and had its limitations. So in 2017, with the launch of iPhone X, Apple introduced a new form of biometric authentication: Face ID. Rather than using a fingerprint map, the user’s face is used instead. Face ID uses infrared light to project thousands of invisible dots over the entire face to create a map.
As a developer, sometimes you need to provide an extra security layer for your app. For example for the banking apps, you do not want your kid to enter the app and play with it. That’s why it is important that these kinds of apps and others have some type of authentication form.
I personally haven’t worked a lot with Touch ID and Face ID APIs until recently. A project I was working on, although it had a 6 digit passcode for authentication, needed to support Touch ID and Face ID too in order to improve customer experience. It sounds simple and straightforward enough; there are a lot of articles out there, besides the official documentation. So I started researching.
The first thing that should be mentioned when this type of request comes in from the clients, is to inform them that using Touch ID/Face ID will not guarantee that only one user can access the secure data. To give more context here let’s say we have user A that installed the app, activated the secure data by setting up a passcode, and confirmed using the Touch ID for future logins. But when setting Touch ID on his personal device user A also added a fingerprint for his family member, user B. This is a common practice for parents to give their children access to the phone. And in these cases, using Touch ID will grant access to the secure data to all the users that have set a fingerprint on that phone, and there is no way to differentiate between them. It’s a similar case for Face ID. This is a known risk and you need to inform the client about it. For some applications, this can be a deal breaker, and the clients might choose not to use Touch ID or Face ID at all. As for the rest of the clients that will choose to use Touch ID and Face ID despite this, they need to know so they can include it in the risk management and in the disclaimers and/or license agreements for the app.
In the context of the project I was working on, the client understood the risks and accepted them. As I started to research I came across different articles:
- How To Secure iOS User Data: The Keychain and Biometrics – Face ID or Touch ID
- Touch ID iOS Tutorial
- How to use Touch ID for a quicker, easier login to your app
- How to add FaceID/TouchID using Swift 4
- Logging a User into Your App with Face ID or Touch ID
After reading these resources and the official documentation from Apple, and getting a sense on how to implement it, I started coding. Checking for errors, adding fallback to the old passcode screen, checking for Touch ID vs. Face ID and updating the messages and icons to match the device capabilities. Everything was working great, the implementation passed the code review and received the thumbs up from the QA team. So it was merged and ready to go in production. There was only one more step left: it had to pass Penetration Testing – also known as Pen Test. The results came and you may guess the output: it failed Pen Testing ?. You might be asking why that happened. It was not because of the reason I already mentioned, with multiple users having their fingerprints set on the same phone. It was because if an attacker gained access to the phone and knew the device passcode, they could add their own fingerprint from Settings, then use it to activate secure mode without the need to enter the application’s password or passcode. So, knowing the phone’s passcode will bypass our application’s custom passcode.
This is when I realized that all the tutorials and articles for adding Touch ID/Face ID that I read are missing one important step: detecting if the Touch ID/Face ID configurations have changed since the user’s last authentication.
Ok, back to the drawing board. But how can we check if the Touch ID/Face ID configuration had changed? I had to do some digging but eventually, I found the missing piece: evaluatedPolicyDomainState. The official documentation states that:
This property returns a value only when the canEvaluatePolicy(_:error:) method succeeds for a biometric policy or the evaluatePolicy(_:localizedReason:reply:) method is called and a successful biometric authentication is performed. Otherwise, nil is returned.
The returned data is an opaque structure. It can be used to compare with other values returned by this property to determine whether the authorized database has been updated. However, the nature of the change cannot be determined from this data.
Based on this description it looks like everything we need to do in order to secure the app from this kind of attack is to check this property whenever a user authenticates with the application’s passcode and saves a copy of it. Then, whenever Touch ID/Face ID is used to authenticate, we can get the current state and compare it to the old one. If they’re different, we don’t let the Touch ID/Face ID authenticate and instead require normal authentication; in this case, the user will have to enter the application’s passcode. As a side effect, this will lock out all users from Touch ID until the passcode is re-entered, even the ones who were already enrolled, before the attack. This is because Touch ID/Face ID doesn’t allow us to identify specific fingerprints/faces, so there is no way around it.
After I added this extra step in the authentication logic the Passcode screen was finally secure enough to pass Pen Testing and it was ready for production.
If you are like me and you understand more from a piece of code than from a bunch of words, I will give you a basic example of adding Touch ID/Face ID to your code as suggested by any tutorial out there and also the final version that will also test for changes to the enrollment of Touch ID/Face ID since the last user authentication.
So if the initial code looked something like this:
The new code with increased security will look like this:
I hope you found this information useful, and good luck with your projects!