一、iOS安全攻防
1.本地数据攻防
1.1 文件存储
每个App的文件都保存在一个沙盒目录中。每个沙盒都包含Documents、Library/Caches、Tmp、SystemData文件夹。
-Documents: 保存应⽤运行时生成的需要持久化的数据,iTunes同步设备时会备份该目录。
-tmp: 保存应⽤运行时所需的临时数据,使⽤完毕后再将相应的文件从该目录删除。应用没有运行时,系统也可能会清除该目录下的文件。iTunes同步设备时不会备份该目录。
-Library/Caches: 保存应用运行时⽣成的需要持久化的数据,iTunes同步设备时不会备份该目录。一般存储体积大、不需要备份的非重要数据,比如网络数据缓存存储到Caches下。
1.2 数据库
APP一般运用数据库存储一些数据量比较大,逻辑比较复杂的数据,方便我们使用和增加使用效率。数据库,一般存储在Documents路径下的特殊文件,格式一般为.db或者.sqlite,导出后使用特殊工具即可查看如DB Browser for sqlite 和 SQLiteStudio。
防止攻击者读取数据库的建议:
数据加密: 使用如AES256加密算法对数据进行安全加密后再存入数据库中。
优点:使用简单,无需第三方库支持。
缺点:每次存储都有加密解密过程,增加APP资源消耗。
整库加密: 可使用第三方的SQLite扩展库,对数据库进行整体的加密。如:SQLCipher,git地址,也可以使用pod,在podfile文件中添加:pod 'FMDB/SQLCipher'
1.3 App加固
目前市场上iOS常用的App加固工具有:Obfuscator-LLVM、网易易盾、顶象加固、360加固。以下是几种加固方式的对比:
1.4 类文件保护
文件保护可以分为静态混淆和动态保护。
- 静态混淆:类名、方法名、属性的混淆
使用 class-dump 导出的头文件很容易根据名字知道某个类,方法的作用,这时候需要对类名,方法名进行混淆。第一种方法:通过宏定义混淆,在 pch 文件中比如这样写 #define MyClass 89s343ss。网上有一个开源的库 ios-class-guard。第二种方法:二进制修改,修改可执行文件的 __obj_classname 和 objc_methname。也可以通过三方工具codeobscure等对整个项目添加混淆。
- 动态保护:反调试、注入检测、hook 检测,越狱检测、签名检测等
虽然静态混淆后不能猜到类名方法名的作用,但是如果采用动态的方式还是能知道某些方法的参数、以及加密后的数据等。这就是要防动态调试。
- 反调试:主要通过阻止调试去附加和检测调试器是否存在。主要有 ptrace、sysctl、syscall、ARM 等方式;
- 反反调试:过滤掉反调试机制;
- 反注入:检测当前有没有其它模块注入;
动态注入是利用了 iOS系统中 DYLD_INSERT_LIBRARIES这个环境变量,如果设置了DYLD_INSERT_LIBRARIES 环境变量,那么在程序运行时,动态链接器会先加载该环境变量所指定的动态库;也就是说,这个动态库的加载优先于任何其它的库,包括 libc。
我们可以在代码中通过判断环境变量来检测是不是被注入:char *env = getenv("DYLD_INSERT_LIBRARIES");如果方法返回非空,我们可以做一些上报之类的。
- hook 检测:检测是否被恶意的 hook 掉了;
当App的二进制格式不会被混淆的前提下,App加载的某个dylib或者编译时期某个framework的.a文件,其二进制肯定连续的,意思是在二进制文件中,某个dylib或者framework中符号肯定是相对固定的。譬如,对于上述这个例子,UIViewController这个CLASS的symbol的地址相对于-[UIViewController viewDidLoad]的IMP的地址偏移肯定是固定
- 完整性校验:load command、代码校验、重签名校验;
1.5 代码优化
1.5.1UIWebView XSS漏洞
XSS (cross-site script) 跨站脚本自1996年诞生以来,一直被OWASP(open web application security project) 评为十大安全漏洞中的第二威胁漏洞。也有黑客把XSS当做新型的“缓冲区溢出攻击”而JavaScript是新型的shellcode。2011年6月份,新浪微博爆发了XSS蠕虫攻击,仅持续16分钟,感染用户近33000个,危害十分严重。XSS最大的特点就是能注入恶意的代码到用户浏览器的网页上,从而达到劫持用户会话的目的。由于web应用程序对用户的输入过滤不严产生的。攻击者利用网站漏把恶意的脚本代码注入到网页中,当用户浏览这些网页时,就会执行其中的恶意代码,对受害用户可能采用cookie资料窃取,会话劫持,钓鱼欺骗等攻击手段。
解决方案:
(1)对输入框内的数据进行关键词过滤(如“script”字符串),避免输入恶意脚本语言。 (2)在加载网页的内容之前,会用dataWithContentsOfURL方法将加载的网页的内容提取出来放入NSData中,在使用loadHTMLString之前可以对NSData的内容过滤,编码等等,上述代码中是将script字符串都替换掉,保证了UIWebView不去加载脚本,从而也避免了加载恶意脚本的可能,如下代码:
或者可以通过这些截断的URL来做一些白名单、黑名单策略,如禁用file域,不让它加载本地文件
1.5.2 KVC修改系统值
我们知道,对于类里的私有属性,Objective-C是无法直接访问的,但是KVC是可以的。那么对于我们的类方法或者实例方法,用户可以利用KVC这种方式来改变我们项目里的某个私有变量从而实现篡改代码的逻辑。
所以我们应该尽量少的暴露方法或成员变量至.h文件中,需要声明的属性可以声明在.m文件中,然后直接使用。
1.6 KeyChain数据
Keychain是一个拥有有限访问权限的SQLite数据库(AES256加密),可以为多种应用程序或网络服务存储少量的敏感数据(如用户名、密码、加密密钥等)。如保存身份和密码,以提供透明的认证,使得不必每次都提示用户登录。在iPhone上,Keychain所存储的数据在 /private/var/Keychains/keychain-2.db SQLite数据库中。
数据库内数据,大多数是加密的,Keychain的数据库内容使用了设备唯一的硬件密钥进行加密,该硬件密钥无法从设备上导出。因此,存储在Keychain中的数据只能在该台设备上读取,而无法复制到另一台设备上解密后读取。
一旦攻击者能够物理接触到没有设置密码的iOS设备时,他就可以通过越狱该设备,运行如keychain_dumper这样的工具,读取到设备所有的Keychain条目,获取里面存储的明文信息。具体方法是通过ssl让mac连接iPhone,使用keychain_dumper,导出Keychain。
1.7 越狱监测
先说判断设备是否越狱的几种情况
A.设备越狱普遍都会有以下文件
"/Applications/Cydia.app",
"/Library/MobileSubstrate/MobileSubstrate.dylib",
"/bin/bash",
''/usr/sbin/sshd'',
''/etc/apt''
B.判断是否存在cydia应用
C.能够读取系统所有的应用名称
非越狱手机是无法获取系统应用参数的。
D.读取环境变量 DYLD_INSERT_LIBRARIES
非越狱手机DYLD_INSERT_LIBRARIES获取到的环境变量为NULL。
结合以上条件,就可以判断设备是否越狱,
1.8 防止二次打包
有些hacker可能会通过篡改你的程序包(包括资源文件和二进制代码)加入一些广告或则修改你程序的逻辑,然后重新签名打包,由于第三方hacker获取不到签名证书的私钥,因此会替换掉程序包中签名相关的文件embedded.mobileprovision,我们可以直接检查此文件是否被修改,来判断是否被二次打包,如果程序被篡改,则退出程序。
检测embedded.mobileprovision是否被篡改:
// 校验值,可通过上一次打包获取
#define PROVISION_HASH @"w2vnN9zRdwo0Z0Q4amDuwM2DKhc="
static NSDictionary * rootDic=nil;
void checkSignatureMsg()
{
NSString *newPath=[[NSBundle mainBundle]resourcePath];
if (!rootDic) {
rootDic = [[NSDictionary alloc] initWithContentsOfFile:[newPath stringByAppendingString:@"/_CodeSignature/CodeResources"]];
}
NSDictionary*fileDic = [rootDic objectForKey:@"files2"];
NSDictionary *infoDic = [fileDic objectForKey:@"embedded.mobileprovision"];
NSData *tempData = [infoDic objectForKey:@"hash"];
NSString *hashStr = [tempData base64EncodedStringWithOptions:0];
if (![PROVISION_HASH isEqualToString:hashStr]) {
abort();//退出应用
}
}
2.网络安全
2.1 Https防护
Https 是一种基于证书的加密连接。 应用服务器首先会向证书颁发机构申请一个数字证书。这个证书颁发机构一般是一些比较大的组织,比如 Godaddy, Comodo, Symantec 这些公司。
当我们客户端访问 https:// 开头的地址的时候。先会和服务器进行一次 "握手操作"。 服务器会把它的证书返回给客户端,比如我们电脑上的浏览器。
然后客户端会验证这个证书是不是可信任的,如果是可信任的才会进行下一步操作。
那么问题来了,什么样的证书才是可信任的呢, 这就涉及到证书的数字签名过程了。 我们前面提到的证书颁发机构都会有一个根证书,根证书上面会有两个秘钥,一个公钥一个私钥。公钥是公开的,而私钥只有颁发机构才会知道。 然后证书颁发机构会用根证书的私钥为它签发的子证书中的整体内容进行加密,然后生成数字签名,并且包含到这个子证书中,同时子证书中也会包含自己的公钥和私钥。 这样一来,我们在进行 Https 握手的时候,服务器发送给我们的证书中会带有数字签名, 我们在用颁发机构的公钥对数字签名进行解密,如果解密后的内容和证书本身的内容能够完全匹配,那么我们就可以信任这个证书了。
因为数字签名只有证书颁发机构用它的私钥才能够生成,所以第三方是不能伪造的,除非它能知道颁发机构的私钥。公钥-私钥的这种加密方式,我们称之为非对称加密。也就是说加密和解密过程使用的密钥是不同的。
可以参考下图:
2.2 接口校验
使用cycript和反编译工具是逆向APP工程的基本工具,cycript是解析APP布局和修改代码逻辑的利器,为防止cycript修改代码逻辑,从而逃过我们自己代码的逻辑,我们在编写重要流程时,需要提醒接口也要鉴权判断,如支付订单时,在钱不够的情况下,我们不让button可以点击,但是通过cycript,我们可以修改代码button的属性enable = YES,这样如果后台不做判断导致支付成功,会引发安全问题。
2.3 防止抓包
通常破解一个 app,我们会抓包。这样的话,我们的 app 所有接口,接口数据都会暴露在逆向人员的眼皮底下。这时候,我们可以限制 http 抓包。方式很简单,就是将 NSURLSessionConfiguration 的 connectionProxyDictionary 设置成空的字典,因为这个属性就是用来控制会话的可用代理的。可用参见官方文档。下面是对于 AFNetWorking 的使用方法:
// 继承 AFHTTPSessionManager,重写下列方法
- (instancetype)initWithServerHost:(PDLServerHost*)serverHost {
#ifdef DEBUG
// debug 版本的包仍然能够正常抓包
self = [super initWithBaseURL:serverHost.baseURL];
#else
// 由于使用 ephemeralSessionConfiguration session 发起的请求不带 cookie 和使用缓存等
NSURLSessionConfiguration *conf = [NSURLSessionConfiguration ephemeralSessionConfiguration];
conf.connectionProxyDictionary = @{};
self = [super initWithBaseURL:serverHost.baseURL sessionConfiguration:conf];
#endif
return self;
}
但是由于 OC 方法很容易被 hook,避免抓包是不可能的,所以,最好的方式是对请求参数进行加密。
2.4 请求参数加密
为了确保信息的安全,我们采用AES+RSA组合的方式进行接口参数加密和解密。
AES:对称加密,加密与解密使用的是同样的密钥,所以速度快,但由于需要将密钥在网络传输,所以安全性不高。
RSA:非对称加密,使用了一对密钥,公钥与私钥,所以安全性高,但加密与解密速度慢。
所以我们结合两者的特性,采用AES对传输的数据进行加密,然后采用RSA对AES的秘钥进行加密。AES的秘钥是客户端生成的(iOS我采用的是UUID),然后把AES的秘钥用不可逆的RSA加密,最后发送给服务器的是RSA加密过的AES的秘钥和AES加密的数据。服务器端保留RSA的私钥,可以对AES的秘钥进行解密,然后用AES的秘钥解密数据。这样就拿到客户端的数据了。服务器进行数据处理过后,用传过来的AES的秘钥加密数据再传给客户端。
3.系统层面安全
3.1 进入App时弹窗验证
当首次使用一些App时,部分应用会向用户请求获得手机位置、通讯录、日历或照片的权限。然而,这些权限有些是必要的,有些则存在过度收集用户信息的行为。
判断App是否“越界获取”隐私权限的标准是 App向用户提供的功能是否必须用到相应的权限。也就是说,只要不妨碍正常使用App,某些权限用户大可不必授权,以此最大程度地保护个人隐私。
以地理位置为例,除非需要使用地图类App,否则都可以不授权。如果不需要扫二维码、录音、拍照、拍视频,用户也无需授权相机和麦克风。值得注意的是,短信/通讯录这类权限,最好都不要授权。
3.2 剪切板的缓存
我们在使用剪切板进行操作时,iOS系统会缓存所操作的数据,如果数据涉及隐私,将会带来安全问题。如下图,iOS的三种剪切板和功能,其中系统级别的剪切板优先级最高。
注意:在获取剪切板内容时,总是先取系统的剪切板内容,若没有值才会取应用间的剪切板内容。所以在使用完剪切板时,我们在程序退出时,清空剪切板内容。
3.3 系统键盘缓存
我们打字在使用系统键盘打字时时,会出现提示,键盘一般会缓存这些提示字符串,缓存在手机的“/private/var/mobile/Library/Keyboard/dynamic-text.dat”文件中,攻击者可以提取出来,出现频率最高的往往是账户名和密码。
可以采用应用内自定义键盘,银行类APP大多使用自定义键盘。
第三方键盘权限问题:
第三方键盘可能会收集自己的信息以及上传自己的信息,因此在苹果设置里禁止这种行为至关重要,再有就是,因为第三方键盘不可控因素很多,可能缓存数据逻辑与系统键盘不一样,防止攻击者攻击本地缓存数据,因此建议尽量少用第三方键盘。如下图,苹果给的建议。
另外,我们使用的第三方键盘,会上传你的键入数据到服务器,这样我们的隐私完全暴露给第三方,使用时因注意不要启用不完全访问。
3.4 高斯模糊
防止手机截屏图片泄密方案:
一般我们在应用被挂起时,在当前页面添加一层高斯模糊,在应用重新进入前台时,删除模糊效果,代码如下:
代码如下:
4.Xcode来源
从非官方渠道下载的xcode,被导入恶意代码会上传产品自身的部分基本信息,包括安装时间、应用id,应用名称、系统版本以及国家和语言等。
验证Xcode来源:
终端内输入:spctl --assess --verbose/Applications/Xcode.app
如果输出是/Applications/Xcode.app: accepted source=Apple
或者/Applications/Xcode.app: accepted source=Apple System
或者/Applications/Xcode.app: accepted source=Mac App Store
中的一个,表明来源可靠。
因此,我们为防止工具和第三方库被污染,使用时一定要注意来源,对于SDK来说,选用之前要做到观看代码逻辑,避免恶意代码,对于含有动态库或者静态库的SDK,尽量避免使用,或者运用反编译手段,逆向SDK,这点难度较大。
二、黑客攻击工具
1.netcat
iTunes Store的数据都在/var/mobile/Library/AddressBook/AddressBook.sqlitedb
中,只要能能拿出AddressBook.sqlitedb就可以非法拿到用户的数据。
利用netcat,指定服务名称,就可以抓取设备 iTunesstore 信息。
2.GDB
使用 <sys/types.h>
和 <dlfcn.h>
库来阻止 GDB(GDB是用于调试C程序的一种工具,一般包含在gcc工具链中)依附,从而达到阻止攻击者破解代码后,对我们的程序进行调试。
3.iOS逆向
3.1 dumpdecrypted
App Store上的应用都使用了FairPlay DRM数字版权加密保护技术。我们要对文件进行反汇编,而IPA都是加密的,哪怎么办呢?所以在逆向之前我们需要先对应用进行砸壳。这里我们使用的是 dumpdecrypted。(还可以使用Clutch,这里我们就不讲Clutch了)
3.2 class-dump
class-dump是用于解析Mach-O文件中存储的OC运行时信息的。他能生成类的声明、分类、协议。和otool -ov类似,但是由于class-dump的结果是以OC代码展示的,所以有很强的可读性。
3.3 Reveal
Reveal能够在运行时调试和修改iOS应用程序。它能连接到应用程序,并允许开发者编辑各种用户界面参数,
这反过来会立即反应在程序的UI上。就像用FireBug调试HTML页面一样,在不需要重写代码、
重新构建和重新部署应用程序的情况下就能够调试和修改iOS用户界面。
3.4 Cycript
Cycript是一个理解Objective-C语法的javascript解释器,这意味着我们能够在一个命令中用Objective-C或者javascript, 甚至2者兼用。它能够挂钩正在运行的进程,能够在运行时修改应用的很多东西。使用Cycript有如下好处: 1.我们能够挂钩正在运行的进程,并且找出正被使用的类信息,例如view controllers,内部和第3方库,甚至程序的delegate的名称。 2.对于一个特定的类,例如View Controller, App delegate或者任何其他的类,我们能够得到所有被使用的方法名称。 3.我们能够得到所有实例变量的名称和在程序运行的任意时刻实例变量的值。 4.我们能够在运行时修改实例变量的值。 5.我们能够执行Method Swizzling,例如替换一个特定方法的实现。 6.我们可以在运行时调用任意方法,即使这个方法目前并不在应用的实际代码当中。
3.5 Charles、Wireshark
Charles是Mac下常用的对网络流量进行分析的工具,类似于Windows下的Fiddler。 在开发iOS程序的时候,往往需要调试客户端和服务器的API接口,这个时候就可以用Charles,Charles能够拦截SSL请求、模拟慢速网络、 支持修改网络请求包并多次发送、能够篡改Request和Response等强大的功能。
参考资料:
App 安全方案
iOS 安全攻防
《iOS 应用逆向与安全》读后感
对 iOS app 进行安全加固
念茜的博客
Objective-C 使用宏定义来做最基本的代码函数混淆
iOS移动端的安全架构设计