Wherein I Move a Lot of Words Around

Core Data and Data Protection

A project I’m working on needed to secure the Core Data database on-disk in iOS (the nature of the content demanded it). When asked about this I instinctively said, “Sure! iOS has data protection APIs that easily allow that!”

I should stop answering so quickly.

Yes, there are APIs for that. The ones you’ll find mentioned all around the Internet are the additions to NSData and NSFileManager that let you use the NSFileProtectionKey attribute and set the protection class of the file and — MAGIC! — the file is encrypted and the keys are managed for you. The very first thing I did, then, was to naively write:

if (![[NSFileManager defaultManager]
     ofItemAtPath:[storeURL path]
            error:&error]) {
    NSLog(@"Failed to set file protection attribute: %@", error);

Well, it ran and didn’t throw an error. That’s good. I then locked the device and had Xcode copy the sandbox out and then I looked inside the Documents folder. As expected, there was no SQLite file inside. However, there were two other files inside I wasn’t expecting.



Of course.

I set the protection attribute on the Documents directory itself, mostly to say I tried it (it didn’t work, of course). While I could test for the files and set the attribute on them at various key moments, that was hacky and not how you do security. Another answer was required.

After an hour of searching around the Internet, blog posts, presentations, and — gasp — the Apple Developer Forums (I was desperate) I came upon the WWDC 2012 video for session 214: “Protecting the User’s Data”. On a slide halfway in there was a list of APIs that offered protection options for files and I saw two that weren’t listed anywhere else I’d found: sqlite3 and Core Data.

Light. Bulb.

I turned to the NSPersistentStoreCoordinator docs and to the options when creating the store itself and found the magic. If you pass in @{NSPersistentStoreFileProtectionKey:NSFileProtectionComplete} to the options: parameter of addPersistentStoreWithType: then Core Data will open the SQLite database with the SQLITE_OPEN_FILEPROTECTION_COMPLETE flag and then everything should be glorious.

So, I set that property, put the app on my phone, locked it, and then pulled the container again. Hmm.


That’s more promising, as the WAL (write-ahead log) has user data in it. But what is this SHM file and why was it excluded? The SQLite site had the answer. It’s a shared memory file used as an index into the WAL file. I opened it up in Hex Fiend and after a file header the rest of the file was filled with NUL characters, so it seems safe. The SQLite documentation assures us that it contains no persistent content. So, barring SQLite bugs, that’s probably fine.