移动认证
by Kathy Dinh
凯西·丁(Kathy Dinh)
如何在移动设备上实施安全的生物特征认证 (How to implement secure Biometric Authentication on mobile devices)
A quick search for React Native biometric authentication would give you several tutorials. That was the first thing I did when there was a need for such a feature in one of my projects. Depending on the level of risk acceptable for your app, one of those solutions might be suitable. For a high risk app like ours, it would not pass security testing.
快速搜索React Native生物特征认证将为您提供一些教程。 当我的一个项目中需要这种功能时,这是我要做的第一件事。 根据您的应用程序可接受的风险级别,这些解决方案之一可能是合适的。 对于像我们这样的高风险应用程序,它不会通过安全测试。
If you want to add a secure biometric authentication to aa React Native iOS app, you are in the right place.
如果您想向React Native iOS应用添加安全的生物特征认证,那么您来对地方了。
react-native-touchid有什么问题? (What is wrong with react-native-touchid?)
Most implementations use the react-native-touchid package. At line 65 in the TouchID.m file, the authenticate method calls the following LAContext method when attempting TouchID/FaceID authentication:
大多数实现使用react-native-touchid包。 在TouchID.m文件的第65行 ,尝试进行TouchID / FaceID身份验证时, authenticate方法将调用以下LAContext方法:
evaluatePolicy:localizedReason:reply:^(BOOL success, NSError *error)
The method relies on a Local Authentication check if the provided fingerprint matches the one enrolled on the device. When the check passes, a success boolean is returned and the user has authenticated successfully with TouchID/FaceID.
如果提供的指纹与设备上注册的指纹匹配,则该方法依赖于本地身份验证检查。 通过检查后,将返回成功布尔值,并且用户已使用TouchID / FaceID成功认证。
There have been reports of the possibility to bypass Local Authentication by sending a success signal to Apple’s APIs on jailbroken or non-jailbroken iOS devices. Thus, biometric authentication via Local Authentication is vulnerable to spoofing by an attacker who could interfere with the check at runtime.
有报道称,有可能通过在越狱或未越狱的iOS设备上向Apple的API发送成功信号来绕过本地身份验证 。 因此,通过本地身份验证进行的生物特征身份验证很容易受到攻击者的欺骗,攻击者可能会在运行时干扰检查。
实施生物特征认证的安全方法是什么? (What is the secure way to implement biometric authentication?)
To implement biometric authentication in an iOS app, there are two ways — either through Apple’s Local Authentication APIs or through access control of Keychain Services natively provided by the underlying system.
要在iOS应用中实现生物特征认证,有两种方法-通过Apple的本地认证API或通过底层系统本地提供的钥匙串服务的访问控制 。
Authenticating with Local Authentication is simpler but generally not recommended for critical applications. As described in the previous section, Local Authentication is a high level API whose behaviour can be overridden, i.e. an attacker could fake a successful authentication by changing the API’s response.
使用本地身份验证进行身份验证更简单,但通常不建议用于关键应用程序。 如上一节所述,本地身份验证是一种高级API,其行为可以被覆盖,即攻击者可以通过更改API的响应来伪造成功的身份验证。
It is acknowledgedly best practice to use Keychain Services for implementing biometric authentication in high risk apps. Keychain Services enforces access control on its stored content using functionality provided by iOS and Secure Enclave. The process executes at the hardware and operating system layer and thus minimises exposure to the less trustworthy application layer. Security risks do exist when user is on a jailbroken or malware-infected device, but these threats can be mitigated by mobile device management (MDM) technology.
公认的最佳实践是使用钥匙串服务在高风险应用程序中实施生物识别。 钥匙串服务使用iOS和Secure Enclave提供的功能对存储的内容实施访问控制。 该过程在硬件和操作系统层执行,因此将对可信度较低的应用程序层的影响降至最低。 当用户处于越狱或感染了恶意软件的设备上时,确实存在安全风险,但是可以通过移动设备管理(MDM)技术减轻这些威胁。
我们如何通过钥匙串服务实施生物特征认证? (How do we implement biometric authentication with Keychain Services?)
In order to access Keychain Services in our React Native app, we are going to use the package react-native-keychain. The example code is in TypeScript, which should easily be converted to JavaScript.
为了在React Native应用程序中访问Keychain Services,我们将使用package react-native-keychain 。 示例代码在TypeScript中,应该可以轻松转换为JavaScript。
First, install react-native-keychain and its type declaration as your project dependency:
首先, 安装 react-native-keychain及其类型声明作为您的项目依赖项:
npm i -S react-native-keychain
npm i -S @types/react-native-keychain
Next, we have to link the library as it depends on the native component. There are two ways of linking libraries in a React Native app: automatic linking and manual linking. I encountered many errors with CocoaPods while performing automatic linking. Manual linking works but involves many steps.
接下来,我们必须链接库,因为它依赖于本机组件。 在React Native应用程序中有两种链接库的方式:自动链接和手动链接。 在执行自动链接时,CocoaPods遇到许多错误。 手动链接有效,但涉及许多步骤。
I discovered that the library is linked properly without errors if you run react-native link after temporarily removing Podfile under the iOS folder. To save you the hassle, let’s follow such a hybrid approach. Assuming your code is under version control so that it is possible to safely revert any changes, delete your Podfile, then run the linking command:
我发现如果临时删除iOS文件夹下的Podfile后运行react-native链接 ,则该库已正确链接,没有错误。 为了节省您的麻烦,让我们遵循这种混合方法。 假设您的代码受版本控制 ,因此可以安全地还原所有更改,删除Podfile, 然后运行链接命令:
react-native link react-native-keychain
Now, undo your Podfile deletion. For iOS 10 you’ll need to enable the Keychain Sharing
entitlement in the Capabilities
section of your build target.
现在,撤消您的Podfile删除。 对于iOS 10,您需要在构建目标的“ Capabilities
部分中启用“ Keychain Sharing
权利。
Add the following key value pair to Info.plist:
将以下键值对添加到Info.plist :
<key>NSFaceIDUsageDescription</key><string>Enabling Face ID allows you quick and secure access to your account.</string>
Then rebuild your project with:
然后使用以下命令重建项目:
react-native run-ios
In case you encounter difficulty in installing react-native-keychain, refer to this GitHub README.
如果您在安装react-native-keychain时遇到困难,请参阅此GitHub README 。
Before asking the user to authenticate with TouchID/FaceID, it is wise to check if the user’s iOS device supports such capability by calling getSupportedBiometryType
:
在要求用户使用TouchID / FaceID进行身份验证之前,明智的做法是通过调用getSupportedBiometryType
来检查用户的iOS设备是否支持这种功能:
After confirming that biometric authentication is supported, you need to save some content in the keychain and set access control flags. The content could be user credentials or some access token. Keychain entry is encrypted and stored in secure storage. To store a value in the keychain, call setGenericPassword
:
确认支持生物特征认证后,您需要在钥匙串中保存一些内容并设置访问控制标志。 内容可以是用户凭据或某些访问令牌。 钥匙串条目已加密并存储在安全存储中。 要将值存储在钥匙串中,请调用setGenericPassword
:
A few points to note here:
这里要注意几点:
Setting accessControl to any of these
Keychain.ACCESS_CONTROL
enum valuesBIOMETRY_ANY
,BIOMETRY_CURRENT_SET
,BIOMETRY_ANY_OR_DEVICE_PASSCODE
,BIOMETRY_CURRENT_SET_OR_DEVICE_PASSCODE
mandates that the user authenticate with TouchID/FaceID whenever we attempt to retrieve the keychain item.将accessControl设置为这些
Keychain.ACCESS_CONTROL
枚举值BIOMETRY_ANY
,BIOMETRY_CURRENT_SET
,BIOMETRY_ANY_OR_DEVICE_PASSCODE
,BIOMETRY_CURRENT_SET_OR_DEVICE_PASSCODE
强制要求用户在每次尝试检索钥匙串项目时使用TouchID / FaceID进行身份验证。We also set accessible to
Keychain.ACCESSIBLE
enum valueWHEN_PASSCODE_SET_THIS_DEVICE_ONLY
. This is the strictest accessible constraint, which enforces:我们还将
Keychain.ACCESSIBLE
枚举值WHEN_PASSCODE_SET_THIS_DEVICE_ONLY
设置为可访问 。 这是最严格的可访问约束,它强制执行:
Your device must be unlocked for the secret to be accessible.
Your device must have a device passcode set.
If you turn off your device passcode, the secret is deleted.
The secret cannot be restored to a different device.
The secret is not included in iCloud backups.
Finally, we trigger the TouchID/FaceID authentication prompt by attempting to access the previously stored keychain value with getGenericPassword
:
最后,我们尝试通过使用getGenericPassword
访问先前存储的钥匙串值来触发TouchID / FaceID身份验证提示:
Since we saved our secret with access control previously, accessing the item requires user to pass biometric authentication. When authentication is successful, the result returns an object, whose username is ‘your-secret-name’, password is ‘your-secret-value’, and service is ‘your-service-name’.
由于我们之前通过访问控制保存了我们的秘密,因此访问该项需要用户通过生物特征认证。 身份验证成功后,结果将返回一个对象,其用户名为“您的秘密名称”, 密码为“您的秘密值”, 服务为“您的服务名称”。
After 5 failed attempts of TouchID/FaceID authentication system-wide, biometric authentication is turned off everywhere on the device. The user must lock and unlock the device with a passcode to re-enable TouchID/FaceID. That’s why at line 14 we have to check for supported biometry type and handle the case appropriately, e.g. asking the user to login with their username/password.
在系统范围内进行5次TouchID / FaceID身份验证失败后,生物识别身份验证在设备上的所有位置均被关闭。 用户必须使用密码锁定和解锁设备才能重新启用TouchID / FaceID。 这就是为什么在第14行,我们必须检查受支持的生物特征类型并适当地处理这种情况,例如,要求用户使用其用户名/密码登录。
注意事项 (Caveats)
Though biometric authentication with react-native-keychain is suitable for critical applications, there are a few caveats I would like to bring to your attention:
尽管使用react-native-keychain进行生物特征认证适用于关键应用,但我还是要提醒您一些注意事项:
There is no passcode fallback. You may receive a requirement to allow the user to authenticate with their device passcode. Looking at the package’s README, you should find Keychain.ACCESS_CONTROL
enum keys DEVICE_PASSCODE
, BIOMETRY_ANY_OR_DEVICE_PASSCODE
, BIOMETRY_CURRENT_SET_OR_DEVICE_PASSCODE
.
没有密码回退 。 您可能会收到允许用户使用其设备密码进行身份验证的要求。 查看软件包的自述文件,您应该找到Keychain.ACCESS_CONTROL
枚举键DEVICE_PASSCODE
, BIOMETRY_ANY_OR_DEVICE_PASSCODE
, BIOMETRY_CURRENT_SET_OR_DEVICE_PASSCODE
。
Unfortunately, setting an access control value when calling setGenericPassword
to any of those three enum keys do not enable an “Enter Password” fallback. The issue has been reported on GitHub, but there has been no response at the time of publishing this article.
不幸的是,在调用setGenericPassword
到这三个枚举键中的任何一个时设置访问控制值不会启用“ Enter Password”回退。 该问题已在GitHub上进行了报道 ,但在发布本文时尚未得到任何回应。
You may think of implementing passcode fallback with a different library. Be aware that your system is only as secure as your weakest link. If your passcode fallback implementation executes at the application layer, it is a potential target for a security attack and defeats the purpose of relying on Keychain Services for biometric authentication.
您可能会想到使用其他库来实现密码回退。 请注意,您的系统仅与最弱的链接一样安全 。 如果您的密码后备实现在应用程序层执行,则它可能成为安全攻击的目标,并且无法实现依靠钥匙串服务进行生物识别的目的。
Also, authenticating with react-native-keychain on Android devices may not be considered secure as there is no equivalence of Keychain Services in Android.
此外,由于Android中没有等效的钥匙串服务,因此在Android设备上使用react-native-keychain进行身份验证可能并不安全 。
结语 (Wrapping up)
Thank you for reading this far. I hope you’ve found the tutorial useful. One improvement you may want to make is asking the user whether they would like to opt-in for biometric authentication before enabling it in your app. Also, you may add a setting to let the user turn on or off TouchID/FaceID authentication in your app settings page.
感谢您阅读本文。 我希望您发现本教程对您有所帮助。 您可能要做的一项改进是询问用户在应用程序中启用生物识别身份验证之前,是否愿意加入。 另外,您可以在应用程序设置页面中添加一个设置,以允许用户打开或关闭TouchID / FaceID身份验证。
参考文献 (References)
移动认证