不久之前,安全握手是雙方的業(yè)務(wù)得以實(shí)現(xiàn)的一個(gè)標(biāo)記。畢竟,握手是一次面對(duì)面的機(jī)會(huì),可以對(duì)潛在的合作者進(jìn)行評(píng)價(jià)。安全且可信的握手意味著事務(wù)的雙方都相信它們正在做的事情對(duì)雙方都是有益的。不安全的握手標(biāo)記著只有一方會(huì)對(duì)事務(wù)有著正確的理解。 握手的工作方式與在線事務(wù)相同。 developerWorks 上的前一篇文章“使用 OpenSSL API 進(jìn)行安全編程,第 1 部分:API 概述” 向您介紹了如何使用 OpenSSL 創(chuàng)建基本、簡(jiǎn)單的安全連接。然而,這篇文章只是展示了基本的默認(rèn)設(shè)置;它并沒(méi)有介紹如何對(duì)內(nèi)容進(jìn)行定制。不過(guò),我仍然建議大家閱讀這篇文章,這樣可以使您對(duì)本文的理解更加完整,因?yàn)榍耙黄恼陆榻B了數(shù)字證書(shū)的概念,并且介紹了如何判斷一個(gè)證書(shū)是否成功通過(guò)了 OpenSSL 的內(nèi)部驗(yàn)證。 本文將深入介紹 OpenSSL,向您介紹如何增強(qiáng)握手的安全性,防止所謂的 中間人 (MITM)攻擊。 關(guān)于數(shù)字證書(shū)在本文后面,我們將介紹如何獲取數(shù)字證書(shū)并對(duì)數(shù)字證書(shū)進(jìn)行驗(yàn)證,因此下面我們將快速討論一下什么是數(shù)字證書(shū),以及它是如何工作的。如果您熟悉數(shù)據(jù)加密和 SSL 的知識(shí),就可以跳過(guò)本節(jié)。要了解更多有關(guān)加密和 SSL 問(wèn)題的信息,請(qǐng)參閱在本文后面 參考資料 中所列出的文章和教程。 數(shù)字證書(shū)的最簡(jiǎn)單形式就是 不對(duì)稱加密密鑰(asymmetric cryptography key)。目前關(guān)于數(shù)字證書(shū)的標(biāo)準(zhǔn)中都有一些標(biāo)識(shí)信息,在密鑰中也都包含了這些信息。一個(gè)典型的數(shù)字證書(shū)包含所有者的名字(如果這個(gè)證書(shū)是在一個(gè) Web 服務(wù)器上使用的,那么名字就是完整的域名)以及聯(lián)系信息,還有一個(gè)有效日期范圍,以及一個(gè)安全性簽名,用來(lái)驗(yàn)證這個(gè)證書(shū)沒(méi)有被篡改。 數(shù)字證書(shū)可以使用 OpenSSL 命令行工具或其他用于此目的的工具簡(jiǎn)單地創(chuàng)建。但是任何人創(chuàng)建的數(shù)字證書(shū)都有一個(gè)信任的問(wèn)題。數(shù)字證書(shū)不僅僅是一個(gè)加密密鑰,它還是一個(gè)在線憑證。證書(shū)會(huì)向那些試圖與您進(jìn)行通信的人證明您的身份。為了顯示信任關(guān)系,數(shù)字證書(shū)可以由認(rèn)證權(quán)威(CA)機(jī)構(gòu)進(jìn)行簽名。 認(rèn)證權(quán)威在數(shù)字安全性領(lǐng)域充當(dāng)一個(gè)可信的第三方。由于在在線領(lǐng)域中證明某個(gè)實(shí)體的身份非常困難,認(rèn)證權(quán)威就接管了這個(gè)挑戰(zhàn)。它們?yōu)槟切┵?gòu)買證書(shū)或?qū)ψC書(shū)進(jìn)行簽名的用戶的身份提供證明。因此,要信任一個(gè)證書(shū),用戶只需要信任證書(shū)權(quán)威即可。用戶通過(guò)擁有并使用 CA 的信任證書(shū)來(lái)表明自己對(duì)認(rèn)證權(quán)威的信任。Verisign 和 Thawte 是非常知名的認(rèn)證權(quán)威。 如果一個(gè)證書(shū)的安全性曾經(jīng)受到過(guò)威脅,那么這個(gè)證書(shū)就會(huì)被丟棄 —— 也就是說(shuō),將其聲明為無(wú)效。當(dāng)一個(gè)證書(shū)被聲明為無(wú)效時(shí),CA 不可能將其通知所有擁有該證書(shū)拷貝的人。相反,CA 會(huì)發(fā)布一個(gè) 證書(shū)撤銷列表(Certificate Revocation List)(CRL)。瀏覽器和其他使用數(shù)字證書(shū)的程序都可以驗(yàn)證這個(gè)證書(shū)已經(jīng)被其屬主或 CA 撤銷了。 證書(shū)的撤銷也可以使用 OCSP 協(xié)議進(jìn)行檢查。OCSP 代表 Online Certificate Status Protocol(在線證書(shū)狀態(tài)協(xié)議),它是在 RFC 2560 中定義的。OpenSSL 既有 OCSP 的功能,又有 CRL 的功能,但是對(duì)這些功能的介紹已經(jīng)超出了本文的范圍。目前數(shù)字證書(shū)所采用的標(biāo)準(zhǔn)是 X.509,這是在 RFC 3280 中定義的。 開(kāi)展業(yè)務(wù)之前的握手由于本文重點(diǎn)要介紹在握手過(guò)程中服務(wù)器數(shù)字證書(shū)的處理,因此讓我們來(lái)深入介紹一下握手是如何工作的。如果您熟悉 SSL 的過(guò)程,可以跳過(guò)本節(jié)。 在一個(gè)連接上開(kāi)始握手通常是從客戶機(jī)向服務(wù)器說(shuō)“Hello”開(kāi)始的。helllo 消息(在規(guī)范中就是這么說(shuō)的)包含了客戶機(jī)的安全性參數(shù):
服務(wù)器會(huì)使用自己的 hello 消息進(jìn)行響應(yīng),其中包含了服務(wù)器的安全性參數(shù),這些信息與客戶機(jī)所提供的信息的類型相同。此時(shí)服務(wù)器還會(huì)發(fā)送自己的數(shù)字證書(shū)。如果客戶機(jī)還要為這個(gè)連接進(jìn)行認(rèn)證,那么服務(wù)器還會(huì)發(fā)送一個(gè)請(qǐng)求,索取客戶機(jī)的證書(shū)。 當(dāng)客戶機(jī)接收到服務(wù)器端的 hello 消息之后,數(shù)字證書(shū)就要進(jìn)行驗(yàn)證了。這包括檢查證書(shū)的各種參數(shù),從而確保該證書(shū)從未受到過(guò)侵害,同時(shí)是在它的有效期內(nèi)使用的。 此處還要執(zhí)行的另外一個(gè)步驟是根據(jù)連接所使用的主機(jī)名對(duì)證書(shū)的名字進(jìn)行檢查。雖然這不是 SSL 標(biāo)準(zhǔn)的一部分,但是這個(gè)步驟卻是高度建議的,它可以防止中間人攻擊。這個(gè)步驟會(huì)驗(yàn)證證書(shū)就是來(lái)自您認(rèn)為它所來(lái)自的那個(gè)實(shí)體。如果這兩個(gè)實(shí)體在此處不能匹配,那么就只能懷疑這個(gè)證書(shū)是無(wú)效的。 在客戶機(jī)和服務(wù)器之間共享的隨機(jī)數(shù)據(jù)用來(lái)創(chuàng)建 premaster secret,這是一個(gè)只有服務(wù)器和客戶機(jī)才會(huì)知道的共享秘密值,并且只用于這個(gè)會(huì)話。這個(gè)秘密值會(huì)對(duì)服務(wù)器的數(shù)字證書(shū)進(jìn)行加密,并發(fā)送給服務(wù)器用于驗(yàn)證客戶機(jī)的身份。 如果服務(wù)器請(qǐng)求客戶機(jī)認(rèn)證,那么客戶機(jī)就會(huì)對(duì)這個(gè)在握手過(guò)程中隨機(jī)生成的數(shù)據(jù)(只有服務(wù)器和客戶機(jī)知道它)創(chuàng)建一個(gè)單向的 hash 值??蛻魴C(jī)使用自己的私鑰對(duì)這個(gè) hash 值進(jìn)行簽名,并將簽名后的數(shù)據(jù)和數(shù)字證書(shū)發(fā)送給服務(wù)器。服務(wù)器使用這些信息對(duì)客戶機(jī)進(jìn)行認(rèn)證。 如果認(rèn)證成功,那么服務(wù)器和客戶機(jī)就會(huì)通過(guò)一個(gè)算法使用這個(gè)共享的隨機(jī)數(shù)據(jù)來(lái)創(chuàng)建 master secret。從 master secret 中,客戶機(jī)和服務(wù)器可以創(chuàng)建 session keys(會(huì)話密鑰),這是選擇用來(lái)對(duì)會(huì)話數(shù)據(jù)進(jìn)行加密所使用的對(duì)稱密碼中的對(duì)稱密鑰。 客戶機(jī)通過(guò)向服務(wù)器發(fā)送一個(gè)表明自己已經(jīng)完成握手的消息,以及一組加密的單向 hash 值讓服務(wù)器進(jìn)行驗(yàn)證,從而結(jié)束握手的過(guò)程。服務(wù)器也會(huì)向客戶機(jī)發(fā)送一個(gè)類似的消息。客戶機(jī)和服務(wù)器在結(jié)束握手并開(kāi)始通信之前,都要對(duì)這些數(shù)據(jù)進(jìn)行驗(yàn)證。 中間人雖然這是孩子們玩的一個(gè)游戲,但卻也是在公鑰基礎(chǔ)設(shè)施(PKI)上可能發(fā)生的一種很嚴(yán)重的攻擊。當(dāng)我們?cè)谟懻撚嘘P(guān)數(shù)字證書(shū)的問(wèn)題時(shí),就必須考慮中間人攻擊的問(wèn)題,因?yàn)椴还?SSL 連接之后的安全參數(shù)如何,中間人攻擊都可以讓這些防范措施形同虛設(shè)。 假設(shè) Casey 和 Samantha 希望使用 SSL 進(jìn)行通信。Isabel 是一個(gè)第三方,她截獲了這個(gè)連接請(qǐng)求,并在他們兩個(gè)之前充當(dāng)代理。當(dāng)她注意到正在建立一個(gè) SSL 連接時(shí),就向 Samantha 偽裝成 Casey,向 Casey 偽裝成 Samantha。由于她位于中間,可以截獲會(huì)話雙方的內(nèi)容。如果這個(gè)會(huì)話中包含賬號(hào)和個(gè)人信息,那么她就可以使用這些信息竊取他人的身份了。 在這種情況中,信任鏈和證書(shū)中的通用名可以防止中間人攻擊的發(fā)生。在握手過(guò)程中,會(huì)對(duì)證書(shū)進(jìn)行交換。在分析證書(shū)的有效性時(shí),同時(shí)還會(huì)檢查可信簽名。如果服務(wù)器證書(shū)中的通用名是與證書(shū)的其余部分一起驗(yàn)證的,那么這種攻擊就不攻自破了,對(duì)么?其實(shí)不完全對(duì)。 讓我們假設(shè) Isabel 有一個(gè)證書(shū),其中有 Samantha 的名字;并且這個(gè)證書(shū)由 Casey 信任模型中的一個(gè) CA 進(jìn)行了簽名。此時(shí)只通過(guò)檢查通用名并不能避免 MITM 攻擊的發(fā)生。證書(shū)及其信任關(guān)系都是有效的,名字也檢查出來(lái)了。我們現(xiàn)在有了一個(gè)大麻煩。 然而,如果考慮一下認(rèn)證權(quán)威,這個(gè)問(wèn)題就并不重要了。大部分認(rèn)證權(quán)威都會(huì)在發(fā)行帶有個(gè)人名的數(shù)字證書(shū)之前就試圖驗(yàn)證他的身份就是本人。由于這個(gè)原因,Isabel 就很難從一個(gè)知名的認(rèn)證權(quán)威那里獲得一個(gè)帶有 Samantha 名字的數(shù)字證書(shū)。 如果在 CA 中工作,則可以消弱這種安全設(shè)施的作用。例如,如果 CA 和 Isabel 都在一個(gè)公司工作(換而言之,是一個(gè)“內(nèi)部工作”)。那么用來(lái)簽名的密鑰就有可能會(huì)被 CA 內(nèi)部工作的人員竊取,他們隨后就可以使用任何人的名字來(lái)創(chuàng)建任意的證書(shū)。盡管在創(chuàng)建簽名時(shí)要使用證書(shū)的私有部分,但是采用一些工程的方法或類似的技術(shù)也可以竊取密碼。 MITM 攻擊在使用 代理服務(wù)器 的情況中尤其重要。因?yàn)榘踩B接必須由代理服務(wù)器提供一個(gè)隧道才能到達(dá)目的地,因此惡意的代理服務(wù)器就可以很容易地竊取任何會(huì)話。惡意的代理可以在并不存在隧道時(shí)卻偽裝成仿佛隧道真正存在一樣。在使用 Internet 上的“匿名代理”時(shí),記得這一點(diǎn)尤其重要:您正在通過(guò)它們的系統(tǒng)來(lái)發(fā)送用戶名和密碼,怎樣才能相信它們呢? 然而我們要認(rèn)識(shí)到,這種攻擊并不僅僅存在于計(jì)算機(jī)和數(shù)字安全領(lǐng)域。有一個(gè)女人曾經(jīng)使用類似于 MITM 攻擊的技術(shù)搶劫了很多家的很多錢(qián)(請(qǐng)參閱 參考資料)。 OpenSSL 和數(shù)字證書(shū)OpenSSL 有一個(gè)專門(mén)用于數(shù)字證書(shū)的庫(kù)。假設(shè)您現(xiàn)在已經(jīng)有了 OpenSSL 的源代碼,那么這個(gè)庫(kù)的源代碼就位于 crypto/x509 和 crypto/x509v3 目錄中。源代碼為數(shù)字證書(shū)的處理定義了幾個(gè)結(jié)構(gòu)。表 1 中列出了這些結(jié)構(gòu)。 表 1. 與 X.509 證書(shū)有關(guān)的 OpenSSL 結(jié)構(gòu)
這些只是其中涉及的幾個(gè)結(jié)構(gòu)。在 OpenSSL 中使用的大部分 X.509 結(jié)構(gòu)您自己在應(yīng)用程序中幾乎都不會(huì)用到。在上面列出的這些結(jié)構(gòu)中,本文中只使用了兩個(gè):X509 和 X509_NAME。 在這些結(jié)構(gòu)之上,有一些用來(lái)處理數(shù)字證書(shū)的函數(shù)。這些函數(shù)得名于它們所適用的結(jié)構(gòu)。例如,一個(gè)名字以 X509_NAME 開(kāi)始的函數(shù),通常會(huì)應(yīng)用于一個(gè) X509_NAME 結(jié)構(gòu)。后面我們會(huì)根據(jù)需要介紹一些函數(shù)。 提供自己的信任證書(shū)
在數(shù)字證書(shū)進(jìn)行信任驗(yàn)證之前,必須為在為安全連接設(shè)置時(shí)創(chuàng)建的 OpenSSL 盡管當(dāng)信任證書(shū)在一個(gè)目錄中有多個(gè)單獨(dú)的文件時(shí)更容易添加或更新,但是您不太可能會(huì)如此頻繁地更新信任證書(shū),因此不必?fù)?dān)心這個(gè)問(wèn)題。 驗(yàn)證證書(shū)
在通信繼續(xù)連接或接收證書(shū)之前,請(qǐng)使用 通常,如果返回代碼不是 X509_V_OK,那么這個(gè)證書(shū)就有問(wèn)題,或者這個(gè)證書(shū)存在安全性隱患。時(shí)刻要記住一件事情:OpenSSL 在對(duì)證書(shū)進(jìn)行驗(yàn)證時(shí),有一些安全性檢查并沒(méi)有執(zhí)行,包括證書(shū)的失效檢查和對(duì)證書(shū)中通用名的有效性驗(yàn)證。
不管驗(yàn)證結(jié)果如何,是否繼續(xù)使用一些可能不安全的參數(shù)也完全取決于開(kāi)發(fā)者。由于證書(shū)可能是不安全的,因此會(huì)返回錯(cuò)誤代碼。 檢索證書(shū)
如果您希望向用戶顯示證書(shū)的內(nèi)容,或者要根據(jù)主機(jī)名或證書(shū)權(quán)威對(duì)證書(shū)進(jìn)行驗(yàn)證,那么就需要檢索證書(shū)的內(nèi)容。要在驗(yàn)證測(cè)試結(jié)果之后再檢索證書(shū),請(qǐng)調(diào)用 清單 1. 檢索證書(shū)X509 * peerCertificate; if(SSL_get_verify_result(ssl) == X509_V_OK) peerCertificate = SSL_get_peer_certificate(ssl); else { /* Handle verification error here */ } 驗(yàn)證證書(shū)在握手時(shí)所提供的服務(wù)器的證書(shū)應(yīng)該有一個(gè)名字與該服務(wù)器的主機(jī)名匹配。如果沒(méi)有,那么這個(gè)證書(shū)就應(yīng)該標(biāo)記為值得懷疑的。內(nèi)部驗(yàn)證過(guò)程已經(jīng)對(duì)證書(shū)進(jìn)行信任和有效期的驗(yàn)證;如果這個(gè)證書(shū)已經(jīng)超期,或者包含一個(gè)不可信的簽名,那么這個(gè)證書(shū)就會(huì)被標(biāo)記為無(wú)效的。由于這不是 SSL 標(biāo)準(zhǔn)的一部分,因此 OpenSSL 并不需要根據(jù)主機(jī)名對(duì)該證書(shū)的名字進(jìn)行檢查。 證書(shū)的“名字”實(shí)際上是證書(shū)中的 Common Name 字段。這個(gè)字段應(yīng)該從證書(shū)中進(jìn)行檢索,并根據(jù)主機(jī)名進(jìn)行驗(yàn)證。如果二者不能匹配,就只有懷疑這個(gè)證書(shū)無(wú)效了。有些公司(例如 Yahoo)就在不同的主機(jī)上使用相同的證書(shū),即使證書(shū)中的 Common Name 只是用于一個(gè)主機(jī)的。為了確保這個(gè)證書(shū)是來(lái)自于相同的公司,可以進(jìn)行更深入的檢查,但是這完全取決于項(xiàng)目的安全性需要。 從證書(shū)中檢索通用名需要兩個(gè)步驟:
使用 清單 2. 檢索并驗(yàn)證 Common Namechar commonName [512]; X509_NAME * name = X509_get_subject_name(peerCertificate); X509_NAME_get_text_by_NID(name, NID_commonName, commonName, 512); /* More in-depth checks of the common name can be used if necessary */ if(stricmp(commonName, hostname) != 0) { /* Handle a suspect certificate here */ } 使用標(biāo)準(zhǔn)的 C 字符串函數(shù)或您習(xí)慣使用的字符串庫(kù)對(duì)通用名和主機(jī)名進(jìn)行比較。對(duì)不匹配的處理,完全取決于項(xiàng)目的需要或用戶的決策。如果要更深入地進(jìn)行檢查,我建議使用一個(gè)單獨(dú)的字符串庫(kù)來(lái)降低復(fù)雜性。 獲得信任在本文中,我們已經(jīng)介紹了如何增強(qiáng)握手的安全性,從而防止中間人攻擊(攻擊一方偽裝成另外一個(gè)可信源),我們還介紹了數(shù)字證書(shū)的概念,以及 OpenSSL API 如何處理數(shù)字證書(shū)。 記住,在 SSL 會(huì)話過(guò)程中增強(qiáng)會(huì)話的安全性非常重要,這是因?yàn)樵撨B接中的所有安全性都是在握手過(guò)程中建立的。遵循本文中概要介紹的每個(gè)步驟到底有多重要取決于項(xiàng)目的要求以及開(kāi)發(fā)者的決定。 參考資料
|
|