philm-iOS-wiki
  • 介绍
  • 网络层
    • 说明
  • UI
    • 说明
    • 在ios7以前使用ColorSpace的坑
    • UITableView偏移异常问题
    • drawRect时单独设置文字阴影无效
    • Xcode9下相册访问权限问题
    • 避免同时点击多个Button
    • scroll上的button延迟响应问题
    • uibutton触发边界事件
    • ios 11 上tableview 改动
    • YYImage 显示指定区域的图片
  • 数据持久化
    • 说明
  • 其它
    • 取消延迟执行之坑
    • NSString 转换 float 的精度问题
  • 每周阅读
    • 目录
    • 深入思考NSNotification
    • gitBook使用小助手
    • iOS App签名的原理
    • 响应链
    • iOS10跳转系统到设置页
    • SDWebImage下载高清图内存问题
    • iOS圆角避免离屏渲染
    • 常用的延时调用
    • iOS 神经网络
    • SDWebImage缓存策略
    • 3Dtouch
    • 为什么 Objective-C 对象存储在堆上而不是栈上
    • 深入浅出理解视频编码H264结构
    • CATextLayer学习
    • cocoaPods
    • 任意网站支持RSS
    • Metal简介
    • 动态更改icon
    • CAReplicatorLayer
    • 增加点击间隔
    • 勒索病毒当道的时代
    • iOS常用宏定义
    • Metal实现YUV转RGB渲染视频
    • 获取当前下载的app及下载进度
    • OpenGL ES 三种类型修饰 uniform attribute varying
    • 技术部门引入OKR
    • 基于runloop的线程保活、销毁与通信
    • 深入理解哈希表
    • TOLL-FREE BRIDGING 和 UNMANAGED
    • 开发者能拿到的标识符
    • Swift自定义LOG
    • 系统通知整理
    • iOS 中的 imageIO 与 image 解码
    • CGImageRef基本介绍及方法说明
    • Swift 3.0 语法
    • webview加载部分网页
    • 在CAAnimation中暂停动画
    • 把代码迁移到协调器上
    • ios11API更新整理
    • 非越狱iOS设备的远程控制实现原理
    • 关于本地化
    • swift命名空间
    • CoreML与coremltools体验
    • 力学动画
    • Swift 4官方文档中文版: The Basic(上)
    • swift 中的KVO用法
    • GPUImage的图像形变设计(简单形变部分)
    • iOS响应式架构
    • 移动端图片上传旋转、压缩的解决方案
    • AVFoundation使用指南AVAssert使用
    • 过渡动画
    • 谈谈 MVX 中的 Model
    • AVFoundation编程-AVPlayer使用
    • GPUImage的图像形变设计(复杂形变部分)
    • What's New in LLVM 9
    • ios的事件机制
    • GPUImage源码解读(一)
    • GPUImage源码解读(二)
    • iOS 启动优化
    • 模块化 Swift 中的状态
    • swift中的let和var背后的编程模式
    • Swift Runtime动态性分析
    • RAC下的响应式编程
    • GPUImage源码解读(三)
    • 如何准确判断webView是否加载完成
    • NSObject的+load和+initialize详解
    • ios8以后设置启动图
    • GPUImage源码解读(四)
    • Swift自动闭包
    • IOS11新特性
    • GPUImage源码解读(五)
    • 理解 OC 内部的消息调用、消息转发、类和对象
    • 修饰符
    • IOS 切面统计事件解耦
    • GPUImage源码解读(六)
    • CoreImage介绍
    • 影响Core Animation性能的原因
    • Instruments中的动画工具选项介绍
    • GPUImage源码解读(七)
    • Xcode 7新的特性Lightweight Generics 轻量级泛型与__kindof修饰符
    • GPUImage源码解读(八)
    • Core Image之自定 Filter
    • iOS通用链接
    • 谈nonatomic非线程安全问题
    • 深拷贝与浅拷贝
    • CIKernel 介绍
    • iOS11适配
    • GPUImage源码解读(九)
    • CVPixelBufferCreate使用的坑
    • ios一窥并发底层
    • ARKit进阶:物理世界
    • ARKit的工作原理及流程介绍
    • UI线程卡顿监控
    • FBKVOController使用
    • GPUImage源码解读(十)
    • WKWebView在ios11崩溃问题解决方法
    • 微信iOS SQLite源码优化实践
    • HEIF 和 HEVC 研究
    • 谈谈 iOS 中图片的解压缩
    • 提升 iOS 开发效率! Xcode 9 内置模拟器的9个技巧
    • ObjC和JavaScript的交互,在恰当的时机注入对象
    • iOS数据保护
    • iOS11中网络层的一些变化(Session707&709脱水版)
    • GPUImage源码解读(十一)
    • 一种避免 iOS 内存碎片的方法
    • pods的原理
    • GPUImage源码解读(十二)
    • GPUImage源码解读(十三)
    • iOS 11 Layout的新特性
    • iOS应用瘦身方法思路整理
    • GPUImage源码解读(十四)
    • CAEmitterLayer属性介绍
    • 浅析移动蜂窝网络的特点及其省电方案
    • 如何在 table view 中添加 3D Touch Peek & Pop 功能
    • iOS中锁的介绍与使用
    • NSLog效率低下的原因及尝试lldb断点打印Log
    • GPUImage源码解读(十五)
    • GPUImage源码解读(十六)
    • CADisplayLink
    • GPUImage源码解读(十七)
    • CADisplayLink
    • 老生常谈category增加属性的几种操作
    • 30行代码演示dispatch_once死锁
    • GPUImage源码解读(十八)
    • YYImage设计思路
    • GPUImage源码解读(十九)
    • 深入理解Tagged Pointer
    • iOS 11:WKWebView内容过滤规则详解
    • Swift语法对编译速度的影响
    • GPUImage源码解读(二十)
    • GPUImage源码解读(二十一)
    • iOS App间常用的五种通信方式
    • YYCache深入学习
    • 冲顶大会插件
    • iOS高性能图片架构与设计
    • YUV颜色编码解析
    • iOS传感器:App前后台切换后,获取敏感信息使用touch ID进行校验
    • GPUImage源码解读(二十二)
    • GPUImage源码解读(二十三)
    • 从零开始的机器学习 - Machine Learning(一)
    • 从零开始的机器学习 - Machine Learning(二)
    • GPUImage源码解读(二十四)
    • Objective-C消息转发机制
    • iOS 程序 main 函数之前发生了什么
    • MMKV--基于 mmap 的 iOS 高性能通用 key-value 组件
    • Objective-C 消息发送与转发机制原理
    • 谈Objective-C block的实现
    • GPUImage源码解读(二十五)
    • podfile语法
    • 轻量级低风险 iOS 热更新方案
    • 使用objection来模块化开发iOS项目
    • swift 中delegate的使用注意
    • 使用appledoc自动生成api文档
    • UITextChecker的使用
    • ARKit 如何给SCNNode贴Gif图片
    • Unity与iOS平台交互和原生插件开发
    • SceneKit编程珠玑
Powered by GitBook
On this page
  • 钥匙串(keychain)
  • 使用范例
  • 保护属性
  • iCloud同步
  • 数据保护
  • 文件保护过程:
  • DataProtectionClass 权限
  • 使用范例
  • 保护等级
  • CommonCrypto加密
  • Touch ID
  • 使用范例
  • Touch ID 与普通密码相比的安全性
  • Touch ID 指纹识别的原理
  • Secure Enclave
  1. 每周阅读

iOS数据保护

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

钥匙串(keychain)

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

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

作用

1.备份

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

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

2.共享

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

使用范例

常用方法:

方法

描述

SecItemAdd

添加数据

SecItemDelete

删除数据

SecItemUpdate

修改数据

SecItemCopyMatching

查找数据

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

保护属性

密码属性

项目类

描述

kSecClassGenericPassword

普通密码

kSecClassInternetPassword

专门用于互联网服务的密码

kSecClassCertificate

加密证书

kSecClassKey

加密密钥

kSecClassIdentity

一个密钥对(包括公共证书和私钥)

钥匙串保护属性:

钥匙串保护属性

含义

kSecAttrAccessibleAfterFirstUnlock

开机之后密钥不可用,直到用户首次输入开机密码

kSecAttrAccessibleAlways

密钥在设备开机后依旧可用.在iOS9中已经废弃

kSecAttrAccessibleAlwaysThisDeviceOnly

密钥始终可用,但无法迁移到其他设备

kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly

作用同上

kSecAttrAccessibleWhenUnlocked

只要解锁过设备,则密钥保持可用状态

kSecAttrAccessibleWhenUnlockedThisDeviceOnly

作用同上

kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly

作用同上,只有用户设置密码密钥才可用

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

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

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

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

iCloud同步

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

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

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

数据保护

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

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

文件保护过程:

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

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

3.用文件私钥和Protected Unless Open等级公钥计算出一个共享密码。

4.用共享密码 SHA-1散列值加密文件密钥。

5.加密过的文件密钥会存储在文件的元数据中,元数据中还有文件的公钥。

6.系统丢弃文件私钥。

7.关闭文件时从内存中删除未加密的文件密钥。

8.需要再次打开文件时,用Protected Unless Open等级私钥和文件公钥计算共享密码。

9.计算共享密码的SHA-1散列值,把它当作解密文件的密钥。

DataProtectionClass 权限

如果你的应用在设备进入后台或锁定时不需要写入或读取文件,那你就可以在工程中配置一个NSFileProtectionComplete值来添加权限。这将确保所有受保护的文件数据只能在设备解锁时访问,相当于为所有的应用文件都设置kSecAttrAccessibleWhenUnlocked选项。

从Xcode5开始,新工程会默认启用数据保护权限,但是一些旧工程并不会自动开启。

使用范例

  • NSDataWritingFileProtectionComplete

NSData *data = [self generateData];
NSError *error = nil;
NSString *path = [NSString stringWithFormat:@"%@_demo.pdf", NSTemporaryDirectory()];
[data writeToFile:path options:NSDataWritingFileProtectionComplete error:&error];
if (error) {
NSLog(@"%@", error);
}
  • NSFileProtectionComplete

NSString *path = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject] stringByAppendingPathComponent:@"demo.txt"];
NSError *error = nil;
NSDictionary *attr = [NSDictionary dictionaryWithObject:NSFileProtectionComplete forKey:NSFileProtectionKey];
[[NSFileManager defaultManager] setAttributes:attr ofItemAtPath:path error:&error];
if (error) {
NSLog(@"%@", error);
}
  • SQLITE_OPEN_FILEPROTECTION_COMPLETEUNLESSOPEN

NSString *path = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject] stringByAppendingPathComponent:@"demo.sqlite"];
sqlite3 *handle = NULL;
sqlite3_open_v2([path UTF8String],
&handle,
SQLITE_OPEN_CREATE | SQLITE_OPEN_READWRITE | SQLITE_OPEN_FILEPROTECTION_COMPLETEUNLESSOPEN,
NULL);

保护等级

CompleteUntilFirstUserAuthentication

这个保护等级是iOS5开始使用的默认值。主要用来防御一些需要重启的攻击。

Complete

官方称这是目前最安全的文件保护等级。在这种情况下,锁屏之后系统就会删除内存中的层级密钥,并把文件改为不可读。

具体使用的范例前文可见。

使用Complete保护之前,考虑是否合适。如果你的应用进程需要持续写/读一个文件,那这种保护模式就不合适了。

CompleteUnlessOpen

如果一个文件当前被一个应用打开,那会暂时禁用该文件的保护。它会确保打开的文件在设备被锁定时依然能够写入,并且允许新建文件到磁盘。不过这个等级保护的文件在锁屏时无法打开,除非锁屏时就已经提前打开。

CommonCrypto加密

CommonCrypto是Apple官方提供的一套加密框架。在使用这套方案时,主要注意以下几个问题即可。

1.避免弱算法

众所周知,DES就是一种很典型的弱算法。如果你使用弱算法,那应用就很容易遭受密码攻击和暴力破解。

我们在实际使用时,可以考虑使用AES算法、MD5算法或MD5加盐算法等。

2.破碎的熵

我们通常会使用rand方法来产生随机数,但是系统的这个随机函数并不是真正的随机。它产生的随机数符合正态分布,我们可以使用官方提供的方法SecRandomCopyBytes获得。也可以自己使用算法产生随机数。

3.弱密钥

开发者经常将用户的密码当作加密密钥,尤其在移动设备上,这将导致一个非常脆弱、低熵加密的密钥。

那么正确的姿势是使用CCKeyDerivationPBKDF方法来生成。

Touch ID

使用范例

#import <LocalAuthentication/LocalAuthentication.h>

LAContext *context = [[LAContext alloc] init];
NSError *error = nil;
NSString *reason = @"We use this to verify your identify";
if ([context canEvaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics error:&error]) {
[context evaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics localizedReason:reason reply:^(BOOL success, NSError * _Nullable error) {
if (success) {
NSLog(@"Hello, that's your finger!");
} else {
NSLog(@"Couldn't read your fingerprint. Falling back to PIN or somthing.");
}
}];
} else {
NSLog(@"Error : %@ %@", error, [error userInfo]);
}

Touch ID 与普通密码相比的安全性

一个人的指纹是世界上最好的密码之一。它独一无二,高度特异,并且无需记忆。它会伴随人终生,所以你可以一直使用同一个指纹密码。

比起简单的4位数字锁屏密码一万分之一的破解可能性,能找到同样单个指纹的人的概率为五万分之一,这是非常安全的,比起前者可以不断尝试,Touch ID 指纹的硬性破解几率几乎没有,因为不可能有五万多个人排队来一一解锁你的设备,况且 Touch ID 会拒绝第五次以后的尝试而要求输入密码。所以我们可以添加更加复杂的密码而不是简单的数字密码来进一步提高安全性。

Touch ID 指纹识别的原理

在如此快速的识别过程中,Home 按钮上的一圈金属环起到了感应手指的功能,通知 Touch ID 来读取指纹。Touch ID 置于该按钮中,传感器的厚度只有 170 微米,拥有 500 ppi 高分辨率,可读取极小的指纹细节。传感器可从皮肤皮下层指纹的一些小部分拍摄高分辨率图像,然后会分析该信息,根据三种基本指纹类型(弧形纹、箕形纹或斗形纹)将指纹分类,它会绘制比人眼不可见的纹路细节,确保指纹读取准确无误。

Touch ID 可从 360 度方向读取指纹,随后创建指纹的某种数学表达式,并将其与已注册的数据进行比较,以确定是否匹配。如果匹配,便可替代密码来解锁设备或通过某个令牌。

此外,根据苹果官方的描述,Touch ID 会向以注册的指纹数据里持续的添加新的特征数据,随着时间的推移,这能不断提高匹配准确度,也能进一步的提高安全性。

除了上述的内容,苹果还加入可以一些有效的手段来进一步确保安全,例如设备再重新启动或者保持锁定 48 小时后,就只能通过密码而不是指纹来解锁设备。

Secure Enclave

Apple声明指纹的图案不会被保存,存储于设备中的是指纹特征的数学表达式,而且从这些表达式不可能直接反推出指纹图像

指纹的验证运算独立于主要处理器芯片(例如 A7、A8),芯片内有称为 “Secure Enclave” 的高级安全架构,专用于密码指纹数据,并使用 Secure Enclave 的专用密钥加密,以及每次启动以随机的 UID 进行管理。指纹数据仅能被 Secure Enclave 处理和使用,正因为此架构独立于其它设备部件,仅有 Touch ID 使用它,且不能将它用于匹配其他指纹数据库,所以存储的指纹数据不会由 iOS 或其他应用访问,也不会被存储到 Apple 服务器或备份到 iCloud 等地方。

从硬件上来看指纹特征数据尚不能被 “Secure Enclave” 和 Touch ID 以外的部件访问到,并且两者芯片互相隔离开来,所有软件都没有权利获得指纹数据,仅能得到指纹是否错误的回馈。因为架构的核心秘密仅仅只有苹果公司知道,所以目前几乎没有第三方软件可以访问到这部分敏感信息。

PreviousObjC和JavaScript的交互,在恰当的时机注入对象NextiOS11中网络层的一些变化(Session707&709脱水版)

Last updated 7 years ago