Implementing and Testing iOS data protection

27,176

Solution 1

Update: With iOS 6 it's supposedly possible to require data protection for your application by using an entitlement that needs to be configured on the App ID in the iOS provisioning profile. I haven't tested this yet, and this is the best information I could find on it https://devforums.apple.com/message/707939#707939


My investigations into this matter lead me to believe that it is very difficult to determine if data protection is enabled on a device.

File protection is enabled by setting the NSFileProtectionKey file attribute to NSFileProtectionComplete

For example, to create a protected file you could run code like:

[[NSFileManager defaultManager] createFileAtPath:[self filePath]
                                        contents:[@"super secret file contents" dataUsingEncoding:NSUTF8StringEncoding]
                                      attributes:[NSDictionary dictionaryWithObject:NSFileProtectionComplete
                                                                             forKey:NSFileProtectionKey]];

Unfortunately this code will execute without error even if Data Protection is not enabled on the device (or if the code is run on the Simulator where Data Protection is not available).

Worse, the NSFileProtectionComplete attribute will be be set regardless of whether the file is protected or not. The following:

self.fileProtectionValue = [[[NSFileManager defaultManager] attributesOfItemAtPath:[self filePath]
                                                                             error:NULL] valueForKey:NSFileProtectionKey];

NSLog(@"file protection value: %@", self.fileProtectionValue);

will spit out file protection value: NSFileProtectionComplete no matter whether Data Protection is enabled or not.

There are two methods that I've been able to use to discover if File Protection is working as expected. Unfortunately neither of these methods are suitable for detecting if Data Protection is enabled on a device in the field.

Both methods work on the idea that a protected file can not be read if the device is locked.

Method one involves using a timer to attempt to read the file after the device is locked, but while your application continues to run:

[self performSelector:@selector(doReload) withObject:nil afterDelay:20];

- (void)doReload {

    NSLog(@"protected data available: %@",[[UIApplication sharedApplication] isProtectedDataAvailable] ? @"yes" : @"no");

    NSError *error;

    self.fileContents = [NSString stringWithContentsOfFile:[self filePath]
                                              encoding:NSUTF8StringEncoding
                                                 error:&error];

    NSLog(@"file contents: %@\nerror: %@", self.fileContents, error);
}

If you run the above code and lock a data protected device it will spit out:

protected data available: no
file contents: (null)
error: Error Domain=NSCocoaErrorDomain Code=257 "The operation couldn’t be completed. (Cocoa error 257.)" UserInfo=0x16e110 {NSFilePath=/var/mobile/Applications/D71F1F1F-6C25-4848-BB1F-51539B47EC79/Documents/protected_file, NSUnderlyingError=0x16e010 "The operation couldn’t be completed. Operation not permitted"}

The 20 second delay is necessary because there is a 10 second or so grace period where protected data is still available after a Data Protection enabled device is locked.

The second method is to create a protected file in an application, exit the application, lock the device, wait 10 seconds, and then use the XCode organizer to download the contents of the application. This will produce an error message and the protected file will be empty.

If either of the above tests fail to behave as described then Data Protection is either not enable, or your File Protection code was not implemented correctly.

Because I've not found any way to verify within the application that Data Protection is enabled before I write confidential information to disk, I've filed a feature enhancement request with Apple to be able to mark an application as requiring Data Protection to be enabled. (rdar://10167256)

Apple does offer a solution to this through their Mobile Device Management (MDM) APIs, which combined with a third party server can be used to enforce policies that require Data Protection to be enabled on devices.

Solution 2

You can use the iExplorer app to detect if your files are encrypted. iExplorer lets you browse the filesystem of your iPhone/iPad, and open the file (of course your device must be plugged into your Mac).

When the device is locked, the files can't be read correctly.

Solution 3

From the NSFileManager class doc:

The file is stored in an encrypted format on disk and cannot be read from or written to while the device is locked or booting.

You just pass the constant when you set the file attributes.

When writing the contents of an NSData object to disk using the writeToFile:options:error: method, include the NSDataWritingFileProtectionComplete option.

Use the setAttributes:ofItemAtPath:error: method of NSFileManager to add the NSFileProtectionKey attribute (with the NSFileProtectionComplete value) to an existing file

http://developer.apple.com/iphone/library/documentation/iPhone/Conceptual/iPhoneOSProgrammingGuide/StandardBehaviors/StandardBehaviors.html

EDIT (Determining the Availability of Protected Files)

A protected file is accessible only when a device is unlocked. Because applications may continue running while a device is locked, your code should be prepared to handle the possibility of protected files becoming unavailable at any time. The UIKit framework provides ways to track whether data protection is currently enabled.

*

  Use applicationProtectedDataWillBecomeUnavailable: and applicationProtectedDataDidBecomeAvailable: methods and use them to track changes to the availability of protected data.
*

  An application can register for the UIApplicationProtectedDataWillBecomeUnavailable and UIApplicationProtectedDataDidBecomeAvailable notifications.
*

  The protectedDataAvailable property of the shared UIApplication object indicates whether protected files are currently accessible. 

Any application that works with protected files should implement the application delegate methods. When the applicationProtectedDataWillBecomeUnavailable: method is called, your application should immediately close any protected files and refrain from using them again until the applicationProtectedDataDidBecomeAvailable: method is called. Any attempts to access the protected files while they are unavailable will fail.

Verifying file protection on jailbroken devices

To step further, if you would like to test the file protection of exact file, then you would need a jailbroken device. For that, here are the (non-detailed) steps:

1) Jailbreak an iOS device

2) Install Open SSH via Cydia (This is required to remotely access files from that device) (https://cydia.saurik.com/openssh.html)

3) Login from your computer (using Mac client or Terminal) as a root user to your device.

To find location of your app's directories and files, there are various ways. Either you can

  • grep the process of an app (Such as ps ax | grep YourAppName) - Make sure app is running on device to get the process details. It should give the location of app bundles
  • Alternatively, you can also search specific file using find you are interested in. For eg. find / -type f -name YouAppName.sqlite. It should give file location on the device.

From here, you can try to see if the file is really accessible or not, when phone is locked with a passcode; or not. - You can simply run cat YouAppName.sqlite to see if contents are accessible. Ia f file is protected, it should show

Operation not permitted

error; else if would show contents of file.

Again, this is required if you'd really like to check file protection of an individual file. If entitlements and capabilities are set properly, verifying entitlements should be enough for fileprotection.

On a side node, file explorer tools such as iExplorer don't help much in verification of FileProtection, because such tools require a device to be in "trusted" mode, so they have permissions to access the content of your device/apps.

Good luck!

Solution 4

Testing can be done within Xcode:

  1. Delete and reinstall your app
  2. On your iOS device go to Settings > Passcode and ensure 'Require Passcode' is set to 'Immediately'
  3. Lock your phone and wait 20 seconds
  4. On Xcode go to Window > Devices
  5. Select the app you'd like to test
  6. Click the settings cog and choose 'download container'
  7. Right click the downloaded .xcappdata file and select 'show package contents'. Anything you can view here has not been encrypted by NSFileProtectionComplete
  8. Unlock your phone and repeat steps 3-6. Files that previously did not appear that you can now view were successfully encrypted.

enter image description here

Solution 5

File protection can be enabled on a per-file or per-directory basis, or can be enabled for the whole application (using entitlements and the provisioning profile). To determine if a file or directory is protected, check the filesystem attributes for the data protection key. This should be valid even it's a parent directory that was set to be protected:

- (BOOL) isProtectedItemAtURL:(NSURL *)URL {
    BOOL            result                      = YES;
    NSDictionary    *attributes                 = nil;
    NSString        *protectionAttributeValue   = nil;
    NSFileManager   *fileManager                = nil;

    fileManager = [[NSFileManager alloc] init];
    attributes = [fileManager attributesOfItemAtPath:[URL path] error:&error];
    if (attributes != nil){
        protectionAttributeValue = [attributes valueForKey:NSFileProtectionKey];
        if ((protectionAttributeValue == nil) || [protectionAttributeValue isEqualToString:NSFileProtectionNone]){
            result = NO;
        }
    } else {
        // handle the error
    }
    return result;
}

To determine if the protected content is available, UIApplication provides a method for querying the protection state, isProtectedDataAvailable. Using it with the above method would allow you to determine wether a particular file or directory is available:

- (BOOL) isItemAtURLAvailable:(NSURL *)URL {
    BOOL            result                      = NO;

    if ([self isProtectedItemAtURL:URL]){
        // Item is protected
        if ([[UIApplication sharedApplication] isProtectedDataAvailable]){
            // Protected content is available
            result = YES;
        }
    } else {
        result = YES;
    }

    return result;
}
Share:
27,176

Related videos on Youtube

Omer
Author by

Omer

Ancient History: Graphic + 3D Designer and post-producer. Recent History: App Developer, mobile focused, UI/UX oriented. Present: Anti-Hero @OrangeLoops, watching Kezmo grow from the ground up. What's out there? Here: Kezmo CloseNet Some open source: OLSDynamicHeaderViewController

Updated on July 09, 2022

Comments

  • Omer
    Omer almost 2 years

    Just saw the Session 209 - Securing Application Data from de 2010 WWDC.

    The keynote explains a lot of things, including the way you can set data protection attributes to your files (NSFileProtectionComplete, NSFileProtectionNone) and how to decide which protection is best for your case.

    I just implemented it, but can't figure out how to test if the security is on, any ideas?

    In addition, I have a sql lite database that needs to be accessed in background from time to time, and this method of data protection seems to be not good enough.. any link or tutorial that guide me through the best db protection? (found sql cipher but is kinda heavy to add in a evoluted project)

    Thanks!

    • David Dunham
      David Dunham almost 13 years
      I've been wondering the same — when the device is locked, I can't get to the file system to see if the files are encrypted (not really interested in jailbreaking).
    • pixel
      pixel over 4 years
      I know this is old post but I am having same issue. And @Apple documentation is not helpful, no where did I find that it is not supported on simulators for example. I am also new to Swift (using Swift 4 at the moment). But regardless, even with real device, how do I confirm encription is actiually happening? I did implement applicationProtectedDataWillBecomeUnavailable and applicationProtectedDataDidBecomeAvailable but breakpoints there are not hit when I lock/unlock my device. Something else is missing.
  • Omer
    Omer over 13 years
    ok.. but how do I test the protection? I mean, I want to do something that tells me that the file is protected
  • Feras Arabiat
    Feras Arabiat over 10 years
    I'd add to this that you need to make sure your device is not enabled as a developer device in XCode organizer. So you'll want to right click it and ignore. it might also be a good idea to restart the phone, i held the power and home button until got the apple logo. After that when the device is locked and you try to extract the file from iExplorer the file the operation will fail silently (you wont get an error but no file will get extracted)
  • Mikkel Selsøe
    Mikkel Selsøe over 9 years
    I've tried implementing the doReload method (with delay), but it doesn't get called until the device unlocks again and data is available. How do I make sure it gets called in the background?
  • EmbCoder
    EmbCoder almost 8 years
    Do you mean the files just won't show up in the list? Or they will show in the list but when trying to open them in any kind of viewer would show encrypted values?
  • EmbCoder
    EmbCoder almost 8 years
    @FerasArabiat : How do I make sure the device is not enabled as a developer device in Xcode 7? When I right click in the Devices window, I do not see an option to Ignore. Also, shouldn't it anyways not show any data if the device is not configured in Xcode as a development device, regardless of whether its encrypted or not?
  • Declan McKenna
    Declan McKenna almost 8 years
    @EmbCoder They won't show up in the list. Presumably as your operating system won't be unable to read them.
  • Declan McKenna
    Declan McKenna almost 8 years
    You may also need to go in to your app to create some files that won't be there on the fresh installation.
  • EmbCoder
    EmbCoder almost 8 years
    I am always seeing the files listed. I am trying to encrypt a very simple entity in Core Data which gets created at the time application launches and gets populated after user logs in. It functions fine but the encryption doesn't work. I am setting NSPersistentStoreFileProtectionKey to NSFileProtectionComplete to achieve this. I have put my code here : stackoverflow.com/questions/39151959/…
  • Feras Arabiat
    Feras Arabiat almost 8 years
    @EmbCoder, I think it was removed in XCode 6... I'm not 100% positive about this as I have not tried it myself, but apples documentation says the following: 1. Connect the device to your Mac. 2. In Xcode, choose Window > Devices, and select the device under Devices. 3. In the lower-left corner of the Devices window, click the Action button (the gear icon to the right of the Add button) 4. Deselect “Show in Run Destinations Menu” from the pop-up menu.
  • Feras Arabiat
    Feras Arabiat almost 8 years
  • EmbCoder
    EmbCoder almost 8 years
    @Omer - Were you able to figure out a way to test it? I have been trying for 2 days now, I have posted my question here with the code : stackoverflow.com/questions/39151959/…
  • Omer
    Omer almost 8 years
    Honestly, this question is kinda old and I dont remember much. But what I endup doing is to use some software like iExplorer and try to access de files from the phone. When protected, files are listed but with 0bytes as size and you should not be able to open them. Have you tried something like this?
  • Omer
    Omer almost 8 years
    @EmbCoder basically this answer stackoverflow.com/a/10385450/219777
  • lostintranslation
    lostintranslation over 7 years
    Any more info on how to test that file protection is on when using an entitlements file that has data protect on?
  • lostintranslation
    lostintranslation over 7 years
    Can you test this on debug apps pushed to the device with iExplorer? I set my entitlements file to FileProtectComplete, however if I lock the device iExplorer can still see all the files.
  • Declan McKenna
    Declan McKenna over 7 years
    You're right, had a bit of a panic today as while checking if NSDataProtectionComplete encryptes plists I realised I could see everything in my apps container when connected to XCode>Devices. This weren't previously the case.
  • Michal Cichon
    Michal Cichon over 6 years
    I tried to do it this way, but [attributes valueForKey:NSFileProtectionKey] gives me always nil, I guess this attribute cannot be retrieve using fileManager.
  • Michal Cichon
    Michal Cichon over 6 years
    OK, NSFileProtectionKey can be retrieved only on a device. It doesn't work for a simulator.
  • quellish
    quellish over 6 years
    @MichalCichon that should not be the case, please file a radar