iOS开发技术面试好文

iOS 网络数据安全(防止抓包)

2021-08-05  本文已影响0人  刃之剑

A.首先 浅谈http、https与数据加密

ip、端口、http协议

下面用一个例子来介绍客户端与服务器在应用层的通讯流程

http传输安全

在上个例子中,小客和小服在沟通中存在一个很大的安全隐患,假设邮差在送信过程中被人拦截下来,并篡改了信的内容,这样就会导致隐私泄露和信息被恶意修改问题,并且小客和小服完全不知道发生了什么。我们如果使http请求更安全呢?

使用http请求真的那么不安全吗,攻击者到底是如何拦截我发出的请求的?

【数据加密】

【数据加密示例】

【https】

总结

A 参考 https://github.com/SmileZXLee/aboutHttp.git

B.上面已经说到几个关于数据请求的安全处理.

1.服务端生成一对公钥和私钥把公钥交给客户端,
当客户端需要进行数据请求的时候,数据加密处理,客户端保存的公钥,对请求数据进行加密传给后台,后台利用私钥对请求解密,拿出参数进行处理后再加密返回给客户端.客户端对返回的数据进行解密然后进行界面的展示

NSString *publicKey = @"MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC0OiXHaees9Aj5h31YYGw5nfCUdS6MK0T5UrJAfIdwkUbadDOXclDVK+ftBMe+DVAn7xSORPi1cjiBBjU+lo/hmNGoDWQGgxr/LAkaJz3/A1Sv+S1d3deTc6SFN+toDQbpsx3jYOUrJM1B8olUI1a9f+DgzkF/sIKJ7V4Wh7XtlQIDAQAB";
    //私钥
    NSString *privateKey = @"MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBALQ6Jcdp56z0CPmHfVhgbDmd8JR1LowrRPlSskB8h3CRRtp0M5dyUNUr5+0Ex74NUCfvFI5E+LVyOIEGNT6Wj+GY0agNZAaDGv8sCRonPf8DVK/5LV3d15NzpIU362gNBumzHeNg5SskzUHyiVQjVr1/4ODOQX+wgontXhaHte2VAgMBAAECgYAljox63sXpk70fCq4DMay74P7WYQj/KrEn56S/rXOn8I48TcTGhYr0sT6WdM2O/EU83SSCdTsCzLebo4iK72Mx/VI1alKWSfyncfXi51gZePpgVaudTG9kcI8sszRG+P7zfPptr4HxZ7X2LveJy5myImqQomESEUvDVHdZCtIIgQJBANxT7Bev+LA+jH9gSV4Uc14B5YYabpLso39t/uhTaOdVNxV2eV2UAF1PGg2R3IMwlzHcjRkDDpBiWuXaxbTKCaECQQDRaCUb7J1mPDBrEIi1Aupho16b3Sy82vzZ9WGLgMl+eMKbSy0rAdJA10CtNKL2Gq7EGNoN4CpDPPulJTmm6Cd1AkAajH5BaHHmAtN5McgFbx9rr3zRyPOT/rHA1CdIJWzZmzoU+v6q2P+mPrbb9byFjmBZoMLbxbOGkGN1mQQDweihAkEAkAJ9Mr0AaeSOr7KJMWK16Tu+vpXWRHKdXQ9Ba/y/lThbLQ0AHQl9nJXrprICOBmVgspMeypkJiV0Mdht03joWQJAaF8kDoCNkpp++6aqVbqFBYysiW83AiHgL0JA5dhQ2XzIFYZIpLOsM+Je4yw9ppQ76DqePg6pqRKjR6m9Gatn+A==";
    //测试要加密的数据
    NSString *sourceStr = @"iOS端RSA加密";
    //公钥加密
    NSString *encryptStr = [RSA encryptString:sourceStr publicKey:publicKey];
    //私钥解密
    NSString *decrypeStr = [RSA decryptString:encryptStr privateKey:privateKey];
    
    NSLog(@"公钥加密私钥解密后的数据 %@",decrypeStr);
    
    //私钥加密
    NSString *encryptStr1 = [RSA encryptString:sourceStr privateKey:privateKey];
    //公钥解密
    NSString *decrypeStr1 = [RSA decryptString:encryptStr1 publicKey:publicKey];
    NSLog(@"私钥加密公钥解密后的数据 %@",decrypeStr1);

2.其实第一个很多时候就可以了.对于复杂的APP,有很多接口是不用加密的.这样就出现了防代理的模式:

#pragma mark - 初步方案,判断是否设置代理,如果设置了代理就视为在抓包

- (BOOL)getProxyStatus {

    NSDictionary*proxySettings =  (__bridgeNSDictionary*)(CFNetworkCopySystemProxySettings());

    NSArray *proxies = (__bridge NSArray *)(CFNetworkCopyProxiesForURL((__bridge CFURLRef _Nonnull)([NSURL URLWithString:@"http://www.baidu.com"]), (__bridge CFDictionaryRef _Nonnull)(proxySettings)));

    NSDictionary*settings = [proxiesobjectAtIndex:0];

    NSLog(@"host=%@", [settingsobjectForKey:(NSString*)kCFProxyHostNameKey]);

    NSLog(@"port=%@", [settingsobjectForKey:(NSString*)kCFProxyPortNumberKey]);

    NSLog(@"type=%@", [settingsobjectForKey:(NSString*)kCFProxyTypeKey]);

    if([[settingsobjectForKey:(NSString*)kCFProxyTypeKey]isEqualToString:@"kCFProxyTypeNone"]){

        //没有设置代理

        returnNO;

    }else{

        //设置代理了

        returnYES;

    }

}

这里用的CFNetwork.framework框架,进行代理的获取.
这种接口的处理对于用户开VPN的情况下很多时候是有错误的效果.他其实没有代理想抓你包的意思.但是开的VPN会被认为是代理了,导致无法使用APP.

3.SSL Pinning

通过对服务端生成的.cer证书进行域名校验,服务器通过.crt证书导出.cer放到客户端进行处理.
NSURLSession处理

- (void)URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential * _Nullable))completionHandler {

    //得到远程证书
    SecTrustRef serverTrust = challenge.protectionSpace.serverTrust;
    SecCertificateRef certificate = SecTrustGetCertificateAtIndex(serverTrust, 0);
    //设置ssl政策来检测主域名
    NSMutableArray *policies = [NSMutableArray array];
    [policies addObject:(__bridge_transfer id)SecPolicyCreateSSL(true, (__bridge CFStringRef)challenge.protectionSpace.host)];
    //验证服务器证书
    SecTrustResultType result;
    SecTrustEvaluate(serverTrust, &result);
    BOOL certificateIsValid = (result == kSecTrustResultUnspecified || result == kSecTrustResultProceed);
    //得到本地和远程证书data
    NSData *remoteCertificateData = CFBridgingRelease(SecCertificateCopyData(certificate));
    
    BOOL allChrls = NO;
    allChrls = DebugNet;
    if (allChrls) {
        NSURLCredential *credential = [NSURLCredential credentialForTrust:serverTrust];
        completionHandler(NSURLSessionAuthChallengeUseCredential,credential);
    }else{
        NSString *pathToCer = [[NSBundle mainBundle] pathForResource:@"xiaoqi" ofType:@"cer"];
        NSData *localCertificate = [NSData dataWithContentsOfFile:pathToCer];    //检查
        if ([remoteCertificateData isEqualToData:localCertificate] && certificateIsValid) {
            self.allowTask = YES;
            NSURLCredential *credential = [NSURLCredential credentialForTrust:serverTrust];
            completionHandler(NSURLSessionAuthChallengeUseCredential,credential);
        }else {
            self.allowTask = NO;
            [_dataTask cancel];
            [_downloadTask cancel];
            [_uploadTask cancel];
            completionHandler(NSURLSessionAuthChallengeCancelAuthenticationChallenge,NULL);
        }
    }

    
}


或者是AF里面自定义

// 自定义安全策略
+ (AFSecurityPolicy *)customSecurityPolicy {
    
    // 获取证书
    NSString *cerPath = [[NSBundle mainBundle] pathForResource:@"xiaoqi" ofType:@"cer"];
    NSData *certData = [NSData dataWithContentsOfFile:cerPath];
    NSSet *pinnedCertificates = [[NSSet alloc] initWithObjects:certData, nil];

    /*
     安全模式
     AFSSLPinningModeNone:完全信任服务器证书;
     AFSSLPinningModePublicKey:只比对服务器证书和本地证书的Public Key是否一致,如果一致则信任服务器证书;
     AFSSLPinningModeCertificate:比对服务器证书和本地证书的所有内容,完全一致则信任服务器证书
     */
    AFSecurityPolicy *securityPolicy =
    [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModePublicKey
                     withPinnedCertificates:pinnedCertificates];
    
    // allowInvalidCertificates 是否允许无效证书(也就是自建的证书),默认为NO
    // 如果是需要验证自建证书,需要设置为YES
    securityPolicy.allowInvalidCertificates = YES;
    
    /*
    validatesDomainName 是否需要验证域名,默认为YES;
    假如证书的域名与你请求的域名不一致,需把该项设置为NO;
    如设成NO的话,即服务器使用其他可信任机构颁发的证书,也可以建立连接,这个非常危险,建议打开。
    置为NO,主要用于这种情况:客户端请求的是子域名,而证书上的是另外一个域名。
    因为SSL证书上的域名是独立的,假如证书上注册的域名是www.google.com,那么mail.google.com是无法验证通过的;
    当然,有钱可以注册通配符的域名*.google.com,但这个还是比较贵的。
    如置为NO,建议自己添加对应域名的校验逻辑。
     */
    securityPolicy.validatesDomainName = YES;
    
    return securityPolicy;
}

证书会失效,证书由于是服务端生成的根据域名来的所以一般最长的是一年的证书.所以不能忘了换,不然可能会对接口请求产生问题.
那么还有没有不用证书来校验的方式呢?还能防止抓包呢?

苹果官方文档

CFNetWork

This property controls which proxy tasks within sessions based on this configuration use when connecting to remote hosts.
The default value is NULL, which means that tasks use the default system settings.

这个属性可以设置网络代理,默认值是 NULL,使用系统的代理设置。
configuration.connectionProxyDictionary = @{};

#import "NSURLSession+SafeHttpProxy.h"
#import "SafeURLProtocol.h"
#import <objc/runtime.h>
static BOOL isDisableHttpProxy = YES;
@implementation NSURLSession (SafeHttpProxy)
+(void)load{
    [super load];
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        [NSURLProtocol registerClass:[SafeURLProtocol class]];
        Class class = [NSURLSession class];
        [self swizzingMethodWithClass:class orgSel:NSSelectorFromString(@"sessionWithConfiguration:") swiSel:NSSelectorFromString(@"Safe_sessionWithConfiguration:")];
        [self swizzingMethodWithClass:class orgSel:NSSelectorFromString(@"sessionWithConfiguration:delegate:delegateQueue:") swiSel:NSSelectorFromString(@"Safe_sessionWithConfiguration:delegate:delegateQueue:")];
    });
}
+(void)disableHttpProxy{
    isDisableHttpProxy = YES;
}
+(void)enableHttpProxy{
    isDisableHttpProxy = NO;
}
+(NSURLSession *)Safe_sessionWithConfiguration:(NSURLSessionConfiguration *)configuration
                                    delegate:(nullable id<NSURLSessionDelegate>)delegate
                               delegateQueue:(nullable NSOperationQueue *)queue{
    if (!configuration){
        configuration = [[NSURLSessionConfiguration alloc] init];
    }
    if(isDisableHttpProxy){
        configuration.connectionProxyDictionary = @{};
    }
    return [self Safe_sessionWithConfiguration:configuration delegate:delegate delegateQueue:queue];
}

+(NSURLSession *)Safe_sessionWithConfiguration:(NSURLSessionConfiguration *)configuration{
    if (configuration && isDisableHttpProxy){
        configuration.connectionProxyDictionary = @{};
    }
    return [self Safe_sessionWithConfiguration:configuration];
}

+(void)swizzingMethodWithClass:(Class)cls orgSel:(SEL) orgSel swiSel:(SEL) swiSel{
    Method orgMethod = class_getClassMethod(cls, orgSel);
    Method swiMethod = class_getClassMethod(cls, swiSel);
    method_exchangeImplementations(orgMethod, swiMethod);
}

@end

https://github.com/frankKiwi/FNKSafaNet.git

以上四个方案,建议是第一个加密 和 第四个结合处理.第四个对于H5页面的网络请求还是可以抓到的.

做个记录两年后可能就忘了.

IOS七层协议:
从下到上:
a.物理层:传输的是比特流,网卡位于这层。
b.数据链路层:本层传输的是帧;本层主要定义了如何格式化数据,错误检测。交换机位于本层
c.网络层:本层传输的是数据包,路由器位于本层。本层协议是IP协议(Internet Protocol Address),主要功能是路由选择最短路径,将数据包从发送端路由到接收端
d.传输层:协议有TCP(传输控制协议)/UDP(用户数据报协议);主要是控制重传、数据分割之类的,主要是解决数据之间的传输,和传输质量。是IOS最核心的一层,其中TCP协议是最重要的协议
e.会话层:不同机器之间建立会话
f.表示层:解决不同系统之间的语法问题
g.应用层:应用网络中发送数据:需要注意Http协议(超文本传输协议),Https(超文本传输安全协议)

TCP/IP四层模型
与IOS七层模型相同,从下往上依次为:
a.链路层(物理层+数据链路层),
b.网络层(IP),
c.传输层(TCP),
d.应用层(应用层+表示层+会话层)(HTTP)。

上一篇下一篇

猜你喜欢

热点阅读