限制钥匙串项的可访问性(Restricting Keychain Item Accessibility)


钥匙串项的可访问性,规定了app访问钥匙串项(比如如密码)的条件。

当用钥匙串服务(keychain services)存储用户隐私信息时,框架提供的默认行为是在安全性和可访问性之间进行了合理的权衡(trade-off)之后得到的结果。然而,在某些场景下,你可能想要做些不同的定制行为。

例如,默认地,你只能在设备解锁的情况下访问钥匙串选项(keychain items)。然而,未设密码的设备总是处于解锁状态的,所以你可能想进一步限制访问并规定钥匙串项必须在设置了密码(passcode)的设备上才能被访问。或者,当设备锁定时,你可以对后台进程放宽访问限制允许它访问钥匙串。

结合用户输入,钥匙串服务提供了根据设备状态来管理单个钥匙串项访问性的方式。

基于设备状态的访问控制(Control Access Based on Device State)

通过在创建钥匙串项时设置kSecAttrAccessible属性,可以控制app对和设备状态相关的钥匙串项的访问。关于如何创建一个钥匙串项的讨论,查看Adding a Password to the Keychain
8d491249-982f-49b1-a87b-23b175778012

Accessibility Values 对应的列表为可访问属性(kSecAttrAccessible)选一个值。例如,与系统默认访问性一致的,常常用来创建钥匙串项的查询字典如下:

var query: [String: Any] = [kSecClass as String: kSecClassInternetPassword,
                            kSecAttrAccount as String: account,
                            kSecAttrServer as String: server,
                            kSecAttrAccessible as String: kSecAttrAccessibleWhenUnlocked,
                            kSecValueData as String: password]

kSecAttrAccessible 使控制和设备状态相关的钥匙串项的可访问性成为可能。同时也允许指定在新设备上的恢复资格。如果属性值以字符串ThisDeviceOnly结尾,那么钥匙串项可以在同一设备上恢复,前提如果之前做过备份的话;但是备份的钥匙串项不能迁移到另外一个新设备上。

按照限制程度递减的顺序,可选择的属性值如下:

When Passcode Set

如果用户没有设置过密码,使用这个设置选项的钥匙串项,就不能被存储。如果用户从设备上移除了密码,那么使用这个设置(When Passcode Set)的钥匙串项会从钥匙串中被自动删除。如果设备解锁,你仅仅能访问拥有这个设置(When Passcode Set)的钥匙串项。如果app仅仅需要在前端运行时才访问钥匙串项,那么使用这个设置。

When Unlocked

使用这个设置的钥匙串项仅当app处于解锁状态时,才能被访问的。一个没有密码的设备被认定为总是处于解锁状态。当没有指定其他设置时,这个设置(When Unlocked)是默认的可访问性。

After First Unlock

这种状态,只有当设备重启之后用户第一次解锁设备的时候,或者设备没有设置密码时,才会出现。并且这种状态会一直保持直到设备被再次重启。当app在后台运行的时候需要访问钥匙串时,使用这个选项。

Always

不管设备处于何种状态,钥匙串项总是可访问的。不推荐这个设置选项。

总是使用最严格的限制对app来说是情理之中的事情。对于极其敏感的不想存储在iCloud中的数据,也许可以考虑使用kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly

要求验证用户(Demand User Presence)

仅当设备解锁就允许访问,在某些情况下也许并不足够安全。如果你的app提供直接访问银行账号的功能,你可以在从钥匙串中获取登录认证信息之前的最后时刻,校验授权用户。这有助于保护账户,即使用户把处于解锁状态的设备递给其他人使用。

创建一个钥匙串选项时,可以通过提供SecAccessControlRef 实例作为kSecAttrAccessControl属性的值来增强访问限制。

3a4efd20-75ea-4c84-8466-3e3fc35ba8

可以通过调用SecAccessControlCreateWithFlags函数来创建访问控制实例:

var error: NSError?
let access = SecAccessControlCreateWithFlags(NULL,  // Use the default allocator.
                                             kSecAttrAccessibleWhenUnlocked,
                                             kSecAccessControlUserPresence,
                                             &error);

这个函数需要两个不同的访问控制参数。第一个是可访问性设置,和前面说过的有选择的设置kSecAttrAccessible属性的值一样。如果你把他作为访问控制(kSecAttrAccessControl)属性的一部分,那么你不需要再直接设置可访问性(kSecAttrAccessible)属性。

第二个参数是一个访问控制标志,是SecAccessControlCreateFlags中的一个或多个选项按位与的结果。 你通过这些选项要求用户证明他们在场。 你可以明确要求进行生物识别——像Touch ID或Face ID——亦或者密码,但通常会像前面的代码片段中那样,使用kSecAccessControlUserPresence选项,让系统根据当前的情况来选择一个验证途径。

准备好访问控制实例后,把它加到query字典中:

var query: [String: Any] = [kSecClass as String: kSecClassInternetPassword,
                            kSecAttrAccount as String: account,
                            kSecAttrServer as String: server,
                            kSecAttrAccessControl as String: access,
                            kSecValueData as String: password]

要求验证密码

除了需要详细的设备状态和用户的在场,你还可能需要针对特定应用的密码。这不同于设备的解锁密码:它只保护特定的一个钥匙串选项。

为了做到这些,需要在定义访问控制标志的时候,将选项kSecAccessControlApplicationPassword加进去:

let access = SecAccessControlCreateWithFlags(NULL,  // Use the default allocator.
                                             kSecAttrAccessibleWhenPasscodeSet,
                                             kSecAccessControlBiometryAny
                                               | kSecAccessControlApplicationPassword,
                                             &error);

添加这个标志之后,创建钥匙串选项时,系统会提示用户需要设置一个密码,并且在取钥匙串选项时,系统会提示需要验证密码。用户成功输入之前设置的密码,才有可能获取对应的钥匙串选项,当然可能还需要满足其他限制条件,但这个限制条件和其他限制条件之间的关系是相互独立的。

英文原文:Restricting Keychain Item Accessibility

知识共享许可协议本作品采用知识共享署名-非商业性使用 4.0 国际许可协议进行许可。
本站文章除注明转载/出处外,均为本站原创或翻译,请务必在遵守许可协议的前提下转载。
发布时间:2019-01-11 16:47:24 阅读:390 标签:翻译技术