[关闭]
@qinyun 2017-10-31T02:55:50.000000Z 字数 4747 阅读 817

如何使用iOS数据保护功能

未分类


现代iOS设备支持数据保护功能,其负责利用内置加密硬件保护用户数据。在今天的文章中,我们将了解各类应用如何利用这一功能保护自有文件、从各来源处收集细节信息,同时将其与我自己的观察结论加以结合。

存储在iOS设备之上的每一个文件皆采取四种数据保护类型之一,这些保护手段将确定文件何时可以接受读取与写入操作。而开发者则能够以授权或者编程方式对这些保护类型加以设置。大家应该尽可能利用此类机制保护用户数据,而依效果由好到差排序,这四种手段分别为:

文件仅能够在设备解锁时进行访问。当设备被锁定时,您的应用将收到一条UIApplicationProtectedDataWillBecomeUnavailable通知,并在10秒钟之后失去对受保护文件的访问能力。尽可能多地选用这种方式,但对于那些需要在后台持续运行的应用,请不要选择这一机制。

当设备被锁定时,各文件仍然能够进行创建,而已经打开的文件则可继续接受访问。利用这一机制,我们可以在后台完成各类相关任务——例如保存新数据或者更新数据库。

当设备引导完成后,对应文件可在用户输入密码后随时接受访问——即使是在设备被锁定的情况下。利用这种方式,您可以随时读取运行在后台的文件。

文件始终可接受访问。系统在某些情况下需要利用这一机制,但其并不适合于绝大多数第三方应用。

如果大家希望对受保护的文件进行访问,则操作会返回NSFileReadNoPermissionError(代码257) 或者NSFileWriteNoPermissionError (代码513)错误。

感兴趣的朋友可以点击此处参阅苹果iOS安全白皮书以了解与上述保护类型相关的更多技术信息。

需要强调的是,数据保护机制并不适用于模拟器。Mac设备上采用的FileVault加密机制亦使用完全不同的工作原理。即使设置了保护类型,其在随后的读取中也无法起效。您将始终读取NSURLFileProtectionCompleteUntilFirstUserAuthentication ——至少在我的Mac上是如此。因此请务必在直机之上进行数据保护能力测试。

设置默认保护等级

CompleteUntilFirstUserAuthentication

要完成这项设置,大家无需进行任何操作——因为自iOS 7以来,所有iOS版本都将其作为默认设置。

Complete

你的应用或者扩展容器所创建的文件具体使用怎样的默认保护等级,取决于com.apple.developer.default-data-protection权限,其在Xcode中显示为“Data Protection(数据保护)”。

你需要为相关应用及其全部扩展设置这一选项。在这方面,请注意以下两点:

1.根据Quinn“The Eskimo!”在苹果开发者论坛上所言,此权限需要在应用安装之前进行设置。

你权限设置(通过配置文件存放在您的App ID当中)中的默认数据保护等级仅在应用容器的创建过程中进行使用。

根据我个人的实际调查,如果变更一款已安装应用的权限设置,那么有时候其能够将新等级应用于后续文件,但有时候却不能。不过可以肯定的是,变更不会影响原有文件,因此除非您打算开发一款全新应用,否则最好以编程方式设置保护类型。关于这一点,我们将在后文中详加说明。

2.由于此权限仅应用于您的应用或者扩展容器,因此大家需要以编程方式为共享容器设置保护等级。

牢记以上注意事项,权限设置就变得非常容易。在佻的Xcode项目设置当中,前往应用或者扩展的“Capabilities”标签并启用“Data Protection”即可。就这么方便。

如此一来,您的应用权限文件当中(如果尚不存在,请另行创建)会被添加一条数据保护权限,且该权限的值被设置为NSFileProtectionComplete。其还将在苹果开发者网站Certificates, Identifiers & Profiles中的开发者App ID之上启用数据保护功能。

感兴趣的朋友亦可点击此处查看苹果应用发布指南当中的“启用数据保护”部分,其中简要说明了如果更新此Capabilities。

CompleteUnlessOpen

接下来我们要面对的是NSFileProtectionComplete(具体依以上说明),而后同时对苹果开发者网站Certificates, Identifiers & Profiles部分中的权限文件与App ID进行保护等级变更。大家必须对这两个值进行手动同步,否则会在构建时出现无效权限错误。

这里具体介绍导航流程:Identifiers > App IDs > (具体应用ID) > Edit > Data Protection > Sharing and Permissions。您可以在这里从三种保护等级当中选择其一进行设置(但不包括NSFileProtectionNone)。

该权限文件当中最关键的关注点为NSFileProtectionCompleteUnlessOpen。
我发现即使是进行自动配置时,Xcode都不会更新网站之上配置文件进行的变更。大家可以删除~/Library/MobileDevice/Provisioning Profiles/以强制要求Xcode重新下载该配置文件。如果觉得有些麻烦,您也可以选择使用Craig Hockenberry提供的Provisioning QuickLook插件以快速判断当前配置是否正确。

None

您不可将None设置为默认等级,因为苹果开发者网站的Certificates, Identifiers & Profiles部分并不提供此选项。另外,大家也绝对不应该选择这种自我放弃的作法。

以编程方式设置保护等级

如果您的应用程序已安装在用户设备上,而没有设置权限,并且您想要的其他设备NSFileProtectionCompleteUntilFirstUserAuthentication,则需要以编程方式设置保护类型,以保护新文件并升级现有的文件。请注意,您不需要设置使用这些API的权利。

为特定文件设置保护等级

我们首先来看单独一个文件,其中包含多个API以接收作为选项的保护类型。
如果您打算写入一个来自Data/NSData的新文件,则使用:

  1. try data.write(to: fileURL, options: .completeFileProtection)

对于现有文件,你可以使用 NSFileManager/FileManager 或 NSURL:

  1. try FileManager.default.setAttributes([.protectionKey: FileProtectionType.complete], ofItemAtPath: fileURL.path)
  2. // or
  3. // cast as NSURL because the fileProtection property of URLResourceValues is read-only
  4. try (fileURL as NSURL).setResourceValue(URLFileProtection.complete, forKey: .fileProtectionKey)

在Core Data情况下,您可以在添加持久存储时传递此保护类型:

  1. try persistentStoreCoordinator.addPersistentStore(ofType: NSSQLiteStoreType, configurationName: nil, at: storeURL, options: [NSPersistentStoreFileProtectionKey: FileProtectionType.complete])

感兴趣的朋友可以点击此处参阅苹果iOS应用编程指南当中的“利用磁盘内加密保护数据”章节。

为某一目录中的新文件设置保护等级

Quinn "The Eskimo!"还在苹果开发者论坛上发布了这条非常实用的信息

在默认情况下,数据保护值会在项目创建过程中直接自父目录处继承。举例来说,如果您将目录设置为NSFileProtectionComplete,则在该目录中创建的任何项目都将默认被设置为NSFileProtectionComplete。

权限文件负责控制容器内root目录下的数据保护值,而此值亦将作用于容器内所创建的任何内容的保护等级。然而,如果您为某一目录明确设置了该值,则该目录内创建的后续条目将默认使用这一新值。

因此,您可以使用FileManager/NSFileManager 或者NSURL作为目录URL以替代文件URL,或者在创建此目录时提供该保护属性:

  1. try FileManager.default.createDirectory(at: directoryURL, withIntermediateDirectories: true, attributes: [.protectionKey: FileProtectionType.complete])

如此一来,您随后即可按正常方式在该目录中创建文件了。

变更同一目录当中所有现有文件的保护等级

当为某一目录设置保护等级时,该目录中全部现有文件的保护等级并不会随之变化。因此,大家需要为该目录中的每个文件设置正确的保护等级。

您可以使用FileManager.DirectoryEnumerator/NSDirectoryEnumerator完成这项工作:

  1. guard let directoryEnumerator = FileManager.default.enumerator(at: directoryURL, includingPropertiesForKeys: [], options: [], errorHandler: { url, error -> Bool in
  2. print(error)
  3. return true
  4. }) else {
  5. print("Could not create directory enumerator at \(directoryURL.path)")
  6. return
  7. }
  8. // NSEnumerator is not generic in Swift so we have to deal with Any.
  9. for urlAsAny in directoryEnumerator {
  10. do {
  11. try (urlAsAny as! NSURL).setResourceValue(URLFileProtection.complete, forKey: .fileProtectionKey)
  12. } catch {
  13. print(error)
  14. }
  15. }

利用Instant实现数据保护

大家可以轻松利用PSPDFKit Instant保护您的文件。如果大家设置了一项默认数据保护等级,Instant将自动使用该保护选项,您无需进行任何其它操作。如果大家希望为存储在Instant当中的特定文件及注释设置不同的保护等级,则可在PSPDFInstantClient上使用dataDirectory类属性以访问Instant的数据存储路径,而后使用以上提到的变更方法。感兴趣的朋友可以点击此处参阅我们的Instant数据保护指南以了解更多细节信息。

当然,也欢迎大家热情留言以分享您的心得与体验。

原文链接

https://pspdfkit.com/blog/2017/how-to-use-ios-data-protection/

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注