2011年4月22日金曜日

[iOS] データ保護(Data Protection)

データ保護


iOS 4 以降、データ保護機能が提供されるようになった。特徴は次の通り。

  • データ保護機能を有効にするにはパスコードロックを有効にする必要がある
  • データ保護の適用はファイル単位となる
  • アプリケーションが明示的にファイルにデータ保護属性を付加することで有効になる
  • データ保護属性のついたファイルには、デバイスロック中は保護されていてアクセスができない
  • バックグラウンドで動作するアプリであっても、デバイスロック時にはデータ保護されたファイルへアクセスできない
  • データ保護属性のついたファイルは、デバイスロック中に iTunesなどのツールから持ち出すことができない
  • データ保護属性のついたファイルは暗号化される
  • 対象機種:iPhone 4, iPhone 3GS, iPod touch (3rd generation or later), and all iPad models
[参考情報] Limitations of Data Protection in iOS 4 | Anthony Vance

※上記は若干自信が無いところもあるので違っていたら是非教えて下さい。


データ保護機能を有効にするにはパスコードロックが有効になっている必要があり、有効の場合はその旨メッセージが表示される。

※iOS 3から 4 へアップデートした場合にはこのメッセージが出ない。この場合「復元」操作が必要。
iOS 4:データ保護について


データ保護 API


データ保護向けの APIがいくつか用意されている。

NSFileManager

ファイル属性に NSFileProtectionKey が追加された。値に NSFileProtectionComplete を指定するとデータ保護が有効になる。
NSDictionary* attributes =
    [NSDictionary dictionaryWithObject:NSFileProtectionComplete forKey:NSFileProtectionKey];
NSError* error = nil;
[fileManager setAttributes:attributes ofItemAtPath:filePath error:&error];
NSFilManager Reference - File Protection Values

なおファイルコピー時に NSFileProtectionComplete の設定はコピーされない。コピー先のファイルは NSFileProtectionNone となる。

NSData

ファイル書き出し時に NSDataWritingFileProtectionComplete オプションを指定するとデータ保護の適用を指定することができる。
NSError* error = nil;
[data writeToFile:filePath
 options:NSDataWritingFileProtectionComplete error:&error];
NSData Class Reference - NSDataWritingOptions

UIApplicationDelegate

データ保護が有効になる直前、無効になる直前に呼ばれるデリゲートメソッドが用意されている。
- (void)applicationProtectedDataDidBecomeAvailable:(UIApplication *)application
- (void)applicationProtectedDataWillBecomeUnavailable:(UIApplication *)application
UIApplicationDelegate Protocol Reference

また UIApplication には保護データへアクセス可能かどうかを知ることができるプロパティ protectedDataAvailable が用意されている。
UIApplication Class Reference


サンプル


(1)データ保護なし (2)データ保護あり(NSFileManagerで属性設定) (3)データ保護あり(NSDataで書き出し)の3種類のファイルを作成してデータの持ち出しと属性を調べてみた。コードはこんな感じ。

// definitions
    NSString* basePath = [NSSearchPathForDirectoriesInDomains(
        NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
    NSFileManager* fileManager = [NSFileManager defaultManager];

    NSString* src1 = [[NSBundle mainBundle] pathForResource:@"sample" ofType:@"jpg"];
    NSString* src2 = [[NSBundle mainBundle] pathForResource:@"sample_encrypted" ofType:@"jpg"];

    NSString* dst1 = [basePath stringByAppendingPathComponent:@"sample.jpg"];
    NSString* dst2 = [basePath stringByAppendingPathComponent:@"sample_encrypted.jpg"];
    NSString* dst3 = [basePath stringByAppendingPathComponent:@"sample_encrypted2.jpg"];
    
    NSLog(@"dst1: %@", dst1);
    NSLog(@"dst2: %@", dst2);
    NSLog(@"dst3: %@", dst3);
    
    NSError* error = nil;
    
    // copy files
    if (![fileManager copyItemAtPath:src1 toPath:dst1 error:&error]) {
        NSLog(@"%@", error);        
    }
    if (![fileManager copyItemAtPath:src2 toPath:dst2 error:&error]) {
        NSLog(@"%@", error);        
    }

    // set attributes
    NSDictionary* attributes = [NSDictionary dictionaryWithObject:NSFileProtectionComplete
                                                           forKey:NSFileProtectionKey];
    if (![fileManager setAttributes:attributes
                       ofItemAtPath:dst2
                              error:&error]) {
        NSLog(@"%@", error);
    }
    
    // create dst3
    NSData* data = [NSData dataWithContentsOfFile:src1];
    if (![data writeToFile:dst3
                   options:NSDataWritingFileProtectionComplete
                     error:&error]) {
        NSLog(@"%@", error);
    }
あらかじめ JPEG画像を用意しておき、これを Documentディレクトリへコピーしたり(1)(2)、書きだしたり(3)している。


iTunes でアクセス


このアプリを実行した後、iTunes へ繋ぎデータを持ち出しを試みてみた。

予想通りデータ保護のかかっている(2)(3)のファイルは持ち出し時にエラーが出た。

データ保護はパスコードロックがかかっている時のみ有効。


試しにそれぞれのファイル属性を出してみた。
2011-04-22 10:26:20.894 FileProtectionSample[2272:707] dst1: {
    NSFileCreationDate = "2011-04-20 07:18:08 +0000";
    NSFileExtensionHidden = 0;
    NSFileGroupOwnerAccountID = 501;
    NSFileGroupOwnerAccountName = mobile;
    NSFileModificationDate = "2011-04-20 07:18:08 +0000";
    NSFileOwnerAccountID = 501;
    NSFileOwnerAccountName = mobile;
    NSFilePosixPermissions = 420;
    NSFileProtectionKey = NSFileProtectionNone;
    NSFileReferenceCount = 1;
    NSFileSize = 3896;
    NSFileSystemFileNumber = 77317;
    NSFileSystemNumber = 234881027;
    NSFileType = NSFileTypeRegular;
}
2011-04-22 10:26:20.912 FileProtectionSample[2272:707] dst2: {
    NSFileCreationDate = "2011-04-20 07:18:08 +0000";
    NSFileExtensionHidden = 0;
    NSFileGroupOwnerAccountID = 501;
    NSFileGroupOwnerAccountName = mobile;
    NSFileModificationDate = "2011-04-20 07:18:08 +0000";
    NSFileOwnerAccountID = 501;
    NSFileOwnerAccountName = mobile;
    NSFilePosixPermissions = 420;
    NSFileProtectionKey = NSFileProtectionComplete;
    NSFileReferenceCount = 1;
    NSFileSize = 3896;
    NSFileSystemFileNumber = 77318;
    NSFileSystemNumber = 234881027;
    NSFileType = NSFileTypeRegular;
}
2011-04-22 10:26:20.929 FileProtectionSample[2272:707] dst3: {
    NSFileCreationDate = "2011-04-20 07:53:00 +0000";
    NSFileExtensionHidden = 0;
    NSFileGroupOwnerAccountID = 501;
    NSFileGroupOwnerAccountName = mobile;
    NSFileModificationDate = "2011-04-22 01:26:20 +0000";
    NSFileOwnerAccountID = 501;
    NSFileOwnerAccountName = mobile;
    NSFilePosixPermissions = 420;
    NSFileProtectionKey = NSFileProtectionComplete;
    NSFileReferenceCount = 1;
    NSFileSize = 3896;
    NSFileSystemFileNumber = 13804;
    NSFileSystemNumber = 234881027;
    NSFileType = NSFileTypeRegular;
}
NSFileProtectionKey が設定されているのがわかる。またファイルサイズに違いは無い。APIを使ったデータ保護設定の有無は単純に属性設定の違いだけのようだ。

なおシミュレータでは "NSFileProtectionKey" の値が含まれない。確認できるのは実機だけのようだ。


Xcode でアクセス


Xcode のオーガナイザでアプリのフォルダ一式をダウンロードしてみる。
"Download"ボタンでPCへ保存しようとすると途中でエラーが出る。


ダウンロード


サンプルのソースコードは GitHub からどうぞ。
FileProtectionSample at 2011-04-21 from xcatsan/iOS-Sample-Code - GitHub


参考情報



「11-3 データ保護」が詳しく参考になった。


Core Data and Enterprise iPhone Applications – Protecting Your Data << Nick Harris
今回のデータ保護機能を使って CoreData のデータ(SQLite)を暗号化する方法が紹介されている。


Limitations of Data Protection in iOS 4 | Anthony Vance
データ保護についての特徴と制約がよくまとめられている。データ保護関連の情報は少ないので貴重な解説。


iOS 4: Data protection, hardware encryption and other insight
データ保護、ハードウェア暗号化の話題など。

Working with Protected Files
データ保護機能の使い方。setAttributes: を使う場合はデータを書きだす前に NSFileProtectionComplete を設定することが推奨されている。


iTunesを使ったファイル共有機能を使う方法 - 強火で進め
iTunes で iOSデバイス内のフォルダへアクセスする方法について。Info.plist で "Application supports iTunes file sharing" を有効にすると iTunes の Appタブでアプリケーションの Documents フォルダへアクセスすることができるようになる。今回のサンプルはこれが有効になっている。


iOS 4:データ保護について
データ保護を有効にする方法。iOS 3からのバージョンアップの場合はデバイスの復元が必要。

3 件のコメント:

  1. 私のテストで、iPhone4, 3GS, iPad2いずれも、xcodeとPhoneDiskでsample_encryptedなどのファイルをダウンロード出来ます、かつ閲覧出来ます。

    この保護機能は使える機能ではないね。

    返信削除
  2. Baryon さん、情報どうも。
    先ほど Xcode 4 + iOS 4.3 で試したら確かにダウンロード出来ました。
    この記事を書いた時は Xcode3 ベースだったと思うけれど挙動が変わった?
    確かにこれだと使えませんね。

    返信削除
  3. 思い出しました。

    この機能はデバイスがアンロックされた状態(通常利用している状態)だと働きません。
    文中にあるように「データ保護はパスコードロックがかかっている時のみ有効」です(→ 使おうとするとパスコードが尋ねられる状態)。

    お試しを。

    返信削除