標籤:
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。
附源碼:https://pan.baidu.com/s/1mio6ZnM
如何使用SSL pinning來使你的iOS APP更加安全