Android App 安全的HTTPS 通信

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 其實它就包含倆部分:

  1. 一個身份標識, 一個用來識別身份的東西, 有點類似警察叔叔通過護照或駕照查你的身份;
  1. 一個公共密鑰, 這個用來給數據加密, 而且只有證書的持有者才能解密.
    SSL 證書就倆個功能, 身份驗證跟保障通訊過程中的數據安全.

另外還有一點很重要. 那就是一個證書可以給另外一個證書“簽字”. 用 layman 的話說就是 Bob 用他自己的證書在別的證書上蓋上 “同意” 兩個紅紅的大字. 如果你信任 Bob (當然還有他的證書), 那么你也可以信任由他簽發的證書. 在這個例子中, Bob 搖身一變, 成了證書頒發機構(Certificate Authority). 現在主流的瀏覽器都自帶一大堆受信任證書頒發機構(trusted Certificate Authorities)(比如:Thawte, Verisign等).

瀏覽器是怎么使用證書的

籠統的講, 當你打開下列連接的時候 “https://www.yoursite.com” :

  1. 服務器會給瀏覽器發一個證書.
  1. 瀏覽器會對比證書中的“common name”(有時也叫 “subject”) 跟服務器的域名是否一樣. 例如, 一個從“www.yoursite.com” 網站發過來的證書就應該有一個內容是 “www.yoursite.com” 的 common name, 否則瀏覽器就會提示該證書有問題.
  2. 瀏覽器驗證證書真偽, 有點像門衛通過證件上的全息圖辨別你的證件是不是真的. 既然在現實生活中有人偽造別人的身份. 那么在網絡世界也就有人造假, 比如用你的域名“www.yoursite.com” 來偽造一個安全證書. 瀏覽器在驗證的時候, 會檢查這個證書是否是它信任機構頒發的, 如果不是, 那么瀏覽器就會提示這個證書可能有問題. 當然, 用戶可以選擇無視警告, 繼續使用.
  3. 一旦證書通過驗證 (或是用戶無視警告, 繼續使用有問題的證書), 瀏覽器就開始利用證書中的公開密鑰加密數據并傳給服務器.

TLS (SSL)中的加密

一旦服務器發過來的證書通過驗證, 客戶端就會利用證書中包含的公共密鑰加密某個指定的共享密鑰, 然后發給服務器. 這個加密過的共享密鑰只能用服務器的私有密鑰才能解密(非對稱加密), 別人無法解密出其中的內容. 服務器把解密出來的共享密鑰保存起來, 供本次連接會話專用. 從現在開始, 服務器跟客戶端之間的所有通訊信息都用這個共享密鑰加密解密(對稱加密).


漏洞描述

對于數字證書相關概念、Android 里 https 通信代碼就不再復述了,直接講問題。缺少相應的安全校驗很容易導致中間人攻擊,而漏洞的形式主要有以下3種:

  1. 自定義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);
  1. 自定義了HostnameVerifier。在握手期間,如果 URL 的主機名和服務器的標識主機名不匹配,則驗證機制可以回調此接口的實現程序來確定是否應該允許此連接。如果回調內實現不恰當,默認接受所有域名,則有安全風險。代碼示例。
HostnameVerifier hnv = new HostnameVerifier() {
    @Override
    public boolean verify(String hostname, SSLSession session) {
     // Always return true,接受任意域名服務器
     return true;
    }
};
HttpsURLConnection.setDefaultHostnameVerifier(hnv);
  1. 信任所有主機名。
SSLSocketFactory sf = new MySSLSocketFactory(trustStore);
sf.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);

參考資料

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容