数据保护 API

18 数据保护 API

题外话

开篇先扯几句题外话,许多朋友都问我怎么不写防啊,我确实有点犹豫。 hackers 总是想象如果自己是开发者会怎么写,然后才能找到入手点。同理,开发者们也要想象自己是 hackers 会怎么做,才能采取相应的防御措施。然后,就是一场递归的博弈。

拿越狱检测这件事来说,起初大家只需判断有无安装 Cydia 就好了,hackers 们说好,那我就不安装 Cydia 也可以动手脚。开发者们又说,那你一定得用的上 MobileSubstrate bash ssh 吧,我去检测手机有没有安装这些工具。可是又有什么用呢?你判断什么我绕过去什么。

class-dump 大肆流行,函数符号都被暴露,开发者想尽办法藏起自己的敏感函数代码。hackers 们也知道 class-dump 的死穴在哪里,于是新的检索办法油然而生。也就说,当一个防御手段成为流行,它就不会再是个让 hackers 大骂“真特么费劲”的防御手段了。比如之前介绍的一个小技巧:内存数据擦除 ,hackers 知道开发者都去擦数据了,那我 hook memset 在你擦之前去读就好了。开发者说:我直接写硬盘上然后删除!hackers 说:难道你没听说过文件恢复?

图片 18.1 data-erase1

OK,贫的有点多了,本文介绍一下防御相关的话题—— iOS 的数据保护 API 。

数据保护 API

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

文件保护

1
2
3
4
5
6
7
8
9
10
11
12
/* 为filePath文件设置保护等级 */
NSDictionary *attributes = [NSDictionary dictionaryWithObject:NSFileProtectionComplete
                                                       forKey:NSFileProtectionKey];
[[NSFileManager defaultManager] setAttributes:attributes
                                 ofItemAtPath:filePath
                                        error:nil];

//文件保护等级属性列表  
NSFileProtectionNone                                    //文件未受保护,随时可以访问 (Default)  
NSFileProtectionComplete                                //文件受到保护,而且只有在设备未被锁定时才可访问  
NSFileProtectionCompleteUntilFirstUserAuthentication    //文件收到保护,直到设备启动且用户第一次输入密码  
NSFileProtectionCompleteUnlessOpen                      //文件受到保护,而且只有在设备未被锁定时才可打开,不过即便在设备被锁定时,已经打开的文件还是可以继续使用和写入

keychain 项保护

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/* 设置keychain项保护等级 */
NSDictionary *query = @{(__bridge id)kSecClass: (__bridge id)kSecClassGenericPassword,
                        (__bridge id)kSecAttrGeneric:@"MyItem",
                        (__bridge id)kSecAttrAccount:@"username",
                        (__bridge id)kSecValueData:@"password",
                        (__bridge id)kSecAttrService:[NSBundle mainBundle].bundleIdentifier,
                        (__bridge id)kSecAttrLabel:@"",
                        (__bridge id)kSecAttrDescription:@"",
                        (__bridge id)kSecAttrAccessible:(__bridge id)kSecAttrAccessibleWhenUnlocked};

OSStatus result = SecItemAdd((__bridge CFDictionaryRef)(query), NULL);

//keychain项保护等级列表  
kSecAttrAccessibleWhenUnlocked                          //keychain项受到保护,只有在设备未被锁定时才可以访问  
kSecAttrAccessibleAfterFirstUnlock                      //keychain项受到保护,直到设备启动并且用户第一次输入密码  
kSecAttrAccessibleAlways                                //keychain未受保护,任何时候都可以访问 (Default)  
kSecAttrAccessibleWhenUnlockedThisDeviceOnly            //keychain项受到保护,只有在设备未被锁定时才可以访问,而且不可以转移到其他设备  
kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly        //keychain项受到保护,直到设备启动并且用户第一次输入密码,而且不可以转移到其他设备  
kSecAttrAccessibleAlwaysThisDeviceOnly                  //keychain未受保护,任何时候都可以访问,但是不能转移到其他设备

应用实例

把一段信息 infoStrng 字符串写进文件,然后通过 Data Protection API 设置保护。

1
2
3
4
5
6
7
8
9
10
11
NSString *documentsPath =[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject];
NSString *filePath = [documentsPath stringByAppendingPathComponent:@"DataProtect"];
[infoString writeToFile:filePath
             atomically:YES
               encoding:NSUTF8StringEncoding
                  error:nil];
NSDictionary *attributes = [NSDictionary dictionaryWithObject:NSFileProtectionComplete
                                                       forKey:NSFileProtectionKey];
[[NSFileManager defaultManager] setAttributes:attributes
                                 ofItemAtPath:filePath
                                        error:nil];

设备锁屏(带密码保护)后,即使是越狱机,在 root 权限下 cat 读取那个文件信息也会被拒绝。

图片 18.2 data-erase2

Comments