http 與 https
https 跟 http 的最大區別在于 https 多加了一個保障通訊安全的層.
- https協議需要到ca申請證書,一般免費證書很少,需要交費.
- http是超文本傳輸協議,信息是明文傳輸,https 則是具有安全性的ssl加密傳輸協議。
- http和https使用的是完全不同的連接方式,用的端口也不一樣,前者是80,后者是443。
- http的連接很簡單,是無狀態的;HTTPS協議是由SSL+HTTP協議構建的可進行加密傳輸、身份認證的網絡協議,比http協議安全。
SSL 證書
Transport Layer Security
Secure Sockets Layer 其實它就包含倆部分:
- 一個身份標識, 一個用來識別身份的東西, 有點類似警察叔叔通過護照或駕照查你的身份;
- 一個公共密鑰, 這個用來給數據加密, 而且只有證書的持有者才能解密.
SSL 證書就倆個功能, 身份驗證跟保障通訊過程中的數據安全.
另外還有一點很重要. 那就是一個證書可以給另外一個證書“簽字”. 用 layman 的話說就是 Bob 用他自己的證書在別的證書上蓋上 “同意” 兩個紅紅的大字. 如果你信任 Bob (當然還有他的證書), 那么你也可以信任由他簽發的證書. 在這個例子中, Bob 搖身一變, 成了證書頒發機構(Certificate Authority). 現在主流的瀏覽器都自帶一大堆受信任證書頒發機構(trusted Certificate Authorities)(比如:Thawte, Verisign等).
瀏覽器是怎么使用證書的
籠統的講, 當你打開下列連接的時候 “https://www.yoursite.com” :
- 服務器會給瀏覽器發一個證書.
- 瀏覽器會對比證書中的“common name”(有時也叫 “subject”) 跟服務器的域名是否一樣. 例如, 一個從“www.yoursite.com” 網站發過來的證書就應該有一個內容是 “www.yoursite.com” 的 common name, 否則瀏覽器就會提示該證書有問題.
- 瀏覽器驗證證書真偽, 有點像門衛通過證件上的全息圖辨別你的證件是不是真的. 既然在現實生活中有人偽造別人的身份. 那么在網絡世界也就有人造假, 比如用你的域名“www.yoursite.com” 來偽造一個安全證書. 瀏覽器在驗證的時候, 會檢查這個證書是否是它信任機構頒發的, 如果不是, 那么瀏覽器就會提示這個證書可能有問題. 當然, 用戶可以選擇無視警告, 繼續使用.
- 一旦證書通過驗證 (或是用戶無視警告, 繼續使用有問題的證書), 瀏覽器就開始利用證書中的公開密鑰加密數據并傳給服務器.
TLS (SSL)中的加密
一旦服務器發過來的證書通過驗證, 客戶端就會利用證書中包含的公共密鑰加密某個指定的共享密鑰, 然后發給服務器. 這個加密過的共享密鑰只能用服務器的私有密鑰才能解密(非對稱加密), 別人無法解密出其中的內容. 服務器把解密出來的共享密鑰保存起來, 供本次連接會話專用. 從現在開始, 服務器跟客戶端之間的所有通訊信息都用這個共享密鑰加密解密(對稱加密).
漏洞描述
對于數字證書相關概念、Android 里 https 通信代碼就不再復述了,直接講問題。缺少相應的安全校驗很容易導致中間人攻擊,而漏洞的形式主要有以下3種:
- 自定義X509TrustManager。在使用HttpsURLConnection發起 HTTPS 請求的時候,提供了一個自定義的X509TrustManager,未實現安全校驗邏輯,下面片段就是常見的容易犯錯的代碼片段。如果不提供自定義的X509TrustManager,代碼運行起來可能會報異常(原因下文解釋),初學者就很容易在不明真相的情況下提供了一個自定義的X509TrustManager,卻忘記正確地實現相應的方法。本文重點介紹這種場景的處理方式。
TrustManager tm = new X509TrustManager() {
public void checkClientTrusted(X509Certificate[] chain, String authType)
throws CertificateException {
//do nothing,接受任意客戶端證書
}
public void checkServerTrusted(X509Certificate[] chain, String authType)
throws CertificateException {
//do nothing,接受任意服務端證書
}
public X509Certificate[] getAcceptedIssuers() {
return null;
}
};
sslContext.init(null, new TrustManager[] { tm }, null);
- 自定義了HostnameVerifier。在握手期間,如果 URL 的主機名和服務器的標識主機名不匹配,則驗證機制可以回調此接口的實現程序來確定是否應該允許此連接。如果回調內實現不恰當,默認接受所有域名,則有安全風險。代碼示例。
HostnameVerifier hnv = new HostnameVerifier() {
@Override
public boolean verify(String hostname, SSLSession session) {
// Always return true,接受任意域名服務器
return true;
}
};
HttpsURLConnection.setDefaultHostnameVerifier(hnv);
- 信任所有主機名。
SSLSocketFactory sf = new MySSLSocketFactory(trustStore);
sf.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);