关于Https安全性问题、双向验证防止中间人攻击问题
2016-11-02 11:08
183 查看
关于Https安全性问题、双向验证防止中间人攻击问题
分类:iOSSSLhttps双向认证Fiddlecharles(1361) (0)
关于Https安全性问题、双向验证防止中间人攻击问题
最近项目中遇到一个问题,安全测试时反馈出,利用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。
相关文章推荐
- 关于Https安全性问题、双向验证防止中间人攻击问题
- 关于Https安全性问题、双向验证防止中间人攻击问题
- 关于Okhttp3 https双向验证实现代码
- 一套.net窗体身份验证方案(解决了防止用户重复登陆,session超时等问题)
- 关于验证控件和javaSript验证的共存问题
- 关于"无法验证数据"的问题
- 关于validwhen验证的问题
- 关于Form验证方式的cookie持久性问题
- 关于VTP Domain默认验证的问题
- 一套.net窗体身份验证方案(解决了防止用户重复登陆,session超时等问题)
- 一套.net窗体身份验证方案(解决了防止用户重复登陆,session超时等问题)
- 关于跨页提交与验证控件的问题
- 关于struts表单验证应注意的几个小问题
- 关于Windows Media Player11需验证问题的解决
- 一套.net窗体身份验证方案(解决了防止用户重复登陆,session超时等问题)
- 关于网站安全性的问题
- 碰上一个关于用户验证的问题,不知道您有没有解决方法
- 关于web中的session以及cookie验证的问题
- (转)一套.net窗体身份验证方案(解决了防止用户重复登陆,session超时等问题)
- 关于PHP中出现乱码和Sessions验证问题的解决方法!