データ保護
iOS 4 以降、データ保護機能が提供されるようになった。特徴は次の通り。
- データ保護機能を有効にするにはパスコードロックを有効にする必要がある
- データ保護の適用はファイル単位となる
- アプリケーションが明示的にファイルにデータ保護属性を付加することで有効になる
- データ保護属性のついたファイルには、デバイスロック中は保護されていてアクセスができない
- バックグラウンドで動作するアプリであっても、デバイスロック時にはデータ保護されたファイルへアクセスできない
- データ保護属性のついたファイルは、デバイスロック中に iTunesなどのツールから持ち出すことができない
- データ保護属性のついたファイルは暗号化される
- 対象機種:iPhone 4, iPhone 3GS, iPod touch (3rd generation or later), and all iPad models
※上記は若干自信が無いところもあるので違っていたら是非教えて下さい。
データ保護機能を有効にするにはパスコードロックが有効になっている必要があり、有効の場合はその旨メッセージが表示される。
※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 *)applicationUIApplicationDelegate 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からのバージョンアップの場合はデバイスの復元が必要。