补充更新完善app安全方案:2019.12.13
证书放到app里面,用来校验信任链的话,如果别人使用青花瓷抓包,并且把抓包工具生成的证书在抓包的机器上导出来,替换掉我们app内部的证书,然后对app进行签名,那么仍旧可以使用青花瓷抓我们的包。对于这种情况,我想到了2种方案:
1. 检验app的签名方式并且签发团队(常规app,不需要重签名分发)
苹果商店下载的app包里面是没有embedded.mobileprovision文件的,而且是没被砸壳的,可以认为没有这个文件是我们苹果商店的版本,是不会被替换证书校验机制的,所以可以认为是安全的,如果我们考虑到一些我们自己分发的测试包或者adHoc包的时候,就可能存在这个文件,这个时候就需要校验是否我们自己的开发团队TeamId即可,
代码如下:
- (BOOL)isFromLiangJiSign
{
//取出embedded.mobileprovision这个描述文件的内容进行判断
NSString *mobileProvisionPath = [[NSBundle mainBundle] pathForResource:@"embedded" ofType:@"mobileprovision"];
NSData *rawData = [NSData dataWithContentsOfFile:mobileProvisionPath];
if (rawData == nil) {
return NO;
}
NSString *rawDataString = [[NSString alloc] initWithData:rawData encoding:NSASCIIStringEncoding];
NSRange plistStartRange = [rawDataString rangeOfString:@""];
if (plistStartRange.location != NSNotFound && plistEndRange.location != NSNotFound) {
NSString *tempPlistString = [rawDataString substringWithRange:NSMakeRange(plistStartRange.location, NSMaxRange(plistEndRange))];
NSData *tempPlistData = [tempPlistString dataUsingEncoding:NSUTF8StringEncoding];
NSDictionary *plistDic = [NSPropertyListSerialization propertyListWithData:tempPlistData options:NSPropertyListImmutable format:nil error:nil];
NSArray *applicationIdentifierPrefix = [plistDic valueForKey:@"ApplicationIdentifierPrefix" ];
NSDictionary *entitlementsDic = [plistDic valueForKey:@"Entitlements"];
NSString *mobileBundleID = [entitlementsDic valueForKey:@"application-identifier"];
if (applicationIdentifierPrefix.count > 0 && mobileBundleID != nil) {
if ([mobileBundleID isEqualToString:@"自己的团队teamID"]) {
return YES;
}
}
}
return NO;
}
如此一来,即使别人拿到我们的app,进行重签名之后,我们不让app运行,直接退出即可,(此处不考虑别人再反编译修改我们的代码,因为那个时候,我们完全无能为力,除了增加编译难度,花符号或者混淆问题,最终逆向出我们的app都是时间问题)
2. https请求和响应的数据是加密后的格式
对证书加密的数据再次进行rsa加密(千万保留好我们的加密私钥),这样即使对方已经将证书替换,那么看到的数据仍旧是一堆加密后的数据乱码,必须解密之后才能得到有效数据(一些直播的app大部分是用的这种方式)
网上数据抓包,在当前抓包工具横行的时代,对于一个IT开发者来说,是一个很简单的必备的技能,例如青花瓷(Charles)等等工具.
在讲https抓包之前,必须要了解https的整个校验和通信过程,我们就简单的精简的画一下重要的过程,至于什么三个随机数或者通信秘钥的生成就不详细介绍,主要是针对讲一下https的中间人攻击(https抓包的实现基础)过程
在这个过程中,正常的话,如果哪个步骤出现问题,链接都会停止,无法进行通信,这个是https简单的校验的一个过程介绍.
那么,https在抓包工具中是如何实现抓包的呢?
抓包工具就是在上面的过程中,证书认证生成通信密钥中做了手脚.
以青花瓷为例,大家使用青花瓷抓http请求时,由于没有做安全校验,很容易就实现了数据拦截和转发,至于https呢?
中间人攻击的情形
抓取https包的时候,青花瓷会要求使用者 对抓包的设备(手机或其他设备)
,安装一个证书,安装这个证书的时候,其实是安装了一个根证书(允许颁发CA的一个证书机构的根证书),当你安装了该根证书之后,该证书机构颁发的其他证书,默认都会被你的系统所信任,这个就是青花瓷完成https抓包的一个重要前提!!
(如果不太了解证书信任链为什么会这样,可以看一下这个文章,关于iOS系统对https信任链的校验关系,
地址: http://www.jianshu.com/p/2cae04922e9c)
当客户端设置了代理,并且开始发出网络请求的时候,这个网络请求的校验过程就会变成这样
- 当客户端Client对服务器Server发送请求(带着随机数和加密算法),由于青花瓷做了代理,请求被青花瓷拦截,处理(青花瓷的角色现在对于Client来说是服务器),青花瓷将客户端带的随机数和加密算法处理,然后返回自己的证书通过客户端校验,获取到客户端提交的请求参数等数据,
- 青花瓷作为客户端(自己去产生随机数和携带支持的加密算法)去请求刚刚Client想要请求的Server,然后,Server会和青花瓷完成上面讲的那个完整的校验,并且读取青花瓷带错来的具体请求,返回正常的数据结果.
- 青花瓷得到服务器数据的返回结果之后,开始继续和过程1中的Client以服务器的身份,去做处理,首先收到客户端的随机数和加密算法,自己生成一个随机数和选择一个客户端的加密算法,然后*********重要********** 青花瓷会返回一个伪造的CA证书(公钥和真实的server不一样,但是域名是一样的,或者说,除了域名是一致的,其他的都不是一致的,而且这个签发机构是青花瓷之前让你安装的根证书 签发的,所以,当返回这个证书的时候,你的客户端的信任链是可以完成的,会被系统信任),然后Client在这个伪造的证书(对于青花瓷和Client是真实证书(验证信任链和证书信息都通过了),但是和真实的域名对应的证书来看,是伪造证书)的基础上,和青花瓷通信,然后青花瓷再和Server通信,成了一个中间人的角色,这样,整个过程的数据传输,都被青花瓷给监听到了
在此,中间人攻击的过程 就完成了
至于客户端怎么防止被抓包呢? 我一共想到了2个方案
1.当进行网络请求的时候,客户端判断当前是否设置了代理,如果设置了代理,不允许进行访问(不知道微信浏览器 里面 是不是这样实现的,微信里面 设置了代理看公众号等信息就都不允许看了,无法访问)
附带判断是否设置代理的代码:
+ (BOOL)getProxyStatus {
NSDictionary *proxySettings = NSMakeCollectable([(NSDictionary *)CFNetworkCopySystemProxySettings() autorelease]);
NSArray *proxies = NSMakeCollectable([(NSArray *)CFNetworkCopyProxiesForURL((CFURLRef)[NSURL URLWithString:@"http://www.google.com"], (CFDictionaryRef)proxySettings) autorelease]);
NSDictionary *settings = [proxies objectAtIndex:0];
NSLog(@"host=%@", [settings objectForKey:(NSString *)kCFProxyHostNameKey]);
NSLog(@"port=%@", [settings objectForKey:(NSString *)kCFProxyPortNumberKey]);
NSLog(@"type=%@", [settings objectForKey:(NSString *)kCFProxyTypeKey]);
if ([[settings objectForKey:(NSString *)kCFProxyTypeKey] isEqualToString:@"kCFProxyTypeNone"])
{
//没有设置代理
return NO;
}
else
{
//设置代理了
return YES;
}
}
2.客户端本地做证书校验,并且设置不仅仅校验公钥,设置完整的正式校验模式
+(AFSecurityPolicy*)customSecurityPolicy
{
// /先导入证书
NSString *cerPath = [[NSBundle mainBundle] pathForResource:@"test" ofType:@"cer"];//证书的路径
NSData *certData = [NSData dataWithContentsOfFile:cerPath];
// AFSSLPinningModeCertificate 使用证书验证模式 (AFSSLPinningModeCertificate是证书所有字段都一样才通过认证,AFSSLPinningModePublicKey只认证公钥那一段,AFSSLPinningModeCertificate更安全。但是单向认证不能防止“中间人攻击”)
AFSecurityPolicy *securityPolicy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeCertificate];
// allowInvalidCertificates 是否允许无效证书(也就是自建的证书),默认为NO
// 如果是需要验证自建证书,需要设置为YES
securityPolicy.allowInvalidCertificates = YES;
//validatesDomainName 是否需要验证域名,默认为YES;
//假如证书的域名与你请求的域名不一致,需把该项设置为NO;如设成NO的话,即服务器使用其他可信任机构颁发的证书,也可以建立连接,这个非常危险,建议打开。
//置为NO,主要用于这种情况:客户端请求的是子域名,而证书上的是另外一个域名。因为SSL证书上的域名是独立的,假如证书上注册的域名是www.google.com,那么mail.google.com是无法验证通过的;当然,有钱可以注册通配符的域名*.google.com,但这个还是比较贵的。
//如置为NO,建议自己添加对应域名的校验逻辑。
securityPolicy.validatesDomainName = YES;
NSSet * set = [[NSSet alloc]initWithObjects:certData , nil];
securityPolicy.pinnedCertificates = set;
return securityPolicy;
}
这样的话,证书会校验请求的时候不仅仅校验域名,会将证书中的公钥及其他信息也进行校验,这样的话,中间人伪造的证书就无法通过验证,无法进行抓包
上面是我自己整理和猜测的,具体是否真的是这样或者这个方案是否真实可行,仅供参考,有错误的话,希望各位大牛赐教和指出,在此特别感谢,希望可以共同进步,谢谢