NSFileProtectionComplete 不加密核心数据文件 [英] NSFileProtectionComplete doesn't encrypt the core data file

查看:54
本文介绍了NSFileProtectionComplete 不加密核心数据文件的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我使用 Xcode 7.3 for iOS 9.3 来尝试加密核心数据文件.我正在尝试使用 NSPersistentStoreFileProtectionKey 并将其设置为 NSFileProtectionComplete 以启用加密.由于某种原因它不起作用,我总是可以看到应用程序生成的 .sqlite 文件并浏览 sqlitebrowser 或 iexplorer 中的内容.这是我的代码:

I am using Xcode 7.3 for iOS 9.3 to try and encrypt a Core Data file. I am trying to use NSPersistentStoreFileProtectionKey and set it to NSFileProtectionComplete to enable the encryption. It is not working for some reason and I can always see the .sqlite file generated by the app and browse through the content in sqlitebrowser or iexplorer. Here is my code :

lazy var persistentStoreCoordinator: NSPersistentStoreCoordinator = {
    // The persistent store coordinator for the application. This implementation creates and returns a coordinator, having added the store for the application to it. This property is optional since there are legitimate error conditions that could cause the creation of the store to fail.

    // Create the coordinator and store
    let coordinator = NSPersistentStoreCoordinator(managedObjectModel: self.managedObjectModel)
    let url = self.applicationDocumentsDirectory.URLByAppendingPathComponent("SingleViewCoreData.sqlite")
    var failureReason = "There was an error creating or loading the application's saved data."


    let dict: [NSObject : AnyObject] = [
        NSPersistentStoreFileProtectionKey        : NSFileProtectionComplete
    ]

    do {
        try coordinator.addPersistentStoreWithType(NSSQLiteStoreType, configuration: nil, URL: url, options: dict)
    } catch {
        // Report any error we got.
        var dict = [String: AnyObject]()
        dict[NSLocalizedDescriptionKey] = "Failed to initialize the application's saved data"
        dict[NSLocalizedFailureReasonErrorKey] = failureReason

        dict[NSUnderlyingErrorKey] = error as NSError
        let wrappedError = NSError(domain: "YOUR_ERROR_DOMAIN", code: 9999, userInfo: dict)
        // Replace this with code to handle the error appropriately.
        // abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
        NSLog("Unresolved error (wrappedError), (wrappedError.userInfo)")
        abort()
    }

    do {
        let url = self.applicationDocumentsDirectory.URLByAppendingPathComponent("SingleViewCoreData.sqlite")
        try NSFileManager.defaultManager().setAttributes([NSFileProtectionKey : NSFileProtectionComplete], ofItemAtPath: url.path!)

    } catch {

    }

    do {
        let url = self.applicationDocumentsDirectory.URLByAppendingPathComponent("SingleViewCoreData.sqlite-wal")
        try NSFileManager.defaultManager().setAttributes([NSFileProtectionKey : NSFileProtectionComplete], ofItemAtPath: url.path!)
        //            try print(NSFileManager.defaultManager().attributesOfFileSystemForPath(String(url)))

    } catch {

    }

    do {
        let url = self.applicationDocumentsDirectory.URLByAppendingPathComponent("SingleViewCoreData.sqlite-shm")
        try NSFileManager.defaultManager().setAttributes([NSFileProtectionKey : NSFileProtectionComplete], ofItemAtPath: url.path!)
        //            try print(NSFileManager.defaultManager().attributesOfFileSystemForPath(String(url)))

    } catch {

    }


    return coordinator
}()

我还在功能"中为我的目标启用了数据保护.我已从 Apple 开发人员门户重新生成了配置文件,并将其与启用的数据保护结合使用.

I have also enabled Data Protection for my target in the "Capabilities". I have regenerated the provisioning profile from the Apple Developer portal and am using that with Enabled Data Protection.

我还使用以下代码来检查 .sqlite 、 .sqlite-wal 和 .sqlite-shm 文件的文件属性.NSFileProtectionKey 已为所有 3 个正确设置.

I am also using the following code to check the file attributes of .sqlite , .sqlite-wal and .sqlite-shm files. NSFileProtectionKey is correctly set for all 3 of them.

func checkProtectionForLocalDb(atDir : String){

    let fileManager = NSFileManager.defaultManager()
    let enumerator: NSDirectoryEnumerator = fileManager.enumeratorAtPath(atDir)!


    for path in enumerator {

        let attr : NSDictionary = enumerator.fileAttributes!
        print(attr)


    }


}

我还尝试禁用日志模式以防止创建 -wal 和 -shm 文件.但我仍然可以读取 .sqlite 文件.即使属性读取 NSFileProtectionComplete.

I also tried disabling the Journal mode to prevent -wal and -shm files from being created. But I can still read the .sqlite file. Even though the attributes read NSFileProtectionComplete.

如 Apple 文档中所述,Apple Docs 在使用磁盘加密保护数据"下,我尝试检查变量 protectedDataAvailable 的值是否如以下代码所示发生变化

As described in the Apple Documentation at Apple Docs under "Protecting Data using On Disk Encryption", I tried to check whether the value of variable protectedDataAvailable changes as shown in the code below

public func applicationDidEnterBackground(application: UIApplication) {
    // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
    // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
    NSThread.sleepForTimeInterval(10)
    sleep(10)
    let dataAvailable : Bool = UIApplication.sharedApplication().protectedDataAvailable
    print("Protected Data Available : " + String(dataAvailable))

}

如果我在没有延迟的情况下检查该值,则它被设置为 true,但在添加延迟后,它被设置为 false.这有点令人鼓舞,但是,在我下载容器之后,为了显示内容,它仍然有 .sqlite 文件,在 sqlitebrowser 中打开时仍然显示内容.

If I check the value without the delay it's set to true but after adding the delay it's set to false. This is kind of encouraging, however, right after, when I download the container, to show the content, it still has .sqlite file that still shows the content when opened in sqlitebrowser.

推荐答案

好的,我终于明白了.

使用 Xcode 7.3.1

Using Xcode 7.3.1

启用文件保护

  1. 使用应用目标上的功能"选项卡启用文件保护
  2. 如果您不想要默认的 NSFileProtectionComplete,请在您的应用 ID 下的开发者门户中更改此设置
  3. 确保 Xcode 具有由此创建的新配置文件.
  4. 为了保护您的应用创建的文件,仅此而已.
  5. 为了保护核心数据,您需要将 NSPersistentStoreFileProtectionKey: NSFileProtectionComplete 选项添加到您的持久存储中.

示例:

var options: [NSObject : AnyObject] = [NSMigratePersistentStoresAutomaticallyOption: true,
                   NSPersistentStoreFileProtectionKey: NSFileProtectionComplete,
                NSInferMappingModelAutomaticallyOption: true]
    do {
        try coordinator!.addPersistentStoreWithType(NSSQLiteStoreType, configuration: nil, URL: url, options: options)

测试文件保护

我无法使用连接到计算机的未越狱设备进行测试.每次尝试以这种方式访问​​设备都需要我信任"计算机,并且我相信受信任的计算机始终能够读取手机的数据(受信任的计算机可以与您的 iOS 设备同步、创建备份并访问您设备的照片、视频、联系人和其他内容" - https://support.apple.com/en-us/HT202778).我认为关于此技术的其他答案对于更新的 iOS 版本不再有效.事实上,我总是能够使用 XCode 下载容器并使用 iPhone Explorer 查看应用程序的数据.那么如何测试...

I am not able to test this using a non-jailbroken device connected to a computer. Every attempt to access the device this way requires that I "trust" the computer and I believe that trusted computers are always able to read the phone's data ("Trusted computers can sync with your iOS device, create backups, and access your device's photos, videos, contacts, and other content" - https://support.apple.com/en-us/HT202778). I think the other answers on SO referencing this technique are no longer valid with more recent versions of iOS. Indeed, I am always able to download the container using XCode and view the app's data using iPhone Explorer. So how to test...

1 - 通过从命令行对 .app 文件运行以下命令,创建存档并确保其具有正确的权利:

codesign -d --entitlements :- <path_to_app_binary>

您应该会看到代表您的数据保护级别的键/值对.在这个例子中,NSFileProtectionComplete:

You should see a key/value pair that represents your Data Protection level. In this example, NSFileProtectionComplete:

<key>com.apple.developer.default-data-protection</key>
<string>NSFileProtectionComplete</string>

此外,我使用了以下两种技术来让自己确信数据保护确实有效.它们都需要更改代码.

In addition, I used the following two techniques to satisfy myself that the data protection is indeed working. They both require code changes.

2 - 添加一些代码来验证是否在您的文件和/或核心数据存储中设置了正确的 NSFileProtectionKey:

NSFileManager.defaultManager().attributesOfItemAtPath(dbPath.path!)

如果我将其打印在我的一个文件上,我会得到:

If I print this out on one of my files I get:

["NSFileCreationDate": 2016-10-14 02:06:39 +0000, "NSFileGroupOwnerAccountName": mobile, "NSFileType": NSFileTypeRegular, "NSFileSystemNumber": 16777218, "NSFileOwnerAccountFileName": mobile,"NSFileReference1":, "NSFileModificationDate": 2016-10-14 02:06:39 +0000, "NSFileExtensionHidden": 0, "NSFileSize": 81920, "NSFileGroupOwnerAccountID": 501, "NSFileOwnerAccountID": 5001, "NSFileSFileix2sPermission", "NSFileSize":": NSFileProtectionComplete, "NSFileSystemFileNumber": 270902]

["NSFileCreationDate": 2016-10-14 02:06:39 +0000, "NSFileGroupOwnerAccountName": mobile, "NSFileType": NSFileTypeRegular, "NSFileSystemNumber": 16777218, "NSFileOwnerAccountName": mobile, "NSFileReferenceCount": 1, "NSFileModificationDate": 2016-10-14 02:06:39 +0000, "NSFileExtensionHidden": 0, "NSFileSize": 81920, "NSFileGroupOwnerAccountID": 501, "NSFileOwnerAccountID": 501, "NSFilePosixPermissions": 420, "NSFileProtectionKey": NSFileProtectionComplete, "NSFileSystemFileNumber": 270902]

注意 "NSFileProtectionKey": "NSFileProtectionComplete" 对.

3 - 修改以下代码并将其连接到您应用中的某个按钮.

@IBAction func settingButtonTouch(sender: AnyObject) {
        updateTimer = NSTimer.scheduledTimerWithTimeInterval(0.5, target: self,
                                                             selector: #selector(TabbedOverviewViewController.runTest), userInfo: nil, repeats: true)
        registerBackgroundTask()
}

var backgroundTask: UIBackgroundTaskIdentifier = UIBackgroundTaskInvalid
var updateTimer: NSTimer?

func registerBackgroundTask() {
    backgroundTask = UIApplication.sharedApplication().beginBackgroundTaskWithExpirationHandler {
        [unowned self] in
        self.endBackgroundTask()
    }
    assert(backgroundTask != UIBackgroundTaskInvalid)
}

func endBackgroundTask() {
    NSLog("Background task ended.")
    UIApplication.sharedApplication().endBackgroundTask(backgroundTask)
    backgroundTask = UIBackgroundTaskInvalid
}

func runTest() {
    switch UIApplication.sharedApplication().applicationState {
    case .Active:
        NSLog("App is active.")
        checkFiles()
    case .Background:
        NSLog("App is backgrounded.")
        checkFiles()
    case .Inactive:
        break
    }
}

func checkFiles() {
    // attempt to access a protected resource, i.e. a core data store or file
}        

当您点击按钮时,此代码开始每 0.5 秒执行一次 checkFiles 方法.这应该与应用程序在前台或后台无限期运行 - 直到您锁定手机.那时它应该会在大约 10 秒后可靠地失败 - 完全如 NSFileProtectionComplete 的描述中所述.

When you tap the button this code begins executing the checkFiles method every .5 seconds. This should run indefinitely with the app in the foreground or background - until you lock your phone. At that point it should reliably fail after roughly 10 seconds - exactly as described in the description of NSFileProtectionComplete.

这篇关于NSFileProtectionComplete 不加密核心数据文件的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆