关于Https安全性问题、双向验证防止中间人攻击问题

本文深入探讨了HTTPS的安全机制,解析了为什么HTTPS数据仍可能被截取,并介绍了如何通过HTTPS双向验证来增强安全性,防止中间人攻击。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

最近项目中遇到一个问题,安全测试时反馈出,利用fiddler截取到了客户端与后台的https接口的明文内容,这是一个可怕的问题,那么接下来我从下面几点做一些概述和代码举例。

1、Https比Http安全性? 
是的,Https是以安全为目标的Http版本,在Http上使用SSL层,进行加密传输(对称和非对称加密),还具备身份验证的功能(下面会重点说HTTPS的双向验证)。当然,Https是需要配置CA证书的,一般要从某些机构购买。

2、为何Https依然会被截取? 
目前的一些抓包工具,fiddler、charles使用了叫做man-in-the-middle(中间人)机制:

著作权归作者所有。 商业转载请联系作者获得授权,非商业转载请注明出处。 
作者:连山归藏 
来源:知乎 
第一步, fiddler向服务器发送请求进行握手, 获取到服务器的CA证书, 用根证书公钥进行解密, 验证服务器数据签名,获取到服务器CA证书公钥。 
第二步, fiddler伪造自己的CA证书, 冒充服务器证书传递给客户端浏览器,客户端浏览器做跟fiddler一样的事。 
第三步, 客户端浏览器生成https通信用的对称密钥,用fiddler伪造的证书公钥加密后传递给服务器, 被fiddler截获。 
第四步, fiddler将截获的密文用自己伪造证书的私钥解开,获得https通信用的对称密钥。 
第五步, fiddler将对称密钥用服务器证书公钥加密传递给服务器, 服务器用私钥解开后建立信任,握手完成, 用对称密钥加密消息, 开始通信。 
第六步, fiddler接收到服务器发送的密文, 用对称密钥解开,获得服务器发送的明文。再次加密, 发送给客户端浏览器。 
第七步, 客户端向服务器发送消息, 用对称密钥加密, 被fidller截获后,解密获得明文。 
由于fiddler一直拥有通信用对称密钥, 所以在整个https通信过程中信息对其透明。也就是说fiddler&charles向服务器冒充是客户端,向客户端又谎称自己是服务器。

这就解释了问什么截取到了Https的数据,并且,Https在传输过程加密,所以到了客户端已经不是Https传输加密的范围了

这个时候其实很多人都是这么解决的,对Https数据先自己进行加密(Ex:AES、RSA),再Https传输,

这样即使截取到Https数据,也看不到明文。

那么有没有方式可以不让fiddler&charles这种家伙截取到Https呢?

有,就是Https的双向验证;

3、Https双向验证 
服务器端对请求它的客户端要进行身份验证,客户端对自己所请求的服务器也会做身份验证。

服务端一旦验证到请求自己的客户端为不可信任的,服务端就拒绝继续通信。

客户端如果发现服务端为不可信任的,那么也中止通信。

具体怎么做呢?

首先我们需要服务器端到处证书给我们,拿我公司举例,后台给我一个.cer文件,

我用它导出一个.p12文件,将这两个导入Xcode工程resource目录下: 
这里写图片描述 


接下来,有大致三种方式来进行双向认证: 
(1)用NSURLConnection,需添加俩delegate

- (BOOL)connection:(NSURLConnection *)connection canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)protectionSpace{
  return [protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust];
}
- (void)connection:(NSURLConnection *)connection willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge {
    static CFArrayRef certs;
        if (!certs) {
            NSData*certData =[NSData dataWithContentsOfFile:[[NSBundle mainBundle]
                                            pathForResource:@"srca" ofType:@"cer"]];
            SecCertificateRef rootcert
            =SecCertificateCreateWithData(kCFAllocatorDefault,CFBridgingRetain(certData));
            const void *array[1] = { rootcert };
            certs = CFArrayCreate(NULL, array, 1, &kCFTypeArrayCallBacks);
            CFRelease(rootcert);    // for completeness, really does not matter
        }

        SecTrustRef trust = [[challenge protectionSpace] serverTrust];
        int err;
        SecTrustResultType trustResult = 0;
        err = SecTrustSetAnchorCertificates(trust, certs);
        if (err == noErr) {
            err = SecTrustEvaluate(trust,&trustResult);
        }
        CFRelease(trust);
        BOOL trusted = (err == noErr) 
                       && ((trustResult == kSecTrustResultProceed)
                       ||(trustResult == kSecTrustResultConfirm) 
                       || (trustResult == kSecTrustResultUnspecified));

        if (trusted) {
            [challenge.sender useCredential:[NSURLCredential
                         credentialForTrust:challenge.protectionSpace.serverTrust]
                 forAuthenticationChallenge:challenge];
        }else{
            [challenge.sender cancelAuthenticationChallenge:challenge];
        }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36

 
(2)使用AFNetworking

- (void)getNBString:(void(^)(BOOL))completion
{
  SecIdentityRef identity = NULL;
  SecTrustRef trust = NULL;
  NSString *p12 = [[NSBundle mainBundle] pathForResource:@"cc"
                                                  ofType:@"p12"];
  NSData *PKCS12Data = [NSData dataWithContentsOfFile:p12];

  [self extractIdentity:&identity 
           andTrust:&trust
         fromPKCS12Data:PKCS12Data];
  NSString *url = [NSString stringWithFormat:@"%@/service/GetAPPValue", kDefaultHost];

  AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
  [manager.requestSerializer setValue:@"1" 
           forHTTPHeaderField:@"Platform"];
  [manager.requestSerializer setValue:kAppId 
           forHTTPHeaderField:@"AppId"];
  [manager.requestSerializer setValue:AppVersion()
                   forHTTPHeaderField:@"AppVersion"];
  [manager.requestSerializer setValue:@"application/json" 
           forHTTPHeaderField:@"Accept"];
  [manager.requestSerializer setValue:@"application/json" 
           forHTTPHeaderField:@"Content-Type"];
  manager.responseSerializer.acceptableContentTypes 
    = [NSSet setWithArray:@[@"application/json", @"text/html"]];
  manager.securityPolicy = [self customSecurityPolicy];

  [manager GET:url parameters:nil progress:^(NSProgress * _Nonnull downloadProgress) {

  } success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
    [HardCodingKeyManager getInstance].nbString = responseObject[@"Data"];
    completion(YES);
  } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
    NSLog(@"%@", error);
    completion(NO);
  }];

}

- (BOOL)extractIdentity:(SecIdentityRef *)outIdentity
               andTrust:(SecTrustRef*)outTrust
         fromPKCS12Data:(NSData *)inPKCS12Data {
  OSStatus securityError = errSecSuccess;

  // 证书密钥
  NSDictionary *optionsDictionary = @{@"testHttps": (__bridge id)kSecImportExportPassphrase};
  CFArrayRef items = CFArrayCreate(NULL, 0, 0, NULL);
  securityError = SecPKCS12Import(
    (__bridge CFDataRef)inPKCS12Data,
    (__bridge CFDictionaryRef)optionsDictionary, &items);

  if (securityError == 0) {
    CFDictionaryRef myIdentityAndTrust = CFArrayGetValueAtIndex (items, 0);
    const void *tempIdentity = NULL;
    tempIdentity = 
       CFDictionaryGetValue (myIdentityAndTrust, kSecImportItemIdentity);
    *outIdentity = (SecIdentityRef)tempIdentity;
    const void *tempTrust = NULL;
    tempTrust = 
       CFDictionaryGetValue (myIdentityAndTrust, kSecImportItemTrust);
    *outTrust = (SecTrustRef)tempTrust;
  } else {
    NSLog(@"Failed with error code %d",(int)securityError);
    return NO;
  }
  return YES;
}

// 设置manager的AFSecurityPolicy属性
- (AFSecurityPolicy*)customSecurityPolicy {
  NSString *cerPath = [[NSBundle mainBundle] pathForResource:@"ClientC" 
                              ofType:@"cer"];
  NSData *certData = [NSData dataWithContentsOfFile:cerPath];
  AFSecurityPolicy *securityPolicy = 
     [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeCertificate];
  [securityPolicy setAllowInvalidCertificates:NO]; // 记得设置为NO
  NSSet *set = [NSSet setWithArray:@[certData]];
  [securityPolicy setPinnedCertificates:set];
  /**** SSL Pinning ****/
  return securityPolicy;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82


(3)使用ASIHttpRequest 
我们的项目老框架都是这种方式,苦逼的是我试了很多方法,都没有双向认证生效,大家有好方法,可以分享,这里我就不贴了。

当我们配置完毕,使用charles&fiddler检测时,https的接口都不会connect,提示证书不受信任,可能是伪服务器之类的提示,成功。

亲测,即便我们的.p12被人拿到,导入charles&fiddler,作为它们的root certificater,

只要[securityPolicy setAllowInvalidCertificates:NO]; // 记得设置为NO,依然无法connect。

原文地址:http://blog.youkuaiyun.com/u010731949/article/details/50538280

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值