淺談Charles抓包原理

類似Charles這樣的抓包工具,對(duì)于高效程序員是必不可少的;
本文不會(huì)介紹Charles的安裝及使用,主要是淺顯的探討其抓包原理;Charles的安裝及使用相關(guān)內(nèi)容可以參考以下文章:

iOS開發(fā)輔助工具-抓包工具-Charles青花瓷
Charles 4.5.6 Mac pojie版

大致原理

Charles作為一個(gè)中間人代理,在客戶端給服務(wù)器端發(fā)消息的時(shí)候,會(huì)截取客戶端發(fā)送給服務(wù)器的請(qǐng)求,然后偽裝成客戶端與服務(wù)器進(jìn)行通信;服務(wù)器返回?cái)?shù)據(jù)時(shí)將截取的數(shù)據(jù)發(fā)送給客戶端,偽裝成服務(wù)器與客戶端進(jìn)行通信。

這個(gè)過程其實(shí)很簡(jiǎn)單,但不同于HTTP,更安全的HTTPS能有效防止中間人攻擊;Charles是如何截取HTTPS鏈接的呢?

HTTPS的安全性

相比HTTP,HTTPS之所以更安全的是因?yàn)槠湓贖TTP傳輸層之上加了一個(gè)安全層(SSL或TLS協(xié)議);HTTPS的安全性主要體現(xiàn)在下面3個(gè)方面:

  • 數(shù)據(jù)的保密性(防竊聽)
  • 數(shù)據(jù)的完整性(防篡改)
  • 通信雙方身份的真實(shí)性(防冒充)
數(shù)據(jù)的保密性

要實(shí)現(xiàn)數(shù)據(jù)的保密,就需要使用加密算法對(duì)數(shù)據(jù)進(jìn)行加密;加密算法大致分為兩類:對(duì)稱加密,非對(duì)稱加密;

  • 對(duì)稱加密:加密和解密使用相同密鑰的加密算法。對(duì)稱加密速度快,通常在需要加密大量數(shù)據(jù)時(shí)使用;因?yàn)榧用芎徒饷芏际褂猛粋€(gè)密鑰,把密鑰傳遞到解密者的過程中也會(huì)有風(fēng)險(xiǎn),因此對(duì)稱加密不是很安全的;常用的對(duì)稱加密算法有:DES、3DES、RC2、RC4、RC5、IDEA等

  • 非對(duì)稱加密,加密和解密使用不同密鑰的加密算法;它使用了一對(duì)密鑰,公鑰私鑰;使用公鑰加密的數(shù)據(jù),利用私鑰解密;使用私鑰加密的數(shù)據(jù),利用公鑰解密;非對(duì)稱加密通常使用RSA算法(RSA原理探究),RSA的公鑰和私鑰其實(shí)就是一組數(shù)字,數(shù)字長(zhǎng)度越長(zhǎng)加密強(qiáng)度越大;數(shù)字一般是高于768位(二進(jìn)制),不能被輕易破解;RSA算法加密解密其實(shí)就是對(duì)這個(gè)比較大的數(shù)進(jìn)行運(yùn)算;因此RSA非對(duì)稱加密要比對(duì)稱加密慢很多,為了保證效率,RSA非對(duì)稱加密只用于小數(shù)據(jù);

基于對(duì)稱加密和非對(duì)稱加密的優(yōu)缺點(diǎn),HTTPS的加密方案就是:

連接建立過程(TLS握手):
  1. 客戶端向服務(wù)器請(qǐng)求(發(fā)送TLS版本號(hào)、支持的加密算法、隨機(jī)數(shù)Client Random);
  2. 服務(wù)器返回非對(duì)稱加密的公鑰(證書)、商定的加密算法、隨機(jī)數(shù)Server Random給客戶端;
  3. 客戶端驗(yàn)證服務(wù)器返回的證書;
  4. 證書驗(yàn)證通過,客戶端就會(huì)生成一個(gè)新的隨機(jī)數(shù)pre-master,用服務(wù)器的公鑰加密該隨機(jī)數(shù)并發(fā)送給服務(wù)器;服務(wù)器收到后,用私鑰解密,得到客戶端發(fā)來的隨機(jī)數(shù)pre-master。
    至此,客戶端和服務(wù)端雙方都共享了三個(gè)隨機(jī)數(shù),分別是Client Random/Server Random/pre-master??蛻舳司透鶕?jù)服務(wù)器返回的證書及3個(gè)隨機(jī)數(shù)生成一個(gè)會(huì)話密鑰(對(duì)稱加密);
  5. 客戶端用服務(wù)器返回的公鑰(證書)對(duì)會(huì)話密鑰進(jìn)行非對(duì)稱加密后傳輸給服務(wù)器;
  6. 服務(wù)器通過私鑰解密得到會(huì)話密鑰;
  7. 客戶端和服務(wù)器互相傳輸加密的握手消息來驗(yàn)證安全通道是否已完成;
通信過程
  1. 客戶端使用會(huì)話密鑰對(duì)傳輸?shù)臄?shù)據(jù)進(jìn)行對(duì)稱加密傳輸給服務(wù)器;
  2. 服務(wù)器使用會(huì)話密鑰對(duì)傳輸?shù)臄?shù)據(jù)進(jìn)行解密;
  3. 服務(wù)器使用會(huì)話密鑰對(duì)響應(yīng)的數(shù)據(jù)進(jìn)行對(duì)稱加密傳輸給客戶端;
  4. 客戶端使用會(huì)話密鑰對(duì)傳輸?shù)臄?shù)據(jù)進(jìn)行解密;

總的來說就是:連接建立過程使用非對(duì)稱加密,后續(xù)通信過程使用對(duì)稱加密;

數(shù)據(jù)的完整性

數(shù)據(jù)的加密,有效保證了數(shù)據(jù)不被竊聽(很難得到原始的數(shù)據(jù)),但傳輸?shù)臄?shù)據(jù)在傳輸過程中有可能被篡改或替換;比如:
傳輸?shù)脑紨?shù)據(jù)是123456,經(jīng)過加密后數(shù)據(jù)是abcdef;客戶端將abcdef這個(gè)數(shù)據(jù)傳輸給服務(wù)器,傳輸過程中中間人能拿到abcdef這個(gè)數(shù)據(jù),但因?yàn)闆]有密鑰很難解密出原始數(shù)據(jù)123456;但是,中間人還是能對(duì)得到的abcdef這個(gè)加密數(shù)據(jù)進(jìn)行處理,比如將這個(gè)數(shù)據(jù)改為xxxxx;這樣服務(wù)器得到的數(shù)據(jù)就是被篡改后的xxxxx;同樣,服務(wù)器返回?cái)?shù)據(jù)給客戶端時(shí)也會(huì)被篡改;這樣其實(shí)也是不安全的;
解決方案是進(jìn)行數(shù)字簽名:使用Hash算法將任意長(zhǎng)度的字符串轉(zhuǎn)化為固定長(zhǎng)度的字符串,該過程不可逆,可用來作數(shù)據(jù)完整性校驗(yàn);
具體可參考淺談Hash
數(shù)字簽名的簡(jiǎn)要過程(服務(wù)器-->客戶端為例,客戶端-->服務(wù)器類似):

  1. 服務(wù)器使用Hash算法對(duì)數(shù)據(jù)提取定長(zhǎng)摘要
  2. 服務(wù)器使用私鑰對(duì)摘要進(jìn)行加密,作為數(shù)字簽名
  3. 服務(wù)器將數(shù)字簽名連同加密的數(shù)據(jù)一同傳輸給客戶端
  4. 客戶端使用公鑰對(duì)數(shù)字簽名進(jìn)行解密,得到摘要A
  5. 客戶端對(duì)解密后的傳輸數(shù)據(jù)也使用Hash算法得到定長(zhǎng)摘要B
  6. 對(duì)比摘要A和摘要B,如果不一致則數(shù)據(jù)已被篡改
通信雙方身份的真實(shí)性

以上加密過程,最核心的就是非對(duì)稱加密的公鑰和私鑰;如果這個(gè)密鑰都是攻擊者提供的,那傳輸?shù)臄?shù)據(jù)在攻擊者那里也是相當(dāng)于裸露的;如何確保密鑰是不被冒充的呢?HTTPS使用了數(shù)字證書(簽名),數(shù)字證書就是身份認(rèn)證機(jī)構(gòu)CA(Certificate Authority)加在數(shù)字身份證上的一個(gè)簽名,證書的合法性可以向CA驗(yàn)證;證書的制作方法是公開的,任何人都可以自己制作證書,但只有權(quán)威的證書頒發(fā)機(jī)構(gòu)的證書能通過CA認(rèn)證;
數(shù)字證書主要包含以下信息:

  • 證書頒發(fā)機(jī)構(gòu)
  • 證書頒發(fā)機(jī)構(gòu)簽名
  • 證書版本、有效期
  • 證書綁定的服務(wù)器域名
  • 簽名使用的加密算法(非對(duì)稱算法,如RSA)
  • 公鑰

數(shù)字證書的作用,是用來認(rèn)證公鑰持有者的身份,以防止第三方進(jìn)行冒充;簡(jiǎn)單說,證書就是用來告訴客戶端,服務(wù)端是否合法。
為了讓服務(wù)端的公鑰被信任,服務(wù)端的證書都由CA簽名,CA就是網(wǎng)絡(luò)世界里的公安局,具有極高的可信度,所以由它來給各個(gè)公鑰簽名,信任的一方簽發(fā)的證書,那必然證書也是被信任的。

數(shù)字證書簽發(fā)和驗(yàn)證流程

  • CA簽發(fā)證書的過程

首先CA會(huì)把持有者的公鑰、頒發(fā)者、用途、有效時(shí)間等信息打包;然后對(duì)這些信息進(jìn)行Hash計(jì)算。然后CA會(huì)使用自己的私鑰將該Hash值加密,生成Certificate Signature,即CA對(duì)證書做了簽名。最后將Certificate Signature添加在文件證書上,形成了數(shù)字證書;

  • 客戶端校驗(yàn)服務(wù)端的數(shù)字證書的過程:

客戶端會(huì)使用同樣的Hash算法獲取該證書的Hash值A(chǔ);
通常瀏覽器和操作系統(tǒng)中集成了CA的公鑰信息,瀏覽器收到證書后可以使用CA的公鑰解密Certificate Signature內(nèi)容,得到一個(gè)Hash值B;
最后比較A和B,如果值相同,則為可信賴的證書,否則認(rèn)為證書不可信。

客戶端和服務(wù)器連接過程中,收到服務(wù)器返回的證書后,會(huì)先向CA驗(yàn)證證書的合法性(根據(jù)證書的簽名、綁定的域名等信息),如果校驗(yàn)不通過中止連接,并提示證書不安全。

HTTPS抓包原理

<unknown>的原因

首先我們看下默認(rèn)情況下,使用Charles抓包HTTPS的情況:

正常情況下,得到的結(jié)果都是<unknown>;這是因?yàn)槲覀兦懊嬷v的HTTPS的安全性的作用;
點(diǎn)擊<unknown>查看具體信息:

tls

(ps: 截圖中也可以看到 TLS版本號(hào),協(xié)商的加密算法等信息;這個(gè)和上述流程對(duì)應(yīng)上了)

可以看到,報(bào)錯(cuò)原因是SSL握手失敗,也就是在TLS/SSL連接建立時(shí)就已經(jīng)失敗,還沒有到數(shù)據(jù)通信這步來;
進(jìn)一步查看TLS Alert Code得到更詳細(xì)的信息:

handshake_failure (40) - Unable to negotiate an acceptable set of security parameters, this probably means there are no cipher suites in common

大概意思是,密鑰參數(shù)沒有協(xié)商一致;
這是因?yàn)镃harles為了能竊聽、篡改HTTPS通信數(shù)據(jù),在客戶端與服務(wù)端連接建立過程中(參考上面HTTPS連接建立過程的第2步),當(dāng)服務(wù)器返回非對(duì)稱加密的公鑰(證書)、商定的加密算法、隨機(jī)數(shù)Server Random給客戶端時(shí),Charles攔截到服務(wù)器返回給客戶端的公鑰(證書)替換成自己的公鑰(證書)再發(fā)送給客戶端; 準(zhǔn)備以此來冒充通信的雙方;
但是前面我們也分析過了,HTTPS會(huì)通過數(shù)字證書驗(yàn)證通信雙方身份的真實(shí)性;Charles的證書并不能通過CA驗(yàn)證,證書未驗(yàn)證通過那客戶端后續(xù)流程就不會(huì)進(jìn)行:包括生成一個(gè)新的隨機(jī)數(shù)pre-master、生成非對(duì)稱密鑰等流程;最終導(dǎo)致連接失??;

由于是TLS握手都沒成功,不止是Charles失敗,原先的客戶端和服務(wù)器也不能建立正常連接,客戶端網(wǎng)絡(luò)請(qǐng)求也都是失??;

Charles的策略

Charles如何解決證書驗(yàn)證的問題呢?
根據(jù)官方教程,需要我們使用者在手機(jī)上安裝Charles根證書并設(shè)置為信任:

配置好后,就能和HTTP一樣抓包使用了;
為什么手機(jī)安裝了Charles根證書后就能正常抓包呢?結(jié)合數(shù)字證書驗(yàn)證的原理,CA起到了決定性作用;一般常用的CA證書(公鑰)是事先內(nèi)嵌在手機(jī)系統(tǒng)里的,如果不是內(nèi)嵌的CA私鑰簽名的都驗(yàn)證不通過;Charles根證書就類似Charles CA,用戶手動(dòng)安裝到系統(tǒng)并設(shè)置信任,相當(dāng)于系統(tǒng)里多了一個(gè)CA;后續(xù)Charles的weizao的經(jīng)過Charles根證書(私鑰)簽名的公鑰就能驗(yàn)證通過,握手成功,通信過程也能竊聽、篡改;

Charles抓包的完整流程
  • 當(dāng)客戶端和服務(wù)器建立連接時(shí),Charles會(huì)攔截到服務(wù)器返回的證書(服務(wù)器公鑰)
  • 然后動(dòng)態(tài)生成一張weizao證書(Charles公鑰/假公鑰)發(fā)送給客戶端
  • 客戶端收到Charles證書后,進(jìn)行驗(yàn)證;因?yàn)橹拔覀兪謾C(jī)設(shè)置了信任,所以驗(yàn)證通過;(只要手機(jī)不信任這種證書,HTTPS還是能確保安全的)
  • 客戶端生成會(huì)話密鑰,使用Charles證書對(duì)會(huì)話密鑰進(jìn)行加密再傳輸給服務(wù)器
  • Charles攔截到客戶端傳輸?shù)臄?shù)據(jù),使用自己的Charles私鑰進(jìn)行解密得到會(huì)話密鑰
  • 連接成功后,客戶端和服務(wù)器通信,客戶端對(duì)傳輸?shù)臄?shù)據(jù)使用會(huì)話密鑰加密并使用公鑰對(duì)數(shù)據(jù)摘要進(jìn)行數(shù)字簽名,一同傳輸給服務(wù)器;
  • Charles攔截到通信的數(shù)據(jù),使用之前獲得的會(huì)話密鑰解密就能得到原始數(shù)據(jù);
  • Charles同樣也能篡改通信的數(shù)據(jù):將篡改后的數(shù)據(jù)重新加密并重新生成摘要并使用之前獲得的公鑰進(jìn)行數(shù)字簽名,替換原本的簽名,再傳輸給服務(wù)器;
  • 服務(wù)器收取到數(shù)據(jù),按正常流程解密驗(yàn)證;
  • 服務(wù)器返回響應(yīng)數(shù)據(jù)時(shí),Charles也是類似攔截過程

整個(gè)流程大致如下圖:

防抓包

從上文可以知道,抓包主要的原理就是中間人替換了原本的證書;防抓包就可以通過針對(duì)證書的校驗(yàn)來實(shí)現(xiàn);
AFN有封裝類似校驗(yàn)證書的功能,接下來主要介紹AFN+SSL Pinning的方式對(duì)證書進(jìn)行校驗(yàn);

  • 獲取服務(wù)器的HTTPS證書,并把證書加到項(xiàng)目Bundle中;
  • AFN代碼設(shè)置Policy
AFSecurityPolicy *securityPolicy = [AFSecurityPolicy policyWithPinningMode: AFSSLPinningModeCertificate];
securityPolicy.allowInvalidCertificates = YES;
securityPolicy.validatesDomainName = NO;
[AFHTTPSessionManager manager].securityPolicy = securityPolicy;
AFSecurityPolicy *securityPolicy = [AFSecurityPolicy policyWithPinningMode: AFSSLPinningModeCertificate];

這句代碼中,會(huì)去Bundle中獲取所有的cer證書文件:

代碼

AFSSLPinningMode有3種模式:

// 驗(yàn)證證書的模式
typedef NS_ENUM(NSUInteger, AFSSLPinningMode) {
    AFSSLPinningModeNone, // 不做驗(yàn)證:證書是信任機(jī)構(gòu)簽發(fā)的就會(huì)通過,若是自己服務(wù)器生成的證書,不會(huì)通過
    AFSSLPinningModeCertificate, // 證書驗(yàn)證:驗(yàn)證證書的域名/有效期等信息,對(duì)比服務(wù)端返回的證書和客戶端返回的是否一致
    AFSSLPinningModePublicKey, // 公鑰驗(yàn)證(只驗(yàn)證證書里的公鑰,不驗(yàn)證證書的有效期等信息)
};

在證書配置好及代碼設(shè)置好后,再使用抓包軟件就無(wú)法查看更改接口了;

AFN的HTTPS校驗(yàn)核心代碼在evaluateServerTrust方法內(nèi)

- (BOOL)evaluateServerTrust:(SecTrustRef)serverTrust
                  forDomain:(NSString *)domain {
    if (domain && self.allowInvalidCertificates && self.validatesDomainName && (self.SSLPinningMode == AFSSLPinningModeNone || [self.pinnedCertificates count] == 0)) {
        NSLog(@"In order to validate a domain name for self signed certificates, you MUST use pinning.");
        return NO;
    }

    NSMutableArray *policies = [NSMutableArray array];
    if (self.validatesDomainName) {
        // 驗(yàn)證域名時(shí),返回用于評(píng)估SSL證書鏈的策略對(duì)象。
        [policies addObject:(__bridge_transfer id)SecPolicyCreateSSL(true, (__bridge CFStringRef)domain)];
    } else {
        // Returns a policy object for the default X.509 policy.
        [policies addObject:(__bridge_transfer id)SecPolicyCreateBasicX509()];
    }

    // 設(shè)置驗(yàn)證策略
    SecTrustSetPolicies(serverTrust, (__bridge CFArrayRef)policies);

    if (self.SSLPinningMode == AFSSLPinningModeNone) {
         // 如果支持自簽名則驗(yàn)證通過 否則驗(yàn)證serverTrust是否有效、有效則驗(yàn)證通過
        // SSLPinningMode=AFSSLPinningModeNone allowInvalidCertificates=YES 表示任何證書都能驗(yàn)證通過
        // AFServerTrustIsValid(serverTrust) 證書是否是系統(tǒng)信任的證書
        return self.allowInvalidCertificates || AFServerTrustIsValid(serverTrust);
    } else if (!AFServerTrustIsValid(serverTrust) && !self.allowInvalidCertificates) {
        return NO;
    }

    switch (self.SSLPinningMode) {
        case AFSSLPinningModeNone:
        default:
            return NO;
        case AFSSLPinningModeCertificate: {
            NSMutableArray *pinnedCertificates = [NSMutableArray array];
            for (NSData *certificateData in self.pinnedCertificates) {
                [pinnedCertificates addObject:(__bridge_transfer id)SecCertificateCreateWithData(NULL, (__bridge CFDataRef)certificateData)];
            }
            // 設(shè)置了參與校驗(yàn)錨點(diǎn)證書之后,假如驗(yàn)證的數(shù)字證書是這個(gè)錨點(diǎn)證書的子節(jié)點(diǎn),即驗(yàn)證的數(shù)字證書是由錨點(diǎn)證書對(duì)應(yīng)CA或子CA簽發(fā)的,或是該證書本身,則信任該證書
            SecTrustSetAnchorCertificates(serverTrust, (__bridge CFArrayRef)pinnedCertificates);

            if (!AFServerTrustIsValid(serverTrust)) {
                return NO;
            }

            // obtain the chain after being validated, which *should* contain the pinned certificate in the last position (if it's the Root CA)
            // 服務(wù)器證書和客戶端的是否相同,如果有一個(gè)相同則通過
            NSArray *serverCertificates = AFCertificateTrustChainForServerTrust(serverTrust);
            
            for (NSData *trustChainCertificate in [serverCertificates reverseObjectEnumerator]) {
                if ([self.pinnedCertificates containsObject:trustChainCertificate]) {
                    return YES;
                }
            }
            
            return NO;
        }
        case AFSSLPinningModePublicKey: {
            // 比較證書當(dāng)中公鑰(PublicKey)部分來進(jìn)行驗(yàn)證,通過SecTrustCopyPublicKey方法獲取本地證書和服務(wù)器證書,然后進(jìn)行比較,如果有一個(gè)相同,則通過
            NSUInteger trustedPublicKeyCount = 0;
            NSArray *publicKeys = AFPublicKeyTrustChainForServerTrust(serverTrust);

            for (id trustChainPublicKey in publicKeys) {
                for (id pinnedPublicKey in self.pinnedPublicKeys) {
                    if (AFSecKeyIsEqualToKey((__bridge SecKeyRef)trustChainPublicKey, (__bridge SecKeyRef)pinnedPublicKey)) {
                        trustedPublicKeyCount += 1;
                    }
                }
            }
            return trustedPublicKeyCount > 0;
        }
    }
    
    return NO;
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 背景 作為移動(dòng)平臺(tái)的RD,項(xiàng)目開發(fā)過程中一項(xiàng)比較重要的甩鍋技能——抓包應(yīng)該大家都比較熟悉了,畢竟有些bug可能是由...
    silentleaf閱讀 47,505評(píng)論 12 61
  • 什么是HTTPS HTTPS(全稱: Hyper Text Transfer Protocol over Secu...
    小生不怕閱讀 1,491評(píng)論 0 1
  • 客戶端訪問https網(wǎng)站 一般有兩種方式實(shí)現(xiàn),一是信任所有的證書,也就是跳過證書合法性校驗(yàn)這一步驟,對(duì)于這種做法肯...
    mike_fei閱讀 14,925評(píng)論 3 25
  • 在做開發(fā)時(shí),為了調(diào)試與服務(wù)器端的網(wǎng)絡(luò)通訊協(xié)議,常常需要截取網(wǎng)絡(luò)數(shù)據(jù)包來分析。不管是移動(dòng)開發(fā)者還是測(cè)試者,抓包工具對(duì)...
    小道蕭兮閱讀 5,437評(píng)論 4 7
  • 客戶端向服務(wù)器發(fā)起HTTPS請(qǐng)求 Charles攔截客戶端的請(qǐng)求,偽裝成客戶端向服務(wù)器進(jìn)行請(qǐng)求 服務(wù)器向“客戶端”...
    IT卡農(nóng)閱讀 2,663評(píng)論 0 2

友情鏈接更多精彩內(nèi)容