了解钥匙串项的访问控制设置“.userPresence”

Understanding the access control setting `.userPresence` for keychain items

上下文

我正在开发一个 iOS 应用程序,它应该受到本地身份验证的保护。用户必须在打开应用程序后使用 fingerprint/password/... 解锁应用程序。当应用程序在前台 运行 时,该应用程序应保持解锁状态。当应用程序解锁时,应用程序必须访问钥匙串中的多个项目(当应用程序 运行 时,某些项目可能会被多次访问)。

使用 .userPresence 的钥匙串访问控制

将钥匙串项目的访问控制设置为 .userPresence 会在访问项目之前强制执行本地身份验证。但是,这并不真正适合我的用例,因为每次访问钥匙串中的项目时,用户都必须进行身份验证。

// storing keychain items with .userPresence access control
guard let accessControl = SecAccessControlCreateWithFlags(
    nil,
    kSecAttrAccessibleWhenUnlockedThisDeviceOnly,
    .userPresence,
    nil
) else {
    throw CommonError("Unable to create access control flags")
}

let query = [
    kSecClass: kSecClassGenericPassword,
    kSecAttrAccount: account,
    kSecAttrAccessControl: accessControl,
    kSecValueData: ...
] as [String: Any]

SecItemAdd(query as CFDictionary, nil)

为了适合我的用例,我可以自己实现本地身份验证并使用 kSecAttrAccessible: kSecAttrAccessibleWhenUnlockedThisDeviceOnly(而不是 .userPresence)存储钥匙串项。我担心,在没有 .userPresence 的情况下存储钥匙串项目可能会导致安全风险。如果我自己实现本地身份验证并使用 kSecAttrAccessible: kSecAttrAccessibleWhenUnlockedThisDeviceOnly 访问钥匙串项而不是使用 .userPresence 的访问控制,这会有什么不同吗?

// storing keychain items without access control
let query = [
    kSecClass: kSecClassGenericPassword,
    kSecAttrAccount: account,
    kSecAttrAccessible: kSecAttrAccessibleWhenUnlockedThisDeviceOnly,
    kSecValueData: ...
] as [String: Any]

SecItemAdd(query as CFDictionary, nil)

我找不到任何文章或官方来源来解释我的担忧。如果可以 link 任何官方文档,或者甚至可以为我的用例提供更好的解决方案,我将不胜感激。

TL;DR

将钥匙串项的访问控制设置为 .userPresence 是否以任何方式提高安全性,或者如果我自己实现本地身份验证并使用 [=13 存储钥匙串项是否没有任何区别=]?

感谢您的帮助!

在越狱设备上,或者至少在非越狱设备上有足够的权限提升,攻击者可以伪装成您的应用程序,以访问您的钥匙串项目。 .userPresence 在某种意义上提高了安全性,即使在攻击者伪装成您的应用程序的情况下,钥匙串也会对您的钥匙串项目的使用强制执行本地身份验证,即,当攻击者尝试使用您的钥匙串项目,本地身份验证将启动。 关于这种攻击如何可能的线索,可以在越狱分析中找到,例如 this talk.

如果您自己实现了本地身份验证,比如说,在您的应用程序中,那么攻击者甚至不需要 运行 您的应用程序(在刚刚描述的场景中)就可以完全绕过它。就钥匙串而言,当攻击者请求访问您的钥匙串项目时,您的应用程序 正在请求访问您的钥匙串项目。

除此之外,攻击者可能会通过其他方式尝试绕过您对本地身份验证的实施,例如,使用 frida 等工具篡改您已实施的逻辑。据我所知,当您使用 .userPresence 时,很难篡改本地身份验证逻辑,因为这将由 iOS 而不是您的应用程序逻辑控制。

现在,即使 .userPresence 强制执行本地身份验证,攻击者仍可能会在用户尝试访问您的钥匙串项时尝试诱骗用户在此时进行本地身份验证,但这是另一个讨论。至少,使用 .userPresence,与您自己的实现相比,阻止本地身份验证的发生应该更难。

好的,回到你可能考虑在你的用例中做什么。两种可能性可能是:

  1. 您可以在 GitHub 等网站上找到各种移动应用程序保护技术,例如检测您的应用程序是否 运行ning 在越狱设备上,以及可能性检测对您的应用程序进行其他类型的篡改(也有此类功能的商业产品,甚至是安全存储加密材料的替代方法)。因此,如果您进行风险评估并认为此类技术可以充分防止越狱设备上的 运行ning,那么结合此类技术,您最初自己实现本地身份验证的方法可能仍然被认为是一个很好的解决方案.
  2. 出于安全原因,您可以改为考虑使用 .userPresence,并重新检查您的应用程序流程和用户体验,例如,减少需要执行这些 encryption/decryption 操作的频率,对它们进行批处理,甚至在您的用户界面中显示一些内容来向用户解释为什么他们每次都需要进行本地身份验证。根据您的应用程序的功能和您解释它的方式,如果您可以帮助用户理解为什么他们需要进行本地身份验证,那么用户甚至可能会对您的应用程序的安全性更有信心?