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

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

问题描述

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

  lazy var persistentStoreCoordinator:NSPersistentStoreCoordinator = {
//应用程序的持久性存储协调器。此实现创建并返回一个协调器,已将该应用程序的存储添加到该协调器。此属性是可选的,因为存在可能导致创建存储失败的合法错误条件。

//创建协调器并存储
let coordinator = NSPersistentStoreCoordinator(managedObjectModel:self.managedObjectModel)
let url = self.applicationDocumentsDirectory.URLByAppendingPathComponent(SingleViewCoreData.sqlite)
var failureReason =创建或加载应用程序保存的数据时出错。


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

do {
try coordinator .addPersistentStoreWithType(NSSQLiteStoreType,配置:nil,URL:url,options:dict)
} catch {
//报告我们得到的任何错误。
var dict = [String:AnyObject]()
dict [NSLocalizedDescriptionKey] =无法初始化应用程序保存的数据
dict [NSLocalizedFailureReasonErrorKey] = failureReason

dict [NSUnderlyingErrorKey] =错误为NSError
let wrappedError = NSError(domain:YOUR_ERROR_DOMAIN,code:9999,userInfo:dict)
//将其替换为适当的错误代码。
// abort()使应用程序生成崩溃日志并终止。您不应该在运输应用程序中使用此功能,尽管它可能在开发过程中有用。
NSLog(未解决的错误\(wrappedError),\(wrappedError.userInfo))
abort()
}

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

} catch {

}

do {
let url = self.applicationDocumentsDirectory.URLByAppendingPathComponent(SingleViewCoreData.sqlite-wal)
尝试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)
尝试NSFileManager.defaultManager()。setAttributes([NSFileProtectionKey:NSFileProtectionComplete],ofItemAtPath:url.path!)
// try print(NSFileManager.defaultManager()。attributesOfFileSystemForPath(String(url) ))

} catch {

}


返回协调员
}()

我还在功能中为我的目标启用了数据保护。我从Apple开发人员门户重新生成了配置文件,并且使用了Enabled Data Protection。



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

  func checkProtectionForLocalDb(atDir:String){

let fileManager = NSFileManager.defaultManager()
让枚举器:NSDirectoryEnumerator = fileManager.enumeratorAtPath(atDir)!


在枚举器中的路径{

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


}


}

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



如Apple文档中 Apple Docs 保护数据使用磁盘加密检查变量protectedDataAvailable的值是否如以下代码所示更改

  public func applicationDidEnterBackground(application:UIApplication){
//使用此方法释放共享资源,保存用户数据,使定时器无效,并存储足够的应用程序状态信息,以便将应用程序恢复到当前状态,以备稍后终止。
//如果您的应用程序支持后台执行,则调用此方法而不是applicationWillTerminate:当用户退出时。
NSThread.sleepForTimeInterval(10)
sleep(10)
let dataAvailable:Bool = UIApplication.sharedApplication()。protectedDataAvailable
print(Protected Data Available:+ String(dataAvailable ))

}

如果我检查值没有延迟它设置为true,但在添加延迟后设置为false。不过,当我下载容器来显示内容的时候,这是一个令人鼓舞的事情,它仍然具有在sqlitebrowser中打开时仍显示内容的.sqlite文件。

解决方案

好的,我终于明白了。



使用Xcode 7.3.1 ..



启用文件保护


  1. 使用应用程序目标上的功能选项卡启用文件保护

  2. 如果您不想使用默认的NSFileProtectionComplete,请在应用程序ID下的开发人员门户中更改此设置

  3. 确保XCode具有新的配置文件这将创建。

  4. 为了保护您的应用程序创建的文件,就是这样。

  5. 为了保护Core Data,您需要将NSPersistentStoreFileProtectionKey:NSFileProtectionComplete选项添加到您的持续存储。

示例:

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

测试文件保护



我无法使用连接到计算机的非越狱设备进行测试。每次尝试以这种方式访问​​设备时,都需要信任电脑,我相信信任计算机总是能够读取手机的数据(可信计算机可以与iOS设备同步,创建备份和访问设备的照片,视频,联系人和其他内容 - https://support.apple.com/en-us/HT202778 )。我认为其他答案在SO引用这种技术不再适用于更新版本的iOS。确实,我总是能够使用XCode下载容器,并使用iPhone Explorer查看应用程序的数据。所以如何测试...



1 - 创建存档,并确保它具有正确的权利,方法是运行以下命令。应用程序文件从命令行:

  codesign -d --entitlements: - < path_to_app_binary> 

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

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

此外,我使用以下两种技术来确保数据保护确实有效。他们都需要代码更改。



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

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

如果我在我的一个文件中打印出来:

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

注意NSFileProtectionKey:NSFileProtectionComplete对。



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

  @IBAction func settingButtonTouch (发件人: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(后台任务已结束)
UIApplication.sharedApplication ).endBackgroundTask(backgroundTask)
backgroundTask = UIBackgroundTaskInvalid
}

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

func checkFiles(){
//尝试访问受保护的资源,即一个核心数据存储或文件
}

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


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
}()

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.

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)


    }


}

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.

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))

}

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.

解决方案

Ok, I finally understand this.

Using Xcode 7.3.1..

Enabling File Protection

  1. Enable File Protection using the Capabilities tab on your app target
  2. If you do not want the default NSFileProtectionComplete, change this setting in the developer portal under your app id
  3. Make sure XCode has the new provisioning profile this creates.
  4. For protecting files your app creates, that's it.
  5. To protect Core Data, you need to add the NSPersistentStoreFileProtectionKey: NSFileProtectionComplete option to your persistent store.

Example:

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

Testing File Protection

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 - Create an archive and ensure that it is has the proper entitlements by running the following on the .app file from the command line:

codesign -d --entitlements :- <path_to_app_binary>

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 - Add some code to verify that the proper NSFileProtectionKey is being set on your files and/or core data store:

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, "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]

Note the "NSFileProtectionKey": NSFileProtectionComplete pair.

3 - Modify the following code and hook it up to some button in your app.

@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
}

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天全站免登陆