【问题标题】:How can I find out if the iPhone user currently has a passcode set and encryption enabled?如何确定 iPhone 用户当前是否设置了密码并启用了加密?
【发布时间】:2011-04-17 21:55:28
【问题描述】:

我正在编写一个需要对其数据进行加密的 iPhone 应用程序。我已经学会了如何通过设置 NSFileProtectionComplete 属性来打开文件加密。我也知道如何检查 iPhone 版本以确保它们运行的​​是 iOS 4.0 或更高版本。

我已经意识到,如果用户没有选择密码并且没有在“设置”>“常规”>“密码锁定”屏幕上专门启用数据保护,那么数据实际上根本没有受到保护。

我想弹出一个警告并告诉用户他们必须启用密码并打开数据保护(这需要在 4 之前的 iPhone 上进行备份和恢复),然后如果他们没有退出应用程序启用密码和数据保护。尽管如此,我还是无法弄清楚这些设置的状态。如果禁用数据保护,我发现的所有 API,例如 UIApplication 中的“protectedDataAvailable”都可以成功通过。

【问题讨论】:

  • 我会假设密码锁与正在运行的应用程序无关,所以我猜它不是 SDK 的一部分。如果是,它可能是这个 API 的一部分:developer.apple.com/library/ios/#documentation/uikit/reference/…
  • 它与正在运行的应用程序极为相关,因为如果没有密码,您的数据在设备上就不会受到保护。如果无法判断您的数据是否受到保护,那将是 Apple 的巨大疏忽。它使新的 iOS 4 加密对于通过应用商店销售的大多数企业应用程序几乎毫无用处。
  • 大多数企业会(应该)将部署配置文件推送到公司的所有 iPhone 以要求输入密码。这不是程序问题,这是管理问题。你真的想弹出一个警告告诉用户打开他的密码吗?或者更好的是,除非它打开,否则拒绝运行?用户通常不愿意被告知如何使用他们的设备。
  • 您不能为通过应用商店销售的应用提供部署配置文件。此应用程序不适用于公司管理的电话。当前的安全模型让用户感到困惑,他们很难判断他们的数据是否安全。例如,如果您有一部 iPhone 3GS,即使您已升级到 iOS 4 并打开了密码,您的数据也不会受到保护,除非您完成了完整的备份、擦除和恢复以重新格式化文件系统。唯一的判断方法是隐藏在设置菜单中的一小行文本。如果未打开,则不会显示任何内容。
  • 此外,法律法规 (HIPAA) 要求数据受到保护。如果未启用数据保护,用户将无法运行应用程序。这根本不取决于用户,他是否喜欢它。似乎不幸的替代方案是从头开始实施加密。

标签: iphone core-data encryption ios4 data-protection


【解决方案1】:

免责声明:此答案在 ios 4.3.3 之前有效

如果开启数据保护,新创建的文件默认有nilNSFileProtectionKey

如果关闭数据保护,新创建的文件将默认具有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]];

【讨论】:

  • 感谢您花时间实际阅读问题。很好的答案
  • 你确定这有效吗??我目前正在使用 iPad (4.3.5) 进行测试,属性始终为 NSFIleProtectionNone...“数据保护已启用”显示在 keycode-settings 中...。
  • 我用 4.3.3 对此进行了测试,效果很好。如果这对您不起作用,请随时以不同的答案发布您的代码。
  • 这个方法在我的 iPad 5.0.1 上也不起作用,总是返回 true。
  • 看起来这种行为在 iOS 5.0/5.0.1 上可能已经改变。使用示例代码,无论 iPad2 上的密码设置如何,我总是得到 NSFileProtectionNone。
【解决方案2】:

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

} 

【讨论】:

  • 顺便说一句,查找朋友应用程序具有此功能。只有当设备没有密码保护时,它才会要求重新输入密码。
  • 嗨,我已经检查了链接,但每次它返回相同的值 NSFileProtectionKey = NSFileProtectionNone;
【解决方案3】:

Apple 没有提供方法来确定用户是否设置了密码。

如果您的应用需要加密,您应该考虑使用受信任的加密实现对文件进行加密和解密,并提示用户输入密码或将密钥存储在钥匙串中。

【讨论】:

    【解决方案4】:

    iOS 8 (OS X Yosemite) 引入了一个新的 API/常量,用于检测用户的设备是否有密码。

    kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly 可用于检测设备上是否设置了密码。

    流程是:

    1. 尝试使用该属性集在钥匙串上保存新项目
    2. 如果成功则表明当前启用了密码
    3. 如果密码没有保存,说明没有密码
    4. 清理项目,因为如果它已经在钥匙串上,它会导致“添加”失败,看起来好像没有设置密码

    我在我的 iPhone 5S 上对此进行了测试,首先它返回了true,然后我在设置中禁用了密码,它返回了false。最后,我重新启用了密码,它返回true。以前的操作系统版本将返回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;
    

    附:正如 Apple 在 WWDC 视频中指出的那样(711 Keychain 和 Touch ID 身份验证),他们选择不故意通过 API 直接提供密码状态,以防止应用程序进入不应出现的情况(即“这个设备有密码吗?好的,太好了,我会以纯文本格式存储这个私人信息”。最好创建一个加密密钥,将其存储在kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly 下并加密该文件,这将如果用户决定禁用他们的密码,则无法恢复)。

    【讨论】:

    • 这是苹果认可的吗?
    • 我从未提交过使用它的应用程序,但所有功能都已记录在案(没有私有 API),所以我认为会的。
    【解决方案5】:

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

    【讨论】:

      【解决方案6】:

      斯威夫特 3

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

      需要 iOS 9 或更高版本。

      【讨论】:

        猜你喜欢
        • 2011-10-28
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-07-13
        • 2012-01-28
        相关资源
        最近更新 更多