web-dev-qa-db-ja.com

iPhoneユーザーが現在パスコードセットと暗号化を有効にしているかどうかを確認するにはどうすればよいですか?

データを暗号化する必要があるiPhoneアプリケーションを書いています。 NSFileProtectionComplete属性を設定して、ファイルの暗号化をオンにする方法を学びました。また、iPhoneのバージョンをチェックしてiOS4.0以降を実行していることを確認する方法も知っています。

しかし、ユーザーがパスコードを選択しておらず、[設定]> [一般]> [パスワードロック]画面でデータ保護を特に有効にしていない場合、データは実際にはまったく保護されていないことに気付きました。

警告をポップアップして、パスコードを有効にしてデータ保護をオンにする必要があることをユーザーに伝え(4より前のiPhoneではバックアップと復元が必要です)、パスコードがない場合はアプリケーションを終了しますデータ保護が有効になっています。とにかく、これらの設定の状態を知ることはできません。 UIApplicationの「protectedDataAvailable」など、私が見つけたすべてのAPIは、データ保護が無効になっている場合はすべて成功します。

39
Mike

免責事項:この回答はiOS 4.3.3まで有効でした

データ保護がオンになっている場合、新しく作成されたファイルにはデフォルトでnilNSFileProtectionKeyがあります。

データ保護がオフになっている場合、新しく作成されたファイルにはデフォルトでNSFileProtectionNoneNSFileProtectionKeyがあります。

したがって、次のコードでファイル保護の存在を検出できます。

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]];
18
Heath Borders

iOS 8(OS X Yosemite)は、ユーザーのデバイスにパスコードがあるかどうかを検出するために使用される新しいAPI /定数を導入しました。

kSecAttrAccessibleWhenPasscodeSetThisDeviceOnlyは、デバイスにパスコードが設定されているかどうかを検出するために使用できます。

フローは次のとおりです。

  1. その属性を設定して、キーチェーンに新しいアイテムを保存しようとしています
  2. 成功した場合は、パスコードが現在有効になっていることを示します
  3. パスワードが保存されない場合は、パスコードがないことを示しています
  4. アイテムがすでにキーチェーン上にある場合、パスコードが設定されていないように見えるため、「追加」が失敗するため、アイテムをクリーンアップします。

これを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に保存する方がはるかに良いでしょう。そのファイルを暗号化します。これは、ユーザーがパスコードを無効にすることを決定した場合は回復できません)。

13
owenfi

Appleは、ユーザーがパスコードを設定しているかどうかを判断する方法を提供していません。

アプリで暗号化が必要な場合は、信頼できる暗号化の実装を使用してファイルを暗号化および復号化し、ユーザーにパスコードの入力を求めるか、キーチェーンにキーを保存することを検討する必要があります。

3
Eric

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;
    }

} 
1
igraczech

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;
}
0
Vojta

スウィフト3

func isPasscodeEnabled() -> Bool {
    return LAContext().canEvaluatePolicy(LAPolicy.deviceOwnerAuthentica‌​tion, error:nil)
}

iOS9以降が必要です。

0
zaph