B0ee8da93499c43faa2f7f64e4bf9680
聊聊 iOS 数据保护

文件系统中的文件、keychain中的项,都是加密存储的。当用户解锁设备后,系统通过UDID密钥和用户设定的密码生成一个用于解密的密码密钥,存放在内存中,直到设备再次被锁,开发者可以通过Data Protection API 来设定文件系统中的文件、keychain中的项应该何时被解密。这个就是数据保护的内容。

钥匙串(keychain)

keychain服务提供了一种安全的保存私密信息(密码,序列号,私钥,证书等)的方式,每个iOS程序都有一个独立的keychain存储。相对于NSUserDefaults、文件保存等一般方式,keychain保存更为安全,而且keychain里保存的信息不会因APP被删除而丢失。

基于钥匙串的特点,我们在使用时对于新增的数据项目,要注意数据删除的时机。还有一点需要注意的就是,保护属性的指定。如果没有指定保护属性,这将被视为严重的安全漏洞。

作用

备份

当我们备份设备数据时,系统会将用户数据保存在相应的钥匙串中,并依照相应的安全策略保存。主要分成两类:加密不加密

两者的主要区别在于恢复数据时的范围不同。加密备份的数据可以恢复到任何设备上(ThisDeviceOnly指定的项目除外),而不加密的备份则只能恢复至同一台设备。

共享

keychain支持在多个应用之间共享数据。但现实开发中UIPasteboardNameFind的使用代替了keychain。不过好在Apple在iOS11中已经废弃它。看来Apple想规范开发者对于用户敏感数据的共享使用,同时提醒大家使用更加安全的方式存储用户敏感数据。

使用范例

SecItemAdd

NSMutableDictionary *dict = [NSMutableDictionary dictionary];
NSData *passwordData = [@"myPassword" dataUsingEncoding:NSUTF8StringEncoding];

[dict setObject:(__bridge id)kSecClassGenericPassword forKey:(__bridge id)kSecClass];
[dict setObject:@"Conglomco" forKey:(__bridge id)kSecAttrLabel];
[dict setObject:@"This is your password for the Conglomco service." forKey:(__bridge id)kSecAttrDescription];
[dict setObject:@"chars" forKey:(__bridge id)kSecAttrAccount];
[dict setObject:@"cn.zaker.keychain.sample" forKey:(__bridge id)kSecAttrService];
[dict setObject:passwordData forKey:(__bridge id)kSecValueData];
[dict setObject:(__bridge id)kSecAttrAccessibleWhenUnlocked forKey:(__bridge id)kSecAttrAccessible];

OSStatus error = SecItemAdd((__bridge CFDictionaryRef)dict, NULL);
if (error == errSecSuccess) {
    NSLog(@"Yay");
}

SecItemDelete

NSMutableDictionary *dict = [NSMutableDictionary dictionary];

[dict setObject:(__bridge id)kSecClassGenericPassword forKey:(__bridge id)kSecClass];
[dict setObject:@"chars" forKey:(__bridge id)kSecAttrAccount];
[dict setObject:@"cn.zaker.keychain.sample" forKey:(__bridge id)kSecAttrService];

OSStatus error = SecItemDelete((__bridge CFDictionaryRef)dict);
if (error == errSecSuccess) {
    NSLog(@"Yay");
}

SecItemUpdate

NSMutableDictionary *dict = [NSMutableDictionary dictionary];
NSData *newPasswordData = [@"newMyPassword" dataUsingEncoding:NSUTF8StringEncoding];

[dict setObject:(__bridge id)kSecClassGenericPassword forKey:(__bridge id)kSecClass];
[dict setObject:@"chars" forKey:(__bridge id)kSecAttrAccount];
[dict setObject:@"cn.zaker.keychain.sample" forKey:(__bridge id)kSecAttrService];

NSDictionary *updatedAttribute = [NSDictionary dictionaryWithObject:newPasswordData forKey:(__bridge id)kSecValueData];

OSStatus error = SecItemUpdate((__bridge CFDictionaryRef)dict, (__bridge CFDictionaryRef)updatedAttribute);
if (error == errSecSuccess) {
    NSLog(@"Yay");
}

SecItemCopyMatching

NSMutableDictionary *dict = [NSMutableDictionary dictionary];

[dict setObject:(__bridge id)kSecClassGenericPassword forKey:(__bridge id)kSecClass];
[dict setObject:@"chars" forKey:(__bridge id)kSecAttrAccount];
[dict setObject:@"cn.zaker.keychain.sample" forKey:(__bridge id)kSecAttrService];
[dict setObject:(id)kCFBooleanTrue forKey:(__bridge id)kSecReturnAttributes];

NSDictionary *result = nil;
OSStatus error = SecItemCopyMatching((__bridge CFDictionaryRef)dict, (void *)&result);
if (error == errSecSuccess) {
    NSLog(@"Yay %@", result);
}

常用方法

方法 描述
SecItemAdd 添加数据
SecItemDelete 删除数据
SecItemUpdate 修改数据
SecItemCopyMatching 查找数据

保护属性

钥匙串保护属性 含义
kSecAttrAccessibleAfterFirstUnlock 开机之后密钥不可用,直到用户首次输入密码
kSecAttrAccessibleAlways 密钥在设备开机后依旧可用。在iOS9中已经废弃
kSecAttrAccessibleAlwaysThisDeviceOnly 密钥始终可用,但无法迁移到其他设备
kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly 作用同上
kSecAttrAccessibleWhenUnlocked 只要解锁过设备,则密钥保持可用状态
kSecAttrAccessibleWhenUnlockedThisDeviceOnly 作用同上
kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly 作用同上,只有用户设置密码密钥才可用

kSecAttrAccessibleAlways 将会引入一个很明显的安全问题,因为此种保护属性,只要有人窃取了你的设备,他们就能读取钥匙串的内容。

kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly 这个属性可以完美的解决上述的安全问题。因为越狱时通常需要重启设备。

kSecAttrAccessibleWhenUnlocked 这个属性要求攻击者必须知道用户密码才能提取隐私数据。它很适合做默认属性值。

kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly 是iOS8新增的保护属性。这个属性要求在使用时用户设定了密码,否则使用就失败。

基本用法

项目类 描述
kSecClassGenericPassword 普通密码
kSecClassInternetPassword 专门用于互联网服务的密码
kSecClassCertificate 加密证书
kSecClassKey 加密密钥
kSecClassIdentity 一个密钥对(包括公共证书和私钥)

iCloud同步

这是iOS7引入的一种新机制,可以把钥匙串项目同步到iCloud,允许用户在多个设备之间共享钥匙串项目。

默认情况下,应用程序创建的钥匙串项目会禁用这个机制,但可以把kSecAttrSynchronizable设置为true来启用。

另外请注意,使用此选项时无法指定不兼容的kSecAttrAccessible属性。例如,指定kSecAttrAccessibleWhenUnlockedThisDeviceOnly不起作用,因为ThisDeviceOnly指定的项目不会备份,也不能同步到iCloud、笔记本电脑、台式机或其他同步位置。

数据保护

Apple 推出了数据保护API作为额外的保护层,它允许开发者指定文件解密密钥的生命周期。可以使用这个API控制文件的访问权限,与钥匙串项目中的kSecAttrAccessible 属性类似。

数据保护API使用用户密码和层级密钥来加密保护文件的密钥,而当这些文件不能被访问时,会从内存中删除这个层级密钥。

文件保护过程

文件保护过程

文件保护过程

1.文件生成一个文件密钥来加密文件的内容。

2.生成一个额外的密钥对,用于生成文件公钥文件私钥

top Created with Sketch.