美文网首页iOS开发iOS
iOS 如何防止抓包

iOS 如何防止抓包

作者: Best_Kai | 来源:发表于2021-01-04 08:45 被阅读0次

    iOS 如何防止抓包


    1、抓包原理

    为了防止被抓包那么就要了解抓包的原理。

    其实原理很是简单:一般抓包都是通过代理服务来冒充你的服务器,客户端真正交互的是这个假冒的代理服务,这个假冒的服务再和我们真正的服务交互,这个代理就是一个中间者 ,我们所有的数据都会通过这个中间者,所以我们的数据就会被抓取。HTTPS 也同样会被这个中间者伪造的证书来获取我们加密的数据。

    2、防止抓包

    为了数据的更安全,那么我们如何来防止被抓包。

    第一种思路是:如果我们能判断是否有代理,有代理那么就存在风险。

    第二种思路:针对HTTPS 请求。我们判断证书的合法性。

    第一种方式的实现:

    一、发起请求之前判断是否存在代理,存在代理就直接返回,请求失败。

    CFDictionaryRef dicRef = CFNetworkCopySystemProxySettings();
    CFStringRef proxyStr = CFDictionaryGetValue(dicRef, kCFNetworkProxiesHTTPProxy);
    NSString *proxy = (__bridge NSString *)(proxyStr);
    
    复制代码

    <pre style="margin: 0px; padding: 0px; overflow: auto; word-wrap: break-word; font-family: "Courier New" !important; font-size: 12px !important;">+ (BOOL)getProxyStatus {
    NSDictionary *proxySettings = NSMakeCollectable([(NSDictionary *)CFNetworkCopySystemProxySettings() autorelease]);
    NSArray *proxies = NSMakeCollectable([(NSArray *)CFNetworkCopyProxiesForURL((CFURLRef)[NSURL URLWithString:@"http://www.baidu.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;
    }
    

    }</pre>

    复制代码

    二、我们可以在请求配置中清空代理,让请求不走代理

    我们通过hook到sessionWithConfiguration: 方法。然后清空代理

    + (void)load{
      Method method1 = class_getClassMethod([NSURLSession class],@selector(sessionWithConfiguration:));
      Method method2 = class_getClassMethod([NSURLSession class],@selector(px_sessionWithConfiguration:));
      method_exchangeImplementations(method1, method2);
    
      Method method3 = class_getClassMethod([NSURLSession class],@selector(sessionWithConfiguration:delegate:delegateQueue:));
      Method method4 = class_getClassMethod([NSURLSession class],@selector(px_sessionWithConfiguration:delegate:delegateQueue:));
      method_exchangeImplementations(method3, method4);
    }
    
    + (NSURLSession*)px_sessionWithConfiguration:(NSURLSessionConfiguration*)configuration delegate:(nullable id)delegate delegateQueue:(nullable NSOperationQueue*)queue
    {
          if(configuration) configuration.connectionProxyDictionary = @{};
    
      return [self px_sessionWithConfiguration:configuration delegate:delegate delegateQueue:queue];
    }
    
    + (NSURLSession*)px_sessionWithConfiguration:(NSURLSessionConfiguration*)configuration
    {
    
          if(configuration) configuration.connectionProxyDictionary = @{};
    
      return [self px_sessionWithConfiguration:configuration];
    }
    
    

    ​ 第二种思路的实现:

    主要是针对HTTPS 请求,对证书的一个验证。

    通过 SecTrustRef 获取服务端证书的内容

    static NSArray * AFCertificateTrustChainForServerTrust(SecTrustRef serverTrust) {
       CFIndex certificateCount = SecTrustGetCertificateCount(serverTrust);
       NSMutableArray *trustChain = [NSMutableArray arrayWithCapacity:(NSUInteger)certificateCount];
    
       for (CFIndex i = 0; i < certificateCount; i++) {
           SecCertificateRef certificate = SecTrustGetCertificateAtIndex(serverTrust, i);
           [trustChain addObject:(__bridge_transfer NSData *)SecCertificateCopyData(certificate)];
       }
    
       return [NSArray arrayWithArray:trustChain];
    }
    
    

    然后读取本地证书的内容进行对比

     NSString *cerPath = [[NSBundle mainBundle] pathForResource:@"certificate" ofType:@"cer"];//证书的路径
       NSData *certData = [NSData dataWithContentsOfFile:cerPath];
    SSet<NSData*> * set = [[NSSet alloc]initWithObjects:certData  , nil];  // 本地证书内容
    // 服务端证书内容
    NSArray *serverCertificates = AFCertificateTrustChainForServerTrust(serverTrust);
               
               for (NSData *trustChainCertificate in [serverCertificates reverseObjectEnumerator]) {
                   if ([set containsObject:trustChainCertificate]) {
                       // 证书验证通过
                   }
               }
    

    SSL Pinning(AFN+SSL Pinning)推荐

    SSL Pinning,即SSL证书绑定。通过SSL证书绑定来验证服务器身份,防止应用被抓包。
    1、取到证书
    客户端需要证书(Certification file), .cer格式的文件。可以跟服务器端索取。
    如果他们给个.pem文件,要使用命令行转换:

        openssl x509 -inform PEM -in name.pem -outform DER -out name.cer
    
    

    如果给了个.crt文件,请这样转换:

        openssl x509 -in name.crt -out name.cer -outform der
    
    

    如果啥都不给你,你只能自己动手了:

    openssl s_client -connect www.website.com:443 </dev/null 2>/dev/null | openssl x509 -outform DER > myWebsite.cer**
    
    

    2、把证书加进项目中
    把生成的.cer证书文件直接拖到你项目的相关文件夹中,记得勾选Copy items if neede和Add to targets。
    3、参数名意思
    AFSecurityPolicy
    SSLPinningMode
    AFSecurityPolicy是AFNetworking中网络通信安全策略模块。它提供三种SSL Pinning Mode

    /**
     ## SSL Pinning Modes
    
     The following constants are provided by `AFSSLPinningMode` as possible SSL pinning modes.
    
     enum {
     AFSSLPinningModeNone,
     AFSSLPinningModePublicKey,
     AFSSLPinningModeCertificate,
     }
    
     `AFSSLPinningModeNone`
     Do not used pinned certificates to validate servers.
    
     `AFSSLPinningModePublicKey`
     Validate host certificates against public keys of pinned certificates.
    
     `AFSSLPinningModeCertificate`
     Validate host certificates against pinned certificates.
    */
    
    

    AFSSLPinningModeNone:完全信任服务器证书;
    AFSSLPinningModePublicKey:只比对服务器证书和本地证书的Public Key是否一致,如果一致则信任服务器证书;
    AFSSLPinningModeCertificate:比对服务器证书和本地证书的所有内容,完全一致则信任服务器证书;
    选择那种模式呢?
    AFSSLPinningModeCertificate:最安全的比对模式。但是也比较麻烦,因为证书是打包在APP中,如果服务器证书改变或者到期,旧版本无法使用了,我们就需要用户更新APP来使用最新的证书。
    AFSSLPinningModePublicKey:只比对证书的Public Key,只要Public Key没有改变,证书的其他变动都不会影响使用。
    如果你不能保证你的用户总是使用你的APP的最新版本,所以我们使用AFSSLPinningModePublicKey。

    allowInvalidCertificates

    /**
     Whether or not to trust servers with an invalid or expired SSL certificates. Defaults to `NO`.
     */
    @property (nonatomic, assign) BOOL allowInvalidCertificates;
    

    是否信任非法证书,默认是NO。
    validatesDomainName

    /**
     Whether or not to validate the domain name in the certificate's CN field. Defaults to `YES`.
     */
    @property (nonatomic, assign) BOOL validatesDomainName;
    

    是否校验证书中DomainName字段,它可能是IP,域名如*.google.com,默认为YES,严格保证安全性。

    4、使用AFSecurityPolicy设置SLL Pinning

    + (AFHTTPSessionManager *)manager
    {
        static AFHTTPSessionManager *manager = nil;
        static dispatch_once_t onceToken;
        dispatch_once(&onceToken, ^{
        
            NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];
            manager =  [[AFHTTPSessionManager alloc] initWithSessionConfiguration:config];
    
            AFSecurityPolicy *securityPolicy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModePublicKey withPinnedCertificates:[AFSecurityPolicy certificatesInBundle:[NSBundle mainBundle]]];
            manager.securityPolicy = securityPolicy;
        });
        return manager;
    }
    
    

    扩展
    Android 防止抓包
    1、单个接口访问不带代理的

     URL url = new URL(urlStr);  
       urlConnection = (HttpURLConnection) url.openConnection(Proxy.NO_PROXY); 
    
    

    2、OkHttp框架

        OkHttpClient client = new OkHttpClient().newBuilder().proxy(Proxy.NO_PROXY).build(); 
    

    相关文章

      网友评论

        本文标题:iOS 如何防止抓包

        本文链接:https://www.haomeiwen.com/subject/tjumoktx.html