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中,提供了身份驗證與加密通信方法。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

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提供了驗證證書的方法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執行證書驗證。

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網絡請求

相關文章
相關標籤/搜索