データを暗号化する必要があるiPhoneアプリケーションを書いています。 NSFileProtectionComplete属性を設定して、ファイルの暗号化をオンにする方法を学びました。また、iPhoneのバージョンをチェックしてiOS4.0以降を実行していることを確認する方法も知っています。
しかし、ユーザーがパスコードを選択しておらず、[設定]> [一般]> [パスワードロック]画面でデータ保護を特に有効にしていない場合、データは実際にはまったく保護されていないことに気付きました。
警告をポップアップして、パスコードを有効にしてデータ保護をオンにする必要があることをユーザーに伝え(4より前のiPhoneではバックアップと復元が必要です)、パスコードがない場合はアプリケーションを終了しますデータ保護が有効になっています。とにかく、これらの設定の状態を知ることはできません。 UIApplicationの「protectedDataAvailable」など、私が見つけたすべてのAPIは、データ保護が無効になっている場合はすべて成功します。
免責事項:この回答はiOS 4.3.3まで有効でした
データ保護がオンになっている場合、新しく作成されたファイルにはデフォルトでnil
NSFileProtectionKey
があります。
データ保護がオフになっている場合、新しく作成されたファイルにはデフォルトでNSFileProtectionNone
NSFileProtectionKey
があります。
したがって、次のコードでファイル保護の存在を検出できます。
NSString *tmpDirectoryPath =
[NSHomeDirectory() stringByAppendingPathComponent:@"tmp"];
NSString *testFilePath =
[tmpDirectoryPath stringByAppendingPathComponent:@"testFile"];
[@"" writeToFile:testFilePath
atomically:YES
encoding:NSUTF8StringEncoding
error:NULL]; // obviously, do better error handling
NSDictionary *testFileAttributes =
[[NSFileManager defaultManager] attributesOfItemAtPath:testFile1Path
error:NULL];
BOOL fileProtectionEnabled =
[NSFileProtectionNone isEqualToString:[testFile1Attributes objectForKey:NSFileProtectionKey]];
iOS 8(OS X Yosemite)は、ユーザーのデバイスにパスコードがあるかどうかを検出するために使用される新しいAPI /定数を導入しました。
kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly
は、デバイスにパスコードが設定されているかどうかを検出するために使用できます。
フローは次のとおりです。
これをiPhone5Sでテストしました。最初にtrue
が返され、次に設定でパスコードが無効になり、false
が返されました。最後に、パスコードを再度有効にすると、true
が返されます。以前のOSバージョンはfalse
を返します。コードはシミュレーターで動作し、OS Xパスワードが設定されたマシンでtrue
を返します(代替のOS Xシナリオはテストしていません)。
こちらのサンプルプロジェクトもご覧ください: https://github.com/project-imas/passcode-check/pull/5
最後に、私の知る限り、iOS 8にはデータ保護を無効にする設定がないので、暗号化を保証するために必要なのはこれだけだと思います。
BOOL isAPIAvailable = (&kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly != NULL);
// Not available prior to iOS 8 - safe to return false rather than crashing
if(isAPIAvailable) {
// From http://Pastebin.com/T9YwEjnL
NSData* secret = [@"Device has passcode set?" dataUsingEncoding:NSUTF8StringEncoding];
NSDictionary *attributes = @{
(__bridge id)kSecClass: (__bridge id)kSecClassGenericPassword,
(__bridge id)kSecAttrService: @"LocalDeviceServices",
(__bridge id)kSecAttrAccount: @"NoAccount",
(__bridge id)kSecValueData: secret,
(__bridge id)kSecAttrAccessible: (__bridge id)kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly
};
// Original code claimed to check if the item was already on the keychain
// but in reality you can't add duplicates so this will fail with errSecDuplicateItem
// if the item is already on the keychain (which could throw off our check if
// kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly was not set)
OSStatus status = SecItemAdd((__bridge CFDictionaryRef)attributes, NULL);
if (status == errSecSuccess) { // item added okay, passcode has been set
NSDictionary *query = @{
(__bridge id)kSecClass: (__bridge id)kSecClassGenericPassword,
(__bridge id)kSecAttrService: @"LocalDeviceServices",
(__bridge id)kSecAttrAccount: @"NoAccount"
};
status = SecItemDelete((__bridge CFDictionaryRef)query);
return true;
}
// errSecDecode seems to be the error thrown on a device with no passcode set
if (status == errSecDecode) {
return false;
}
}
return false;
P.S. Appleはこれを紹介するWWDCビデオ(711キーチェーンとTouch IDによる認証)で指摘しているように、アプリを防ぐために、意図的にAPIを介してパスコードステータスを直接利用できないようにすることを選択しましたあるべきではない状況に陥ることから(つまり、「このデバイスにはパスコードがありますか?わかりました。この個人情報をプレーンテキストで保存します」。暗号化キーを作成してkSecAttrAccessibleWhenPasscodeSetThisDeviceOnly
に保存する方がはるかに良いでしょう。そのファイルを暗号化します。これは、ユーザーがパスコードを無効にすることを決定した場合は回復できません)。
Appleは、ユーザーがパスコードを設定しているかどうかを判断する方法を提供していません。
アプリで暗号化が必要な場合は、信頼できる暗号化の実装を使用してファイルを暗号化および復号化し、ユーザーにパスコードの入力を求めるか、キーチェーンにキーを保存することを検討する必要があります。
NSDataWritingAtomicまたはNSDataWritingFileProtectionCompleteに関係なく、結果は常に同じです。奇妙な振る舞い、ここにコードがあります:
BOOL expandTilde = YES;
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, expandTilde);
NSString *filePath;
filePath = [[paths lastObject] stringByAppendingPathComponent:@"passcode-check"];
NSMutableData *testData;
testData = [NSMutableData dataWithLength:1024];
NSLog(@"Attempt to write data of length %u file: %@", [testData length], filePath);
NSError *error = nil;
if (![testData writeToFile:filePath options:NSDataWritingAtomic error:&error]) {
NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
return NO;
} else {
NSLog(@"File write successful.");
error = nil;
NSDictionary *testFileAttributes = [[NSFileManager defaultManager] attributesOfItemAtPath:filePath error:&error];
NSLog(@"Getting attributes: %@", testFileAttributes);
if ([NSFileProtectionComplete isEqualToString:[testFileAttributes objectForKey:NSFileProtectionKey]]) {
error = nil;
[[NSFileManager defaultManager] removeItemAtPath:filePath error:&error];
// passcode disabled
return YES;
} else {
error = nil;
[[NSFileManager defaultManager] removeItemAtPath:filePath error:&error];
return NO;
}
}
iOS 9なので、LocalAuthenticationフレームワークにフラグLAPolicyDeviceOwnerAuthenticationがあります。
+ (BOOL)isPasscodeEnabled
{
NSError *error = nil;
LAContext *context = [[LAContext alloc] init];
BOOL passcodeEnabled = [context canEvaluatePolicy:LAPolicyDeviceOwnerAuthentication error:&error];
if(passcodeEnabled) {
return YES;
}
return NO;
}
スウィフト3
func isPasscodeEnabled() -> Bool {
return LAContext().canEvaluatePolicy(LAPolicy.deviceOwnerAuthentication, error:nil)
}
iOS9以降が必要です。