iOS开发中的https(二):https服务器的简单用例

来源:互联网 发布:mac idea搭建ssh框架 编辑:程序博客网 时间:2024/06/05 08:24

基于对HTTPS运行机制的理解,我们知道,在iOS客户端实现与服务器的HTTPS通信,前提条件是你服务器是一个提供了HTTPS的服务器。如果前提得以满足,那么iOS客户端就需要向服务器发出请求索要公钥,而后验证公钥,然后进行握手,左后开始加密通信。这些基础性工作,苹果官方已经做好了,AFN也已经做好了,本文主要说明基于AFN的通信。

HTTPS的服务器配置的证书分两大类,一类是经过权威机构签名颁发的证书,这样证书通常是要花钱买服务的,当然现在也有少数机构提供免费的证书签名服务。另一类就是服务器配置的是研发人员自己签名生成的证书。

访问经过CA认证签发的Https接口

经过CA认证的HTTPS服务器是最好的,在iOS客户端这里基本上不需要做太多ATS适配,现在我们就尝试一下。

检测一个HTTPS服务器是符合ATS特性中的要求的呢?使用nscurl命令如下:

nscurl --ats-diagnostics --verbose https://example.com

最后的网址可以是想要检测的服务器地址。例如,检测知乎服务器:

nscurl --ats-diagnostics --verbose https://www.zhihu.com

知乎在所有测试的案例中的Result都是PASS,说明通过了ATS检测。

  • 首先,去掉info.plist文件中App Transport Security Settings及其子项Allow Arbitrary Loads,让ATS恢复到默认状态。
  • 其次,修改viewDidLoad方法中的代码,主要将url修改为HTTPS服务器的url
- (void)viewDidLoad {    [super viewDidLoad];    // 将上次实验的URL注释掉    // NSString *urlString = @"http://api.jirengu.com/weather.php";    // NSString *urlString = @"http://itunes.apple.com/search?term=metallica";    // 使用baidu的HTTPS链接    NSString *urlString = @"https://www.baidu.com";    NSURL *url = [NSURL URLWithString:urlString];    AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];    [manager GET:url.absoluteString parameters:nil progress:nil success:^(NSURLSessionDataTask *task, id responseObject) {        NSLog(@"results: %@", responseObject);    } failure:^(NSURLSessionDataTask *task, NSError *error) {        NSLog(@"results: %@", error);    }];}

运行报错,通过查询,发现问题是因为AFNetworking默认把请求的相应结果认为是JSON数据,然而实际上我们输入百度的地址,得到的应该是html数据。但是AFNetworking并不知道,它坚信请求的结果就是一个json文本,然后固执地以json的形式去解析,这样显然没办法把一个网页解析成一个字典或者数组,所以产生了上述错误。

解决办法很简答,在代码中添加一句:

manager.responseSerializer = [AFHTTPResponseSerializer serializer]

告诉AFNetworking别把这个网页当成JSON数据来解析!于是修改代码如下:

- (void)viewDidLoad {    [super viewDidLoad];    // 将上次实验的URL注释掉    // NSString *urlString = @"http://api.jirengu.com/weather.php";    // NSString *urlString = @"http://itunes.apple.com/search?term=metallica";    // 使用baidu的HTTPS链接    NSString *urlString = @"https://www.baidu.com";    NSURL *url = [NSURL URLWithString:urlString];    AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];    manager.responseSerializer = [AFHTTPResponseSerializer serializer]    [manager GET:url.absoluteString parameters:nil progress:nil success:^(NSURLSessionDataTask *task, id responseObject) {        NSLog(@"results: %@", responseObject);    } failure:^(NSURLSessionDataTask *task, NSError *error) {        NSLog(@"results: %@", error);    }];}

运行项目,得到输出如下:

2016-10-08 20:00:18.052 HTTPS[1916:359510] results: <3c21444f 43545950 45206874 6d6c3e0a 3c68746d 6c3e0a20 2020203c 212d2d53 54415455 53204f4b 2d2d3e0a 20202020 3c686561 643e0a20 20202020 2020203c  ...... 6c3e0a>Message from debugger: Terminated due to signal 15

显然,这次成功地获取到了数据。将HTTPS链接换为知乎的URL,得到的结果类似。

由此可知,对于符合ATS要求的HTTPS服务器,在iOS端不需要对ATS做特殊的适配就能和HTTPS服务器进行通信。而要符合ATS要求,则需要老老实实地创建证书请求,然后到权威机构认证,随之配置到服务器。

AFN调用使用我们自己签名证书的HTTPS接口

对于使用我们自己签名的证书来说,浏览器打开web站点也会默认阻止访问,除非用户手动把该站点加入信任列表,这个手动加入的过程其实就是不去验证服务器的合法性,任性的认为服务器是可信赖的。
那么手动加入信任列表,这样会导致证书的验证过程压根没发生,虽然可以成功访问目标服务器返回我们需要的数据,其实,这中间很有可能返回的数据不是正真的目标服务器返回的数据,也可能是网络传输中间的第三者伪装返回的数据。传输的数据被人窃取甚至纂改都是很可能的。

不正确的做法

浏览器手动加入自签名站点到信任列表这个操作的功能相当于iOS开发中AFNetworking的API的如下做法:

A. 非权威机构颁发证书的HTTPS请求一样必须先在Info.plist设置如下:

```<key>NSAppTransportSecurity</key><dict>    <key>NSAllowsArbitraryLoads</key>    <true/></dict>```

B. AFNetworking代码设置SecurityPolicy
站点 https://tv.diveinedu.com 是我前面博客所讲的配置方法配置的自签名证书。

 AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];    //允许非权威机构颁发的证书    manager.securityPolicy.allowInvalidCertificates = YES;    //也不验证域名一致性    manager.securityPolicy.validatesDomainName = NO;    //关闭缓存避免干扰测试    manager.requestSerializer.cachePolicy = NSURLRequestReloadIgnoringLocalCacheData;    [manager GET:@"https://tv.diveinedu.com/channel/" parameters:nil progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {        NSLog(@"%@",responseObject);    } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {        NSLog(@"%@",error);    }];

经过如上两步设置之后,我们可以在iOS应用中访问我们采用自签名证书的HTTPS站点了。但是这个是不安全的,因为他在没有使用HTTPS/SSL代理和使用像Charles那样的HTTPS/SSL代理的情况下都可以访问服务器资源. 完全可以说是白费功夫,只能防止“君子”在网络中用Wireshark之类来TCP抓包嗅探。因为毕竟还是HTTPS加密了传输数据了。那为什么我要说这样是白费功夫呢,因为这个办法不能防止中间人攻击!比如用户可以给手机设置HTTPS的SSL代理(比如Charles),完全可以在代理中看到明文数据,所以,既然用了HTTPS就要防止中间人攻击,不然还不如不用HTTPS。

下面我们来看看用Charles代理抓包工具所抓到的HTTPS传输的数据:
这里写图片描述
上图就是用Charles抓取到的网易新闻的https的数据。

正确的做法

我们要在App端严格验证服务器的合法性,防止网络中间的代理或者防火墙进行中间人的攻击和证书欺骗,那么我们需要把服务器配置的证书打包到客户端程序中(私钥留服务器不要分发不用泄露,非常重要),在代码里去读取该证书/公钥信息和服务器返回的进行匹配验证.
在iOS开发中,从Xcode7和iOS9开始,Apple提升了App的网络安全性,App默认只能进行对采用权威机构签名颁发证书的Web站点进行访问(信任的HTTPS),而自签名的证书的HTTPS站点也被列为属于例外,所以我们需要在App的Info.plist中单独为我们的域名设置Exception Domains白名单”,而不是打开Allow Arbitrary Loads全部放开,设置信息如下:

<key>NSAppTransportSecurity</key><dict>    <key>NSExceptionDomains</key>    <dict>        <key>tv.diveinedu.com</key>        <dict>            <key>NSExceptionAllowsInsecureHTTPLoads</key>            <true/>        </dict>    </dict></dict>

这样就不像上面那个方法那样一刀切全部放开, 而是单独为某个域名放开设置.当然上面也可以使用放开全部的设置NSAllowsArbitraryLoads为true.但是建议使用白名单.

除此之外,要做到严格验证防止像Charles那样的中间人代理抓包,AFNetworking代码应该用如下设置:

//服务器端配置的包含公钥的证书分发到客户端后,需要转换为DER格式的证书文件.    //openssl x509 -outform der -in tv.diveinedu.com.crt -out tv.diveinedu.com.der    NSString *certFilePath = [[NSBundle mainBundle] pathForResource:@"tv.diveinedu.com" ofType:@"der"];    NSData *certData = [NSData dataWithContentsOfFile:certFilePath];    NSSet *certSet = [NSSet setWithObject:certData];    AFSecurityPolicy *policy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModePublicKey withPinnedCertificates:certSet];    policy.allowInvalidCertificates = YES;    AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];    manager.securityPolicy = policy;    //关闭缓存避免干扰测试    manager.requestSerializer.cachePolicy = NSURLRequestReloadIgnoringLocalCacheData;    [manager GET:@"https://tv.diveinedu.com/channel/" parameters:nil progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {        NSLog(@"%@",responseObject);    } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {        NSLog(@"%@",error);    }];

上面的代码能够验证服务器身份在没有使用代理的时候可以正常访问服务器的资源,但是一旦用户给手机网络设置使用了Charle那样的HTTPS/SSL代理服务,则会出现服务器证书验证失败,SSL网络连接会断开,老板再也不用担心数据接口被人抓包或者代理给扒出来了.故达到防止中间人攻击的效果.

参考文章:

iOS开发中的HTTPS

打造安全的App!iOS安全系列之 HTTPS

iOS应用网络安全之HTTPS

0 0
原创粉丝点击