iOS 如何防止https抓包(中间人攻击),及charles抓
网上数据抓包,在当前抓包工具横行的时代,对于一个IT开发者来说,是一个很简单的必备的技能,例如青花瓷(Charles)等等工具.
在讲https抓包之前,必须要了解https的整个校验和通信过程,我们就简单的精简的画一下重要的过程,至于什么三个随机数或者通信秘钥的生成就不详细介绍,主要是针对讲一下https的中间人攻击(https抓包的实现基础)过程
image.png在这个过程中,正常的话,如果哪个步骤出现问题,链接都会停止,无法进行通信,这个是https简单的校验的一个过程介绍.
那么,https在抓包工具中是如何实现抓包的呢?
抓包工具就是在上面的过程中,证书认证生成通信密钥中做了手脚.
以青花瓷为例,大家使用青花瓷抓http请求时,由于没有做安全校验,很容易就实现了数据拦截和转发,至于https呢?
中间人攻击的情形
抓取https包的时候,青花瓷会要求使用者 对抓包的设备(手机或其他设备)
,安装一个证书,安装这个证书的时候,其实是安装了一个根证书(允许颁发CA的一个证书机构的根证书),当你安装了该根证书之后,该证书机构颁发的其他证书,默认都会被你的系统所信任,这个就是青花瓷完成https抓包的一个重要前提!!
(如果不太了解证书信任链为什么会这样,可以看一下这个文章,关于iOS系统对https信任链的校验关系,
地址: http://www.jianshu.com/p/2cae04922e9c)
当客户端设置了代理,并且开始发出网络请求的时候,这个网络请求的校验过程就会变成这样
image.png- 当客户端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<NSData*> * set = [[NSSet alloc]initWithObjects:certData , nil];
securityPolicy.pinnedCertificates = set;
return securityPolicy;
}
这样的话,证书会校验请求的时候不仅仅校验域名,会将证书中的公钥及其他信息也进行校验,这样的话,中间人伪造的证书就无法通过验证,无法进行抓包
上面是我自己整理和猜测的,具体是否真的是这样或者这个方案是否真实可行,仅供参考,有错误的话,希望各位大牛赐教和指出,在此特别感谢,希望可以共同进步,谢谢