iOS移动安全-https(四)

移动开发中,几乎所有的iOS应用程序都会用到iOS的网络API。从抽象程度来看,依次是URL加载系统,Foundation的NSStream接口和Core Core Foundation CFStream接口。从iOS9.0开始,从安全层面考虑,苹果官方开始强制应用使用https,未使用的也要给出具体说明,否则会有下架或则应用审核无法通过的风险。

对于未越狱的iOS设备来说,AppStore和苹果的沙盒机制将许多恶意软件拒之门外, 基本上杜绝了恶意软件的入侵(非越狱)。但除系统安全之外,我们还是面临很多的安全问题:网络信息安全、数据存储安全、进程间的通讯安全、等等诸多方面,每一项涉及也非常广,作为一名合格的开发者,我们除了要关注iOS正向开发过程中的诸多问题之外,跟应该时刻关注iOS安全相关的问题,因为在如今用户越来越注重个人隐私的情况下,安全问题也变得日益重要。

关于https

HTTPS(全称:Hyper Text Transfer Protocol over Secure Socket Layer 或 Hypertext Transfer Protocol Secure,超文本传输安全协议),是以安全为目标的HTTP通道,简单讲是HTTP的安全版。即HTTP下加入SSL层,HTTPS的安全基础是SSL,因此加密的详细内容就需要SSL。 它是一个URI scheme(抽象标识符体系),句法类同http:体系。用于安全的HTTP数据传输。https:URL表明它使用了HTTP,但HTTPS存在不同于HTTP的默认端口及一个加密/身份验证层(在HTTP与TCP之间)。这个系统的最初研发由网景公司(Netscape)进行,并内置于其浏览器Netscape Navigator中,提供了身份验证与加密通讯方法。

本文主要讲解我们最常用的

  • NSURLConnection
  • NSURLSession
  • AFNetworking支持HTTPS的实现

在OC中当使用NSURLConnection或NSURLSession建立URL并向服务器发送https请求获取资源时,服务器会使用HTTP状态码401进行响应(即访问拒绝)。此时NSURLConnection或NSURLSession会接收到服务器需要授权的响应,当客户端授权通过后,才能继续从服务器获取数据。如下图所示:

非自签名证书验证实现

在接收到服务器返回的状态码为401的响应后,对于NSURLSession而言,需要代理对象实现URLSession:task:didReceiveChallenge:completionHandler:方法。对于NSURLConnection而言,需要代理对象实现connection:willSendRequestForAuthenticationChallenge: 方法(OS X v10.7和iOS5及以上),对于早期的版本代理对象需要实现代理对象要实现connection:canAuthenticateAgainstProtectionSpace:和connection:didReceiveAuthenticationChallenge:方法。代码如下(参考文档):

NSURLConnection

NSURLConnectionDelegate在证书验证的过程中定义了处理认证的主要方法

- (BOOL)connectionShouldUseCredentialStorage:(NSURLConnection *)connection;
- (void)connection:(NSURLConnection *)connection willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge;

复制代码
-(BOOL)connectionShouldUseCredentialStorage:(NSURLConnection *)connection{
    return true;
}
复制代码
// Now start the connection
NSURL * httpsURL = [NSURL URLWithString:@"https://www.google.com"];
self.connection = [NSURLConnection connectionWithRequest:[NSURLRequest requestWithURL:httpsURL] delegate:self];

当客户端发送https请求后,服务器会返回需要授权的相关信息,然后connection:willSendRequestForAuthenticationChallenge:方法被调用。客户端根据返回的challenge信息,首先获取需要验证的信任对象trust,然后调用SecTrustEvaluate方法是用系统默认的验证方式对信任对象进行验证,当验证通过时,使用该信任对象trust生成证书凭证,然后self.connection使用该凭证继续连接。如下详解:
    
//回调
- (void)connection:(NSURLConnection *)connection willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge {
 	//1)获取trust object
	SecTrustRef trust = challenge.protectionSpace.serverTrust;
	SecTrustResultType result;
	
	//2)SecTrustEvaluate对trust进行验证
	OSStatus status = SecTrustEvaluate(trust, &result);
	if (status == errSecSuccess &&
		(result == kSecTrustResultProceed ||
       	result == kSecTrustResultUnspecified)) {
       	
       	//3)验证成功,生成NSURLCredential凭证cred,告知challenge的sender使用这个凭证来继续连接
		NSURLCredential *cred = [NSURLCredential credentialForTrust:trust];
		[challenge.sender useCredential:cred forAuthenticationChallenge:challenge];
		
    } else {
    
    	//5)验证失败,取消这次验证流程
    	[challenge.sender cancelAuthenticationChallenge:challenge];
    	
  }
}
复制代码

NSURLSession

NSURLSessionDelegate提供了验证证书的方法

- (void)URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge
completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential * _Nullable credential))completionHandler;
复制代码

如果客户端实现这个方法,当客户端发起服务器请求时,这个代理将有机会向基础连接提供身份验证凭据。某些类型的身份验证将应用于与服务器的给定连接上的多个请求(SSL服务器信任质询)。 如果未实现此委托消息,则行为将使用默认处理,这可能涉及用户交互。

具体实现


 //证书的处理方式
    NSURLSessionAuthChallengeDisposition disposition = NSURLSessionAuthChallengePerformDefaultHandling;
    //网络请求证书
    __block NSURLCredential *credential = nil;
    //判断服务器返回的证书是否是服务器信任的
    if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) { //受信任的
       //获取服务器返回的证书
        credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
        if (credential) {
            disposition = NSURLSessionAuthChallengeUseCredential;
        } else {
            disposition = NSURLSessionAuthChallengePerformDefaultHandling;
        }
    } else {
        disposition = NSURLSessionAuthChallengeCancelAuthenticationChallenge;
    }
    //安装证书
    if (completionHandler) {
        completionHandler(disposition, credential);
    }
复制代码

使用AFNetworking来支持HTTPS AFNetworking上配置对HTTPS的支持非常简单:

NSURL * url = [NSURL URLWithString:@"https://www.google.com"];
AFHTTPRequestOperationManager * requestOperationManager = [[AFHTTPRequestOperationManager alloc] initWithBaseURL:url];
dispatch_queue_t requestQueue = dispatch_create_serial_queue_for_name("kRequestCompletionQueue");
requestOperationManager.completionQueue = requestQueue;
AFSecurityPolicy * securityPolicy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeCertificate];

//allowInvalidCertificates 是否允许无效证书(也就是自建的证书),默认为NO
//如果是需要验证自建证书,需要设置为YES
securityPolicy.allowInvalidCertificates = YES;

//validatesDomainName 是否需要验证域名,默认为YES;
//假如证书的域名与你请求的域名不一致,需把该项设置为NO;如设成NO的话,即服务器使用其他可信任机构颁发的证书,也可以建立连接,这个非常危险,建议打开。置为NO,主要用于这种情况:客户端请求的是子域名,而证书上的是另外一个域名。因为SSL证书上的域名是独立的,假如证书上注册的域名是www.google.com,那么mail.google.com是无法验证通过的;当然,有钱可以注册通配符的域名*.google.com,但这个还是比较贵的。
//如置为NO,建议自己添加对应域名的校验逻辑。
securityPolicy.validatesDomainName = YES;

/* validatesCertificateChain 是否验证整个证书链,默认为YES
    设置为YES,会将服务器返回的Trust Object上的证书链与本地导入的证书进行对比,这就意味着,假如你的证书链是这样的:
*/

/*
    GeoTrust Global CA 
    Google Internet Authority G2
    *.google.com
*/
//那么,除了导入*.google.com之外,还需要导入证书链上所有的CA证书(GeoTrust Global CA, Google Internet Authority G2);
//如是自建证书的时候,可以设置为YES,增强安全性;假如是信任的CA所签发的证书,则建议关闭该验证,因为整个证书链一一比对是完全没有必要(请查看源代码);

securityPolicy.validatesCertificateChain = NO;
requestOperationManager.securityPolicy = securityPolicy;
复制代码

补充:

NSURLAuthenticationChallenge包含如下信息:

  • error :最后一次授权失败的错误信息
  • failureResponse :最后一次授权失败的错误信息
  • previousFailureCount :授权失败的次数
  • proposedCredential :建议使用的证书
  • protectionSpace :NSURLProtectionSpace对象,代表了服务器上的一块需要授权信息的区域。包括了服务器地址、端口等信息。在此指的是challenge.protectionSpace。其中Auth-scheme指protectionSpace所支持的验证方法,NSURLAuthenticationMethodServerTrust指对protectionSpace执行证书验证。

SecTrustRef

表示需要验证的信任对象(Trust Object),在此指的是challenge.protectionSpace.serverTrust。包含待验证的证书和支持的验证方法等。

SecTrustResultType 表示验证结果。其中 kSecTrustResultProceed表示serverTrust验证成功,且该验证得到了用户认可(例如在弹出的是否信任的alert框中选择always trust)。 kSecTrustResultUnspecified表示 serverTrust验证成功,此证书也被暗中信任了,但是用户并没有显示地决定信任该证书。 两者取其一就可以认为对serverTrust验证成功。

SecTrustEvaluate 函数内部递归地从叶节点证书到根证书验证。使用系统默认的验证方式验证Trust Object,根据上述证书链的验证可知,系统会根据Trust Object的验证策略,一级一级往上,验证证书链上每一级证书有效性。

NSURLCredential

表示身份验证证书。URL Lodaing支持3种类型证书:password-based user credentials, certificate-based user credentials, 和certificate-based server credentials(需要验证服务器身份时使用)。因此NSURLCredential可以表示由用户名/密码组合、客户端证书及服务器信任创建的认证信息,适合大部分的认证请求。对于NSURLCredential也存在三种持久化机制:

NSURLCredentialPersistenceNone :要求 URL 载入系统 “在用完相应的认证信息后立刻丢弃”。 NSURLCredentialPersistenceForSession :要求 URL 载入系统 “在应用终止时,丢弃相应的 credential ”。 NSURLCredentialPersistencePermanent :要求 URL 载入系统 “将相应的认证信息存入钥匙串(keychain),以便其他应用也能使用。

对于已经验证通过的信任对象,客户端也可以不提供证书凭证。

对于NSURLSession,传递如下之一的值给completion handler回调:

NSURLSessionAuthChallengePerformDefaultHandling处理请求,就好像代理没有提供一个代理方法来处理认证请求 NSURLSessionAuthChallengeRejectProtectionSpace拒接认证请求。基于服务器响应的认证类型,URL加载类可能会多次调用代理方法。

以上变是NSURLSession 和几个关键类的讲解

参考文章01 iOS安全系列之一:HTTPS NSURLSession发起HTTPS网络请求

转载于:https://juejin.im/post/5c8919846fb9a049df24ee63

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值