iOS 如何防止https抓包(中间人攻击),及charles抓包原理

补充更新完善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抓包的实现基础)过程

iOS 如何防止https抓包(中间人攻击),及charles抓包原理_第1张图片
image.png

在这个过程中,正常的话,如果哪个步骤出现问题,链接都会停止,无法进行通信,这个是https简单的校验的一个过程介绍.

那么,https在抓包工具中是如何实现抓包的呢?
抓包工具就是在上面的过程中,证书认证生成通信密钥中做了手脚.
以青花瓷为例,大家使用青花瓷抓http请求时,由于没有做安全校验,很容易就实现了数据拦截和转发,至于https呢?

中间人攻击的情形

抓取https包的时候,青花瓷会要求使用者 对抓包的设备(手机或其他设备)
,安装一个证书,安装这个证书的时候,其实是安装了一个根证书(允许颁发CA的一个证书机构的根证书),当你安装了该根证书之后,该证书机构颁发的其他证书,默认都会被你的系统所信任,这个就是青花瓷完成https抓包的一个重要前提!!
(如果不太了解证书信任链为什么会这样,可以看一下这个文章,关于iOS系统对https信任链的校验关系,
地址: http://www.jianshu.com/p/2cae04922e9c)

iOS 如何防止https抓包(中间人攻击),及charles抓包原理_第2张图片
image.png

当客户端设置了代理,并且开始发出网络请求的时候,这个网络请求的校验过程就会变成这样

iOS 如何防止https抓包(中间人攻击),及charles抓包原理_第3张图片
image.png
  1. 当客户端Client对服务器Server发送请求(带着随机数和加密算法),由于青花瓷做了代理,请求被青花瓷拦截,处理(青花瓷的角色现在对于Client来说是服务器),青花瓷将客户端带的随机数和加密算法处理,然后返回自己的证书通过客户端校验,获取到客户端提交的请求参数等数据,
  2. 青花瓷作为客户端(自己去产生随机数和携带支持的加密算法)去请求刚刚Client想要请求的Server,然后,Server会和青花瓷完成上面讲的那个完整的校验,并且读取青花瓷带错来的具体请求,返回正常的数据结果.
  3. 青花瓷得到服务器数据的返回结果之后,开始继续和过程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;
}

这样的话,证书会校验请求的时候不仅仅校验域名,会将证书中的公钥及其他信息也进行校验,这样的话,中间人伪造的证书就无法通过验证,无法进行抓包

上面是我自己整理和猜测的,具体是否真的是这样或者这个方案是否真实可行,仅供参考,有错误的话,希望各位大牛赐教和指出,在此特别感谢,希望可以共同进步,谢谢

你可能感兴趣的:(iOS 如何防止https抓包(中间人攻击),及charles抓包原理)