HTTP防劫持方案
HTTP劫持是在使用者與其目的網路服務所建立的專用資料通道中,監視特定資料資訊,提示當滿足設定的條件時,就會在正常的資料流中插入精心設計的網路資料報文,目的是讓使用者端程式解釋”錯誤”的資料,並以彈出新視窗的形式在使用者介面展示宣傳性廣告或者直接顯示某網站的內容。
什麼是HTTP劫持
在運營商的路由器節點上,設定協議檢測,一旦發現是HTTP請求,而且是html型別請求,則攔截處理。後續做法往往分為2種,1種是類似DNS劫持返回302讓使用者瀏覽器跳轉到另外的地址,還有1種是在伺服器返回的 HTML 資料中插入 js 或 dom 節點,從而使網頁中出現自己的廣告等等垃圾資訊。
看圖你就懂了一切(雖然被劫持我也很絕望)
URL Loading System
官方文件
1 2 3 |
The URL loading system is a set of classes and protocols that allow your app to access content referenced by a URL. At the heart of this technology is the NSURL class, which lets your app manipulate URLs and the resources they refer to. |
NSURLCache
NSURLCache 為 URL 請求提供了記憶體中以及磁碟上的綜合快取機制。 作為基礎類庫 URL Loading System 的一部分,任何通過 NSURLConnection 載入的請求都將被 NSURLCache 處理。
當一個請求完成得到來自伺服器的Response,在本地儲存作為cache。下一次同一個請求再發起時,本地儲存的Response就會馬上返回,不需要連線伺服器。NSURLCache 會 自動 且 透明 地返回迴應。
在NSURLConnection載入系統中,快取被設計為request物件的一個屬性,由NSURLRequest物件的cachePolicy屬性指定。而在NSURLSession載入系統中,快取被設計為 NSURLSessionConfiguration對像的一個屬性,該屬性所指定的策略被該session的所有request所共享。
作為一個Cache,它標頭檔案中提供的方法並不複雜,就是基本的增刪查改,(其中增和改可以算是一個功能,沒有就增,改就是覆蓋)。主要方法僅六個:
1 2 3 4 5 6 7 8 9 10 |
// 初始化方法 - (instancetype)initWithMemoryCapacity:(NSUInteger)memoryCapacity diskCapacity:(NSUInteger)diskCapacity diskPath:(nullable NSString *)path; // 查詢方法 - (nullable NSCachedURLResponse *)cachedResponseForRequest:(NSURLRequest *)request; // 儲存方法 - (void)storeCachedResponse:(NSCachedURLResponse *)cachedResponse forRequest:(NSURLRequest *)request; // 刪除方法 - (void)removeCachedResponseForRequest:(NSURLRequest *)request; - (void)removeAllCachedResponses; - (void)removeCachedResponsesSinceDate:(NSDate *)date NS_AVAILABLE(10_10, 8_0); |
當然,在專案中為了方便,我們一般都會實現一個子類,當系統呼叫URLCache的增刪改查方法時,由子類來接管系統的URLCache功能。 NSURLCache的快取策略,以及和HTTP header之間的關係,可以參考NSHipster NSURLCache文章
js簽名防注入
這個方法主要用於資訊類App,就像文章開頭那樣的情況 步驟如下: (1).發版前,在 bundle 中存一份最新的前端js檔案 (2).後臺在返回 js 檔案 URL 的時候,對 js 檔案內容進行 SHA-256 ,得到的 hash 值拼接到 js 的檔名中 (3).請求 js 資原始檔時,在NSURLCache中的- (NSCachedURLResponse )cachedResponseForRequest:(NSURLRequest )request方法中攔截請求 (4).攔截請求之後,判斷本地是否有快取,如果有,則直接返回快取檔案包裝成 response (5).下載 js 資源時,走NSURLProtocol 代理方法- (void)connection:(NSURLConnection )connection didReceiveData:(NSData )data,對 data 進行SHA-256簽名比對,如果簽名一致,將 data 通過;如果簽名不一致,代表 js 被汙染,直接丟棄,從bundle取出本地預存的 js 檔案返回回來。
基本邏輯如下(需要用到上文中NSURLCache實現的子類) URLCache實現檔案中:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
- (NSCachedURLResponse *)cachedResponseForRequest:(NSURLRequest *)request { NSString* ua = [request valueForHTTPHeaderField:@"User-Agent"]; if (!EmptyString(ua) && [ua lf_containsSubString:@"AppleWebKit"]) { if ([CacheManager shouldVerifyHashCode:request]) { //包含64位hashcode的js css檔案 // 取本地JS快取 NSData *resultData = [CacheManager getJSCache]; if (resultData) { NSURLResponse *response = [[NSURLResponse alloc] initWithURL:request.URL MIMEType:nil expectedContentLength:resultData.length textEncodingName:nil]; return [[NSCachedURLResponse alloc] initWithResponse:response data:resultData]; } else { return nil; } } return [super cachedResponseForRequest:request]; } - (void)storeCachedResponse:(NSCachedURLResponse *)cachedResponse forRequest:(NSURLRequest *)request { NSString* ua = [request valueForHTTPHeaderField:@"User-Agent"]; if ([CacheManager shouldVerifyHashCode:request] && [ua lf_containsSubString:@"AppleWebKit"]) { // 將請求回來的,並且通過驗證的新js放到快取中 [[CacheManager defaultManager] storeCachedResponse:cachedResponse forRequest:request]) return; } [super storeCachedResponse:cachedResponse forRequest:request]; } |
自定義的NSURLProtocol子類:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
// 初始化方法 + (BOOL)canInitWithRequest:(NSURLRequest *)request { NSString *ua = [request valueForHTTPHeaderField:@"User-Agent"]; if ([CacheManager shouldVerifyHashCode:request] && [ua lf_containsSubString:@"AppleWebKit"]) { // 攔截js請求 return YES; } return NO; } // 收到請求返回data的代理方法 - (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data { if([data verifySHA256Success]) { [self.client URLProtocol:self didLoadData:data]; } else { localData = [CacheManager bundleCacheFromUrl:url]; [self.client URLProtocol:self didLoadData:localData]; } } |
這裡不用擔心不能更新js檔案,因為當後臺的 js 檔案有更新時,新 js 檔案的簽名就會發生變化,js 檔案的URL也就自然變化,於是本地請求的時候,快取是無法命中的,所以,也就會直接走下載 js 的那個路徑。
缺點如下: (1).在發生 js 劫持的時候,只能使用本地 js,可能會比最新版本 js 落後 (2).js 檔案必須是由自己的服務端提供,並控制,才好對 js 進行簽名,所以適用範圍略窄 作為這個方案的擴充,可以考慮再次利用NSURLProtocol,當發現 js 被汙染,重定向URL,此URL由服務端返回一個加密的 js 檔案,對稱加密,金鑰插入在 js 的密文中,本地解密 js 檔案,就可以保證得到最新的,安全的 js 檔案了。
總結
本文總結的是HTTP的防劫持,其實現在蘋果也建議我們使用HTTPS,是啊,如果大家都使用了HTTPS哪還有這麼多事呢,但是蘋果框架下的URL Loading System需要我們研究的東西還是比較多的,長路漫漫,Fighting~