何 旭,鮮乾坤,雷建平
(1.達州職業(yè)技術學院,四川 達州 635001;2.四川理工學院計算機學院,四川 自貢 643000;3.61902部隊,四川 宜賓 644000)
Web瀏覽器的SSL已經(jīng)成為網(wǎng)絡安全的事實標準,SSL主要目的是針對中間件攻擊提供端到端的安全保障,即使網(wǎng)絡受到像DNS入侵或者路由器被控制等這樣的威脅,SSL也能夠保證客戶機與服務器之間通信的機密性、真實性、完整性[1-2]。
認證服務器是SSL連接建立的關鍵部分,其公鑰證書身份驗證發(fā)生于SSL握手階段。同時,為了保證SSL連接是安全的,客戶端必須確認證書是有效證書,即既沒有過期也沒有被撤銷,用以檢查證書名稱、客戶端連接的匹配性[3-4]。
Web瀏覽器對SSL功能的實現(xiàn)可以通過加入補丁保證安全性,這樣許多與SSL相關的瀏覽器漏洞就可以得到修復[5-6]。然而,SSL也廣泛用于非瀏覽器軟件,如基于云計算虛擬基礎設施的遠程管理以及發(fā)送本地數(shù)據(jù)到云存儲,以及Android、iOS等移動應用程序的驗證服務。這些程序通常不實現(xiàn)SSL本身,主要依靠OpenSSL、JSSE與CryptoAPI等SSL庫以及更高層次數(shù)據(jù)傳輸庫作為SSL封閉,在基于Web服務軟件用額外抽象層引入Web服務中間件。
入侵者可能控制路由器、交換機、無線AP或DNS,也可以控制一個或多個擁有SSL證書的服務器。當SSL客戶機試圖連接到一臺合法服務器時,入侵者可以通過DNS挾持誘導服務器,并欺騙用戶連接到一臺入侵者控制的服務器。
SSL握手協(xié)議是一個更高層的客戶端SSL協(xié)議,該協(xié)議是用來協(xié)商會話的安全屬性,握手消息提供給SSL記錄層,并封裝在一個或多個SSLPlaintext數(shù)據(jù)結(jié)構內(nèi),用以處理與傳輸當前活動會話狀態(tài)。SSL連接開始于客戶端和服務器之間的握手,這樣SSL客戶端的“確認服務器認證”容易作為攻擊目標。SSL握手協(xié)議由RFC6101[7]給出了完整描述,其握手過程如圖1所示。
圖1 SSL握手協(xié)議描述
信任鏈驗證。每個X.509證書有一個“issuer”屬性,其中包含CA的名稱,每臺SSL客戶端配置可信根CA證書列表。除自己的證書外,還有該服務器發(fā)送的發(fā)行CA認證。如果簽發(fā)的CA不是根CA,服務器還發(fā)送一個更高級別通向根CA的證書列表??蛻舳嗽噲D建立一個從底部服務器證書開始的鏈,鏈中每個證書必須立即由上級CA簽署,根CA必須是客戶信任的CA之一,客戶驗證證書沒有過期、中間CA認證以及設置有“Basic Constraints”字段。完整的X.509證書認證算法由 RFC5280[3]與 RFC2818[4]給出了描述。
主機驗證。信任鏈建立后,客戶端必須驗證服務器的身份。RFC2818[4]建議使用“SubjectAltNames”作為服務器標識符主要來源以及支持“Common Name”向后兼容,但大多數(shù)軟件是先檢查“Common Name”。構建服務器標識列表后,客戶端嘗試匹配所有符合DNS域名的被請求服務器標識符。如果客戶端在服務器標識符列表中找到一條精確匹配,就通過簡單字符串比較進行驗證,客戶端也可以查找一個匹配規(guī)則復雜的通配符名字標識符的列表[4,8]。
證書撤銷。OpenSSL的一些 SSL庫實現(xiàn)證書撤銷[9],但要求應用程序提供證書撤銷列表(CRL)。雖然JSSE庫要求應用程序檢查自身CRL的有效性,但大多數(shù)應用程序不用檢查,如有些SSL庫(Python的SSL)不公開CRL的檢查方法。
X.509擴展。一些X.509證書擴展包含關鍵安全信息,如key useage(CA允許使用該秘鑰簽署證書)、name constraints(限制子CA能夠簽署的認證)以及RFC2527[10]描述的證書策略。雖然CA可以分配不同的信任級別給子CA,但應用程序必須提供利用這些信息的策略,實際上這些擴展在很大程度上可以被忽略,即或是目前的OpenSSL也沒有正確驗證名字約束,而cURL甚至沒有接口用于指定應用程序證書策略。
根據(jù)需求,應用程序可以插入到不同層次抽象的SSL內(nèi),但在最低層次有多種使用不同屬性、許可、硬件請求來實現(xiàn)通用 SSL,如 OpenSSL、JSSE、CryptoAPI等。因此,為了避免自己解析HTTP,所以使用SSL的HTTP應用程序通常不直接使用,而使用HTTPS在內(nèi)部使用SSL庫,SOAP或基于REST的Web服務應用程序在其上層使用額外中間件 HTTPS或 Web Socket,如圖2所示。
圖2 使用SSL的協(xié)議棧
OpenSSL。OpenSSL只提供信任鏈驗證,而應用程序必須支持自身主機驗證。不同應用層協(xié)議(HTTPS,LDAP)有不同有效主機組成限制和證書列表匹配要求,因此主機驗證必須通過應用程序自身管理或數(shù)據(jù)傳輸層封裝。OpenSSL通過提供返回函數(shù)或修改配置允許應用程序定制信任鏈驗證[11],如配置“驗證深度(verify depth)”和“驗證模式(verify mode)”(圖3)。
使用OpenSSL的程序可以通過調(diào)用SSL_connect函數(shù)執(zhí)行SSL握手。證書驗證錯誤信息通過SSL_connect函數(shù)返回值提供,而其他錯誤由SSL_connect函數(shù)設置內(nèi)部verify result標識,應用程序必須調(diào)用SSL_get_verify_result函數(shù)以檢查是否發(fā)生此類任何錯誤[12]。
圖3 使用缺省信任鏈驗證SSL連接設置的OpenSSL API圖
JSSE。Java安全套接字擴展(JSSE)提供大量接口給Java應用程序(包括Android移動應用程序)建立SSL連接。低層通過SSLSocketFactory創(chuàng)建的SSL客戶端API可能不執(zhí)行主機驗證。
下面的代碼段來自于X.509的TrustManagerImpl.checkIdentity。
private void checkIdentity(String hostname,
X509Certificate cert,String algorithm)
throws CertificateException{
if(algorithm!=null&&algorithm.length()!=0){
....
if(algorithm.equalsIgnoreCase("HTTPS")){
HostnameChecker. getInstance(HostnameChecker.TYPE_TLS).match(hostname,cert);
}else if(algorithm.equalsIgnoreCase
("LDAP")){
HostnameChecker. getInstance(HostnameChecker.TYPE_LDAP).match(hostname,cert);
}else{
throw new CertificateException("Unknown
identification algorithm:"+algorithm);
}
}
}
上述代碼段的功能是實現(xiàn)對主機名與認證的確認。如果algorithm設置為HTTPS或 LDAP,checkIdentity方法就拋出一個異常,其不同于OpenSSL的返回值,即使驗證失敗也認為應用程序會檢查該值。在HttpsClient和HttpsURLConnection等JSSE應用程序中,當創(chuàng)建SSL客戶端時就會調(diào)用SetHostnameVerification方法并設置為HTTPS,其結(jié)果是調(diào)用HostnameChecker和驗證證書。
如果客戶端字段是NULL或空串,checkIdentitysilently跳過主機驗證且不拋出異常。該作法是為了適應基于證書協(xié)議實現(xiàn)而重用JSSE的默認值HTTPS或LDAP以驗證信任鏈。Java7.3的證書驗證代碼與Java6不同,如果字段是NULL或空串,checkIdentity根本就不調(diào)用。
private void checkTrusted(X509Certificate[]chain,
String authType,Socket socket,boolean isClient)
throws CertificateException{
...
String identityAlg=sslSocket.getSSLParameters().
getEndpointIdentificationAlgorithm();
if(identityAlg!=null&&identityAlg.length!=0){
String hostname=session.getPeerHost();
checkIdentity(hostname,chain[0],identityAlg);
}
}
上述代碼段實現(xiàn)對信任鏈的驗證功能,驗證鏈路中各節(jié)點主機間的信任關系。在SSL客戶端使用SSLSocketFactory,如果該字段為空JSSE就不執(zhí)行主機驗證,主機驗證工作就由運行在JSSE上層的軟件完成,該功能在JSSE參考指南有警告說明。
在發(fā)送任何數(shù)據(jù)前使用SSLSockets/SSLEngines應該檢查各節(jié)點證書,因為SSLSocket和SSLEngine類不自動驗證與URL匹配的域名驗證,如果不進行主機驗證應用程序,這樣就可以利用 URL欺騙。Java軟件使用SSLSocketFactory創(chuàng)建SSL客戶端但不執(zhí)行主機驗證,說明開發(fā)人員并沒意識到該特性,而JSSE接口執(zhí)行主機驗證也增加了其混亂性。
實際上大多數(shù)應用程序依賴數(shù)據(jù)運輸層建立HTTPS連接,這些在內(nèi)部使用SSL庫架構通常是不透明的應用程序。
Apache HttpClient。Apache HttpClient是基于 JDK的客戶端HTTP Java庫。Apache HttpClient廣泛用于Web服務中間件,是因為本地化JDK不支持SOAP Web服務,且在發(fā)送HTTP POST請求等功能時,Apache Http-Client比JDK提供了更好性能。Apache HttpClient使用JSSE的SSLSocketFactory建立SSL連接,即Apache Http客戶端必須執(zhí)行自己的主機驗證,這樣對于基于舊版本的HttpClient不驗證主機就會導致很多漏洞。Apache HttpClient使用HttpHost數(shù)據(jù)結(jié)構描述 HTTP連接,而HttpHost并沒有任何內(nèi)部一致性檢查,如允許連接到443端口。
cURL。cURL是一個從遠程服務器獲取數(shù)據(jù)的工具和庫。從7.10版本開始,cURL默認驗證SSL證書。內(nèi)部cURL使用OpenSSL驗證信任鏈和主機,其功能由參數(shù) CURLOPT_SSL_VERIFYPEER(默認值 true)和CURLOPT_SSL_VERIFYHOST(默認值2)控制,VERIFYPEER參數(shù)是布爾類型,而VERIFYHOST參數(shù)是整數(shù)。開發(fā)人員經(jīng)常誤解這些參數(shù),而且將CURLOPT_SSL_VERIFYHOST設置為TRUE,從而改變其值為1,這樣如果意外禁用主機驗證就會帶來不安全的后果。
PHP。PHP提供多種方法建立SSL連接,打開遠程服務器套接字的fsockopen能夠通過在URL中加入“SSL://”連接到SSL服務器。盡管fsockopen不執(zhí)行任何證書檢查,PHP應用程序開發(fā)人員通常用它來建立SSL連接。PHP也提供cURL綁定,使用cURL默認設置建立SSL連接以適應證書驗證,開發(fā)人員可能設置cURL不正確選項改變默認值,從而破壞證書驗證。
云客戶端API。云端API是連接云端與客戶端的橋梁,它包括面向各不同協(xié)議客戶端的API和面向第三方的API。前者的作用是云端與客戶端之間的交互,使用RTMP或RTMPE協(xié)議傳輸;后者允許定制特定函數(shù)輸出給第三方,使用RTMP或RTMPE傳輸,或通過本地的PHP中轉(zhuǎn)為HTTP傳輸。對于第三方云計算平臺的運行在客戶端SDK,可以通過第三方軟件傳送用戶數(shù)據(jù)到基于云的存儲、管理用戶基于云的計算以及訪問其他云服務。如Amazon在Java、PHP、Python和Perl中提供的EC2 API工具,以及Apache提供的獨立庫訪問多個云的Libcloud。
Web services中間件。許多程序依賴于Web services。Web services是一軟件系統(tǒng),用來支持網(wǎng)絡主機間的互操作性,其服務由一個接口描述計算機可讀的XML格式文檔。不同系統(tǒng)提供不同具體實現(xiàn)接口,異構系統(tǒng)間通過發(fā)送和接收信息實現(xiàn)服務的互操作。
使用基于XML的SOAP或REST的Web services發(fā)送與接收消息。從客戶端軟件的角度來講,Web services可以被認為是提供遠程過程調(diào)用RPC接口,而SOAP或REST中間件是RPC調(diào)用參數(shù)的封裝和解封。
Web service通過使用SOAP中間件與現(xiàn)有Java軟件進行交互。同樣地,如果一個Android應用程序需要實時發(fā)送通知,它可以使用客戶端庫連接到基于REST的Pusher服務。這些中間件框架負責發(fā)送Web service信息到網(wǎng)絡,如果連接必須是安全的,中間件通常使用SSL,而不是現(xiàn)在的SSL連接管理數(shù)據(jù)傳輸庫代替。
2007年發(fā)布并廣泛使用的Apache HttpClient 3.1版本與較早期的版本一樣,使用JSSE的SSLSocketFactory而不執(zhí)行自身主機驗證來建立SSL連接。這樣Apache HttpClient 3.*接受任何具有有效信任鏈的證書,而不管其名字。
HttpClient主機驗證的缺陷在4.0版得到修正,在4.2.1版本中,有其自身的主機驗證和指派JSSE進行信任鏈驗證。這樣依靠SSL連接建立HttpClient實現(xiàn)對應用程序的安全性有較少影響。HttpClient的使用通常隱藏內(nèi)部Web services中間件,從而跳過主機關于SSL證書認證。
值得注意的是,如果用戶將自定義主機驗證代碼添加到HttpClient 4.*是錯誤的,并且將拒絕有效證書。下面的代碼來自HttpClient 4.2.1:
String parts[]=cn.split("\.");
boolean doWildcard=parts.length > =3 &&
parts[0].endsWith("*")&&
acceptableCountryWildcard(cn)&&
!isIPAddress(host);
if(doWildcard){
if(parts[0].length()>1){
String prefix=parts[0].substring(0,parts.length-2);
String suffix=cn.substring(parts[0].length());
String hostSuffix=hostName.substring
(prefix.length());
match=hostName.startsWith(prefix)
&& hostSuffix.endsWith(suffix);
}else{
match=hostName.endsWith(cn.substring(1));
}
if(match&& strictWithSubDomains){
match=countDots(hostName)==countDots(cn);
}
}else{
match=hostName.equals(cn);
}
該代碼段具有計算來自CN的主機名驗證、通配符使用處理、CN與主機通配符忽略處理功能。其方法是計算參數(shù)parts的數(shù)字部分減去2的前綴長度,域名第一部分有效性什么也不操作而其就不具有邏輯性。如,如果證書名是 a*.<b>.<b>.com,它將拒絕 ail.<b>.<b>.com。而且最初的補丁及其衍生代碼在解析IPv4地址通用表達式中有一個小缺陷,使其接受從0開始的IP地址,但這并不會立即導致安全漏洞。
通過對具有代表性的中間件應用程序使用SSL用于安全連接的情況分析,特別對中間件庫使用SSL作為多層軟件協(xié)議棧的一部分功能分析,發(fā)現(xiàn)程序在具有潛在不安全的公用網(wǎng)絡中傳輸極其敏感的數(shù)據(jù)時,非常重要的一點就是必須正確使用SSL。
同時通過對SSL在應用程序軟件設計中的使用,詳細說明了依賴標準SSL庫的應用程序(如JSSE、OpenSSL等)執(zhí)行SSL證書認證可能存在的不安全因素,這些不安全性在有些軟件中是隨處可見的,如FPS、PayPal、EC2以及其他客戶端遠程管理云存儲和云基礎設施等。這些特點對中間件攻擊來講,SSL連接是沒有安全性的。
另外,對于一些宣稱是更加安全的SSL策略,通過分析發(fā)現(xiàn),可以通過開發(fā)代碼分析工具發(fā)現(xiàn)SSL連接建立邏輯錯誤問題。對于這類情況,可以通過形式驗證技術設計和編程,支持自動檢查應用程序是否正確使用SSL庫,而不誤解關鍵選項和參數(shù)的定義,以此來設計更好的API用于SSL或其他安全的網(wǎng)絡協(xié)議,從而解決其不安全隱患等。
[1]舒劍.一種無證書認證密鑰協(xié)商協(xié)議的分析與改進[J].計算機應用研究,2012,29(1):294-296.
[2]張延紅,陳明.標準模型下強安全的無證書認證密鑰協(xié)商協(xié)議[J].四川大學學報:工程科學版,2013,45(1):125-132.
[3] HTTP over TLS[EB/OL].(2000-05-01)[2014-03-10].http://www.ietf.org/rfc/rfc2818.txt.
[4]Internet X.509 public key infrastructure certificate and certificate revocation list(CRL)profile[EB/OL].(2008-05-01)[2014-03-10].http://tools.ietf.org/html/rfc5280.
[5]何 旭.一種可擴展Web模型安全機制[J].計算機系統(tǒng)應用,2012,21(8):89-93.
[6]何旭.隱私模式瀏覽器的安全性分析[J].西華大學學報:自然科學版,2012,31(3):17-22.
[7] The Secure Sockets Layer(SSL)protocol version 3.0[EB/OL].(2011-08-01)[2014-03-11].http://tools.ietf.org/html/rfc6101.
[8] Representation and verification of domain-based application service identity within Internet public key infrastructure using X.509(PKIX)certificates in the context of Transport Layer Security(TLS)[EB/OL].(2011-03-01)[2014-03-11].http://tools.ietf.org/html/rfc6125.
[9] https should check CN of x509 cert[EB/OL].(2007-04-22)[2014-03-12].https://issues.apache.org/jira/browse/HTTPCLIENT-613.
[10] Internet X.509 public key infrastructure certificate policy and certification practices framework[EB/OL].(1999-03-01)[2014-03-11].http://www.ietf.org/rfc/rfc2527.txt.
[11] Viega J,Messier M.Secure programming cookbook for C and C++[M].California:O'Reilly Media,2003.
[12] Moxie M.IE SSL vulnerability[EB/OL].(2002-05-08)[2014-03-12].http://www.thoughtcrime.org/ie-ssl-chain.txt.