APP安全审查目前真的是突飞猛进,各种加固插件层出不穷,下面列举几个我碰到的安全检查的问题和解决方案。
一.注入攻击(高危范畴)
iOS应用注入攻击的方式分为两种;
第一种是越狱环境下的注入攻击,通过修改DYLD_INSERT_LIBRARIES环境变量的值,来插入第三方动态库并执行,加载前会优先加载第三方库文件;
第二种是非越狱环境下的注入攻击,直接将自定义的Framwork或者dylib库打包进入APP并重签名,或者利用yololib修改MachO文件,添加库路径,在应用启动时dyld会加载并执行。攻击者可以通过注入攻击将一段恶意代码写到目标进程,通过这段代码可以加载其它可执行程序,进而监控程序运行、获取敏感信息等。常见的动态注入,可以实现窃取输入的登录账号、密码、支付密码,修改转账的目标账号、金额等。
解决方案:
1.针对第一种攻击方式就是对越狱环境的检测,并加以限制。
2.下面说说第二种非越狱情况:
2.1 在xcode设置 Other Linker Flags 增加参数 -Wl,-sectcreate,__RESTRICT,__restrict,/dev/null
原因:
在参考链接1中可以搜索匹配关键字,找到这段:
由此可知,有三种情况是可以过滤掉,可以滤掉动态库的注入, 详见参考链接1
2.2 需要检索一下程序所加载的动态库是否是我们程序所有,这个需将自身加载动态库,设置为白名单状态,代码如下:
在AppDelegate中,检测是否有动态检测, 如果有需要过滤白名单。
需引入#import <mach-o/dyld.h>
//检测是否有动态注入库
typedef char* _Nullable (*GET_ENV_TYPE)(const char *);
static inline bool __attribute__((optnone))CheckInsertLib()
{
GET_ENV_TYPE get_env = getenv;
char *env = get_env("DYLD_INSERT_LIBRARIES");
return env!=NULL;//不为NULL表示有动态库注入
}
//筛选动态库白名单
staticNSMutableArray *_whitelistFramework;
static inline int __attribute__((optnone))CheckInsertWhiteList()
{
_whitelistFramework = [NSMutableArray array];
// 添加动态库
[_whitelistFramework addObject:@"/AFNetworking.framework/AFNetworking"];
[_whitelistFramework addObject:@"/FMDB.framework/FMDB"];
[_whitelistFramework addObject:@"/IQKeyboardManager.framework/IQKeyboardManager"];
[_whitelistFramework addObject:@"/MJRefresh.framework/MJRefresh"];
[_whitelistFramework addObject:@"/SDWebImage.framework/SDWebImage"];
[_whitelistFramework addObject:@"/FMDB/SQLCipher.framework/FMDB/SQLCipher"];
int count = _dyld_image_count();
for(inti = 0; i < count; i++) {
//遍历拿到库名称
constchar* imageName =_dyld_get_image_name(i);
NSString *filePath = [[NSString alloc]initWithBytes:imageName length:strlen(imageName) encoding:NSUTF8StringEncoding];
NSArray *arrs = [filePath componentsSeparatedByString:@".app/Frameworks"];
// NSLog(@"filePath = %@", filePath);
if(arrs.count> 1) {
// NSLog(@"arrs[1] = %@", arrs[1]);
if (![_whitelistFramework containsObject:arrs[1]]) {
return1;
}
}
}
return0;
}
其中CheckInsertLib() 和 CheckInsertWhiteList() 需要在didFinishLaunchingWithOptions设置, 如果判断有非自身白名单的动态库,需要上报记录和中断程序。
二、检测是否是设置http代理。(中风险)
使用网络代理服务具有以下风险:突破中国电信的IP封锁,访问国外网站;隐藏真实IP;通信数据被监听或者篡改。应用使用代理服务器联网,可能导致客户端与服务器端通信的数据被监听和篡改,导致用户敏感数据泄露或者服务器被恶意攻击。
解决方案:
+ (BOOL)isSettingProxy
{
CFDictionaryRef dicRef = CFNetworkCopySystemProxySettings();
const CFStringRef proxyCFstr = CFDictionaryGetValue(dicRef, (const void*)kCFNetworkProxiesHTTPProxy);
NSString*proxy = (__bridgeNSString*)(proxyCFstr);
if(proxy)
{
returnYES;
}
else
{
returnNO;
}
}
也需要在设置在App的几个生命周期阶段。
三、App中数据库明文存储。(中风险)
iOS自带的SQLite数据库没有内置的加密支持,如果iOS应用自身未对数据进行加密后再存储,那么iOS应用直接以明文格式将敏感数据存储在SQLite数据库中。一旦可以物理访问到设备或其备份文件,存储在SQLite中未加密的敏感信息容易被泄露。
解决方案:
采用方案就是 SQLCipher , 我的项目中使用FMDB, 就是使用了FMDB/SQLCipher开启数据加密。
在FMDataBase.m文件中,搜索sqlite3_open方法,大概181行 新增
else if(err == SQLITE_OK){
[self setKey:DB_SECRETKEY];
}
其中DB_SECRETKEY为自定义秘串。
还有在216行位置,示例:
int err = sqlite3_open_v2([self sqlitePath], (sqlite3**)&_db, flags, [vfsName UTF8String]);
if(err !=SQLITE_OK) {
// NSLog(@"error opening!: %d", err);
returnNO;
}elseif(err ==SQLITE_OK){
[self setKey:DB_SECRETKEY];
}
if (_maxBusyRetryTimeInterval > 0.0) {
// set the handler
[self setMaxBusyRetryTimeInterval:_maxBusyRetryTimeInterval];
}
四、格式化字符串漏洞 (低风险)
格式化字符串漏洞产生的原因是软件使用了格式化字符串作为参数,且该格式化字符串来自外部输入。当程序直接将输入内容作为printf族、scanf族函数的唯一参数、或者参数类型错误、或者格式化字符串参数和传入参数个数不一致时,导致可以实现任意内存地址的读写,造成堆栈信息泄漏。
其中针对printf sprintf sscanf 三种关键字静态分析。
参考链接3中, 使用 nm和grep命令, 查找Payload中frameworks文件夹中 查找这三个关键字, 还有同名文件筛选。
五、弱HASH算法。
弱HASH算法指安全级别低的HASH算法,包括SHA1和MD5,涉及泄露客户端隐私数据。
解决方案:
参考链接3, 例如MD5 方法中关键词 为 CC_MD5
nm -pa 路径 | grep CC_MD5
SHA1 对应的关键词 : CC_SHA1
断定是找到framework中,还是同名文件中查找确定位置 。
把这两项替换为SHA256,或者SHA512等。
参考链接:
1.《dyld源码》