補充更新完善app安全方案:2019.12.13
證書放到app里面,用來校驗信任鏈的話,如果別人使用青花瓷抓包,并且把抓包工具生成的證書在抓包的機器上導出來,替換掉我們app內部的證書,然后對app進行簽名,那么仍舊可以使用青花瓷抓我們的包。對于這種情況,我想到了2種方案:
1. 檢驗app的簽名方式并且簽發團隊(常規app,不需要重簽名分發)
蘋果商店下載的app包里面是沒有embedded.mobileprovision文件的,而且是沒被砸殼的,可以認為沒有這個文件是我們蘋果商店的版本,是不會被替換證書校驗機制的,所以可以認為是安全的,如果我們考慮到一些我們自己分發的測試包或者adHoc包的時候,就可能存在這個文件,這個時候就需要校驗是否我們自己的開發團隊TeamId即可,
代碼如下:
- (BOOL)isFromLiangJiSign
{
//取出embedded.mobileprovision這個描述文件的內容進行判斷
NSString *mobileProvisionPath = [[NSBundle mainBundle] pathForResource:@"embedded" ofType:@"mobileprovision"];
NSData *rawData = [NSData dataWithContentsOfFile:mobileProvisionPath];
if (rawData == nil) {
return NO;
}
NSString *rawDataString = [[NSString alloc] initWithData:rawData encoding:NSASCIIStringEncoding];
NSRange plistStartRange = [rawDataString rangeOfString:@"<plist"];
NSRange plistEndRange = [rawDataString rangeOfString:@"</plist>"];
if (plistStartRange.location != NSNotFound && plistEndRange.location != NSNotFound) {
NSString *tempPlistString = [rawDataString substringWithRange:NSMakeRange(plistStartRange.location, NSMaxRange(plistEndRange))];
NSData *tempPlistData = [tempPlistString dataUsingEncoding:NSUTF8StringEncoding];
NSDictionary *plistDic = [NSPropertyListSerialization propertyListWithData:tempPlistData options:NSPropertyListImmutable format:nil error:nil];
NSArray *applicationIdentifierPrefix = [plistDic valueForKey:@"ApplicationIdentifierPrefix" ];
NSDictionary *entitlementsDic = [plistDic valueForKey:@"Entitlements"];
NSString *mobileBundleID = [entitlementsDic valueForKey:@"application-identifier"];
if (applicationIdentifierPrefix.count > 0 && mobileBundleID != nil) {
if ([mobileBundleID isEqualToString:@"自己的團隊teamID"]) {
return YES;
}
}
}
return NO;
}
如此一來,即使別人拿到我們的app,進行重簽名之后,我們不讓app運行,直接退出即可,(此處不考慮別人再反編譯修改我們的代碼,因為那個時候,我們完全無能為力,除了增加編譯難度,花符號或者混淆問題,最終逆向出我們的app都是時間問題)
2. https請求和響應的數據是加密后的格式
對證書加密的數據再次進行rsa加密(千萬保留好我們的加密私鑰),這樣即使對方已經將證書替換,那么看到的數據仍舊是一堆加密后的數據亂碼,必須解密之后才能得到有效數據(一些直播的app大部分是用的這種方式)
網上數據抓包,在當前抓包工具橫行的時代,對于一個IT開發者來說,是一個很簡單的必備的技能,例如青花瓷(Charles)等等工具.
在講https抓包之前,必須要了解https的整個校驗和通信過程,我們就簡單的精簡的畫一下重要的過程,至于什么三個隨機數或者通信秘鑰的生成就不詳細介紹,主要是針對講一下https的中間人攻擊(https抓包的實現基礎)過程
在這個過程中,正常的話,如果哪個步驟出現問題,鏈接都會停止,無法進行通信,這個是https簡單的校驗的一個過程介紹.
那么,https在抓包工具中是如何實現抓包的呢?
抓包工具就是在上面的過程中,證書認證生成通信密鑰中做了手腳.
以青花瓷為例,大家使用青花瓷抓http請求時,由于沒有做安全校驗,很容易就實現了數據攔截和轉發,至于https呢?
中間人攻擊的情形
抓取https包的時候,青花瓷會要求使用者 對抓包的設備(手機或其他設備)
,安裝一個證書,安裝這個證書的時候,其實是安裝了一個根證書(允許頒發CA的一個證書機構的根證書),當你安裝了該根證書之后,該證書機構頒發的其他證書,默認都會被你的系統所信任,這個就是青花瓷完成https抓包的一個重要前提!!
(如果不太了解證書信任鏈為什么會這樣,可以看一下這個文章,關于iOS系統對https信任鏈的校驗關系,
地址: http://www.lxweimin.com/p/2cae04922e9c)
當客戶端設置了代理,并且開始發出網絡請求的時候,這個網絡請求的校驗過程就會變成這樣
- 當客戶端Client對服務器Server發送請求(帶著隨機數和加密算法),由于青花瓷做了代理,請求被青花瓷攔截,處理(青花瓷的角色現在對于Client來說是服務器),青花瓷將客戶端帶的隨機數和加密算法處理,然后返回自己的證書通過客戶端校驗,獲取到客戶端提交的請求參數等數據,
- 青花瓷作為客戶端(自己去產生隨機數和攜帶支持的加密算法)去請求剛剛Client想要請求的Server,然后,Server會和青花瓷完成上面講的那個完整的校驗,并且讀取青花瓷帶錯來的具體請求,返回正常的數據結果.
- 青花瓷得到服務器數據的返回結果之后,開始繼續和過程1中的Client以服務器的身份,去做處理,首先收到客戶端的隨機數和加密算法,自己生成一個隨機數和選擇一個客戶端的加密算法,然后*********重要********** 青花瓷會返回一個偽造的CA證書(公鑰和真實的server不一樣,但是域名是一樣的,或者說,除了域名是一致的,其他的都不是一致的,而且這個簽發機構是青花瓷之前讓你安裝的根證書 簽發的,所以,當返回這個證書的時候,你的客戶端的信任鏈是可以完成的,會被系統信任),然后Client在這個偽造的證書(對于青花瓷和Client是真實證書(驗證信任鏈和證書信息都通過了),但是和真實的域名對應的證書來看,是偽造證書)的基礎上,和青花瓷通信,然后青花瓷再和Server通信,成了一個中間人的角色,這樣,整個過程的數據傳輸,都被青花瓷給監聽到了
在此,中間人攻擊的過程 就完成了
至于客戶端怎么防止被抓包呢? 我一共想到了2個方案
1.當進行網絡請求的時候,客戶端判斷當前是否設置了代理,如果設置了代理,不允許進行訪問(不知道微信瀏覽器 里面 是不是這樣實現的,微信里面 設置了代理看公眾號等信息就都不允許看了,無法訪問)
附帶判斷是否設置代理的代碼:
+ (BOOL)getProxyStatus {
NSDictionary *proxySettings = NSMakeCollectable([(NSDictionary *)CFNetworkCopySystemProxySettings() autorelease]);
NSArray *proxies = NSMakeCollectable([(NSArray *)CFNetworkCopyProxiesForURL((CFURLRef)[NSURL URLWithString:@"http://www.google.com"], (CFDictionaryRef)proxySettings) autorelease]);
NSDictionary *settings = [proxies objectAtIndex:0];
NSLog(@"host=%@", [settings objectForKey:(NSString *)kCFProxyHostNameKey]);
NSLog(@"port=%@", [settings objectForKey:(NSString *)kCFProxyPortNumberKey]);
NSLog(@"type=%@", [settings objectForKey:(NSString *)kCFProxyTypeKey]);
if ([[settings objectForKey:(NSString *)kCFProxyTypeKey] isEqualToString:@"kCFProxyTypeNone"])
{
//沒有設置代理
return NO;
}
else
{
//設置代理了
return YES;
}
}
2.客戶端本地做證書校驗,并且設置不僅僅校驗公鑰,設置完整的正式校驗模式
+(AFSecurityPolicy*)customSecurityPolicy
{
// /先導入證書
NSString *cerPath = [[NSBundle mainBundle] pathForResource:@"test" ofType:@"cer"];//證書的路徑
NSData *certData = [NSData dataWithContentsOfFile:cerPath];
// AFSSLPinningModeCertificate 使用證書驗證模式 (AFSSLPinningModeCertificate是證書所有字段都一樣才通過認證,AFSSLPinningModePublicKey只認證公鑰那一段,AFSSLPinningModeCertificate更安全。但是單向認證不能防止“中間人攻擊”)
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;
NSSet<NSData*> * set = [[NSSet alloc]initWithObjects:certData , nil];
securityPolicy.pinnedCertificates = set;
return securityPolicy;
}
這樣的話,證書會校驗請求的時候不僅僅校驗域名,會將證書中的公鑰及其他信息也進行校驗,這樣的話,中間人偽造的證書就無法通過驗證,無法進行抓包
上面是我自己整理和猜測的,具體是否真的是這樣或者這個方案是否真實可行,僅供參考,有錯誤的話,希望各位大牛賜教和指出,在此特別感謝,希望可以共同進步,謝謝