移动开发中,几乎全部的iOS应用程序都会用到iOS的网络API。从抽象程度来看,依次是URL加载系统,Foundation的NSStream接口和Core Core Foundation CFStream接口。从iOS9.0开始,从安全层面考虑,苹果官方开始强制应用使用https,未使用的也要给出具体说明,不然会有下架或则应用审核没法经过的风险。浏览器
对于未越狱的iOS设备来讲,AppStore和苹果的沙盒机制将许多恶意软件拒之门外, 基本上杜绝了恶意软件的入侵(非越狱)。但除系统安全以外,咱们仍是面临不少的安全问题:网络信息安全、数据存储安全、进程间的通信安全、等等诸多方面,每一项涉及也很是广,做为一名合格的开发者,咱们除了要关注iOS正向开发过程当中的诸多问题以外,跟应该时刻关注iOS安全相关的问题,由于在现在用户愈来愈注重我的隐私的状况下,安全问题也变得日益重要。安全
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中,提供了身份验证与加密通信方法。bash
本文主要讲解咱们最经常使用的服务器
- 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:方法。代码以下(参考文档):session
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];
}
}
复制代码
NSURLSessionDelegate提供了验证证书的方法ui
- (void)URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge
completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential * _Nullable credential))completionHandler;
复制代码
若是客户端实现这个方法,当客户端发起服务器请求时,这个代理将有机会向基础链接提供身份验证凭据。某些类型的身份验证将应用于与服务器的给定链接上的多个请求(SSL服务器信任质询)。 若是未实现此委托消息,则行为将使用默认处理,这可能涉及用户交互。google
//证书的处理方式
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执行证书验证。
表示须要验证的信任对象(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 和几个关键类的讲解