SSL pinning在構建一個高度安全的移動APP上扮演了一個十分重要的角色。然而如今好多用戶在使用無線移動設備去訪問無數不安全的無線網絡。
這篇文章主要覆蓋了SSL pinning 技術,來幫助我們處理最常見的安全攻擊--中間人攻擊(MITM)。
SSL(Secure Socket Layer 安全套接字層)確保客戶端-服務器在HTTP請求的方式上將通訊內容加密----被指定為HTTPS(SSL上的HTTP)。這種加密體系是基於PKI(Pbulic Key Infrastructure,公鑰體系)和(Session key,會話key)。其中Session key 被引進的原因是對於公鑰/私鑰的加密和解密會消耗處理能力,會是整個交流進程速度變慢。
SSL 安全---鑒定
SSL安全是基於證書的信任鏈。當一個通信開始的時候,客戶端檢查服務器的SSL證書,檢查這個證書是否被信用根CA機構或者其他用戶信任結構所信任。
盡管SSL通信被認為是一個非常安全的和堅不可摧的,但是中間人攻擊依然還是真實存在的威脅,它可以用好幾種方法去實現,例如ARP 緩存中毒、DNS 欺騙等。
對於ARP緩存中毒,可以簡單理解為中間人通過地址解析協議的IP映射到設備的Mac地址這一特性進行攻擊。例如,我們用一下三個角色來描述一個簡單的網絡:
一個普通的用戶設備U
攻擊者的設備A
路由器R
攻擊者的設備A可以發送一個ARP依賴包給用戶設備U,把他自己偽裝成路由器R。為了完成中間人攻擊,A發送另一個ARP依賴給R,告訴路由器它就是U。這樣的話,A就成了U和R溝通的中間者,A就可以竊聽和攔截信息。IP轉發經常被用於攻擊者在用戶設備和路由器的無縫交流。也就是說通過IP轉發,攻擊者可以竊取和監聽,但是用戶和路由器是無感知的。
DNS欺騙主要在於攻擊者破話服務器的域名映射。攻擊者嘗試強迫DNS去返回一個不正確的地址,而這個地址就是攻擊者的計算機地址。
SSL pinning
我們使用SSL pinning來確保app通信僅僅發生在指定的服務器上。其中的先決條件就是將目標服務器的SSL證書放到app里面。這個證書被用於會話配置。這里我簡單介紹SSL pinning在NSURLSession上的使用。
當談到NSURLSession使用SSL pinning有點棘手,因為在AFNetworking中,其本身已經有封裝好的類可以使用來進行配置。這里沒有辦法去設置一組證書來自動取消所有本地證書不匹配的response。我們需要手動執行檢查來實現在NSURLSession上的SSL pinning。我們很榮幸的是我們可以用Security's framework C API。
我們可以先來一個默認會話配置的NSURLSession對象。
NSURLSessionConfiguration *seeConfig = [NSURLSessionConfiguration defaultSessionConfiguration];
NSURLSession *session = [NSURLSession sessionWithConfiguration:seeConfig delegate:self delegateQueue:nil];
NSURLSession使用NSURLSessionTask來發送一個請求,我么使用dataTaskWithURL:completionHandler:方法來進行SSL pinning 測試。發送請求類似於下面這樣:
NSURLSessionDataTask *task = [session dataTaskWithRequest:[NSURLRequest requestWithURL:testURL]];
[task resume];
該方法僅僅是返回了一個task對象,然后使用resume方法發送請求,或者說是執行任務。
使SSL pinning在:
URLSession:didReceiveChallenge:completionHandler:delegate
方法中實現。在NSURLSession對象上,我們設置自己為代理來調用方法。
- (void)URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential * _Nullable))completionHandler { //得到遠程證書 SecTrustRef serverTrust = challenge.protectionSpace.serverTrust; SecCertificateRef certificate = SecTrustGetCertificateAtIndex(serverTrust, 0); //設置ssl政策來檢測主域名 NSMutableArray *policies = [NSMutableArray array]; [policies addObject:(__bridge_transfer id)SecPolicyCreateSSL(true, (__bridge CFStringRef)challenge.protectionSpace.host)]; //驗證服務器證書 SecTrustResultType result; SecTrustEvaluate(serverTrust, &result); BOOL certificateIsValid = (result == kSecTrustResultUnspecified || result == kSecTrustResultProceed); //得到本地和遠程證書data NSData *remoteCertificateData = CFBridgingRelease(SecCertificateCopyData(certificate)); NSString *pathToCer = [[NSBundle mainBundle] pathForResource:@"brhttp" ofType:@"cer"]; NSData *localCertificate = [NSData dataWithContentsOfFile:pathToCer]; //檢查 if ([remoteCertificateData isEqualToData:localCertificate] && certificateIsValid) { NSURLCredential *credential = [NSURLCredential credentialForTrust:serverTrust]; completionHandler(NSURLSessionAuthChallengeUseCredential,credential); }else { completionHandler(NSURLSessionAuthChallengeCancelAuthenticationChallenge,NULL); } }
在方法的開始,我們使用SecTrustGetCertificateAtIndex來得到服務器的SSL證書數據。然后使用證書評估設置policies。證書使用SecTrustEvaluate評估,然后返回以下幾種認證結果類型之一:
typedef uint32_t SecTrustResultType; enum { kSecTrustResultInvalid = 0, kSecTrustResultProceed = 1, kSecTrustResultConfirm SEC_DEPRECATED_ATTRIBUTE = 2, kSecTrustResultDeny = 3, kSecTrustResultUnspecified = 4, kSecTrustResultRecoverableTrustFailure = 5, kSecTrustResultFatalTrustFailure = 6, kSecTrustResultOtherError = 7 };
如果我們得到kSecTrustResultProceed和kSecTrustResultUnspecified之外的類型結果,我們可以認為證書是無效的(不被信任的)。
至今為止我們除了檢測遠程服務器證書評估外,還沒有做其他事情,對於SSL pinning 檢測我們需要通過SecCertificateRef來得到他的NSData。這個SecCertificateRef來自於challenge.protectionSpace.serverTrust。而本地的NSData來自本地的.cer證書文件。然后我們使用isEqual來進行SSL pinning。
如果遠程服務器證書的NSData等於本地的證書data,那么就可以通過評估,我們可以驗證服務器身份然后進行通信,而且還要使用completionHandler(NSURLSessionAuthChallengeUseCredential,credential)執行request。
然而如果兩個data不相等,我們使用completionHandler(NSURLSessionAuthChallengeCancelAuthenticationChallenge,NULL)方法來取消dataTask的執行,這樣就可以拒絕和服務器溝通。
這就是在NSURLSession中使用SSL pinning。