浅谈 HTTPS
淺談 HTTPS
相信大家都會注意到很多網站的地址欄上會出現一個綠色的小鎖,比如:
如果地址欄中出現了這樣子的綠色小鎖,則表示當前頁面是通過 HTTPS 傳遞的,只要證書是正確的,那么目前來說可以保證網頁內容沒有被篡改以及即使第三者截取到了通信內容也無法從密文獲得明文。
什么是 HTTPS?
HTTPS 類似于 HTTP 協議的“安全版”,其全稱為超文本傳輸安全協議(英文:HyperText Transfer Protocol Secure,縮寫 HTTPS),以下引用自維基百科:
超文本傳輸安全協議是一種透過計算機網絡進行安全通訊的傳輸協議。HTTPS 經由 HTTP 進行通訊,但利用 SSL/TLS 來加密封包。HTTPS 開發的主要目的,是提供對網站服務器的身份認證,保護交換資料的隱私與完整性。這個協議由網景公司(Netscape)在1994年首次提出,隨后擴展到互聯網上。HTTPS 的用途就是在不安全的網絡上建立安全的信道,用于傳輸敏感數據(比如信用卡號、密碼等)或者用于保護互聯網賬戶不被盜取、保證網頁數據的正確性等。
HTTPS 有什么用?
通過 HTTP 協議傳輸的數據就像 Tom 給 Jerry 寫信,Tom 把寫好的信直接交給了郵遞員,信封沒有密封甚至直接沒有信封,郵遞員可以隨意查看 Tom 給 Jerry 的信件、修改信件里的內容甚至冒充 Tom 與 Jerry 進行通信。
這明顯是不安全的。
Tom 為了不讓郵遞員偷看他給 Jerry 的信件并確保自己的信件不會被替換,決定在信的最后附上自己的簽名,并在信封上加上火漆印章(一旦信件被私自拆閱則印章就會損毀),這時,如果 Tom 再給 Jerry 寫信,Jerry 只需要在收到信件的時候檢查信封上的火漆印章和信件最后的簽名,就可以確保收到的信件沒有被郵遞員拆閱或者替換過。
在這個例子中,Tom 和 Jerry 分別代表客戶端和服務器(或者服務器與服務器、客戶端與客戶端),郵遞員相當于不安全的網絡信道(比如包含 ISP、代理服務器等),而信件就相當于 HTTP 請求中的內容。Tom 簽名及蓋火漆印章的過程相當于加密,驗證簽名和火漆印章完整性的時候就相當于身份驗證和解密。
在實際中,不使用 HTTPS 的風險不僅是數據被隨時監視,無良的 ISP 可能還會給網頁上加上廣告,甚至可以隨意增刪通過 HTTP 傳輸的網站代碼,這非常危險。而 HTTPS 可以避免這些風險。
HTTPS 連接的握手過程
我們可以使用 curl 命令來簡略查看建立 HTTPS 時的握手過程,在命令行中執行:curl -v -I -L https://ymfe.org
能得到如下的輸出:
簡單說明一下連接的建立過程:
# 表示建立了和 ymfe.org 服務器 443 端口的連接。 Connected to ymfe.org (123.56.155.201) port 443 (#0) # 客戶端發出 client_hello 消息。 TLSv1.2 (OUT), TLS handshake, Client hello (1): # 服務器發出 server_hello 消息。 TLSv1.2 (IN), TLS handshake, Server hello (2): # 服務器發出 certificate 消息。 TLSv1.2 (IN), TLS handshake, Certificate (11): # 服務器發出 server_key_exchange 消息。 TLSv1.2 (IN), TLS handshake, Server key exchange (12): # 服務器發出 server_done 消息。 TLSv1.2 (IN), TLS handshake, Server finished (14): # 客戶端發出 client_key_exchange 消息。 TLSv1.2 (OUT), TLS handshake, Client key exchange (16): # 客戶端發出加密后的 client_hello 消息。 TLSv1.2 (OUT), TLS change cipher, Client hello (1): # 客戶端發出 hello_done 消息。 TLSv1.2 (OUT), TLS handshake, Finished (20): # 服務器將加密后的 client_hello 消息發回。 TLSv1.2 (IN), TLS change cipher, Client hello (1): # 握手結束。 TLSv1.2 (IN), TLS handshake, Finished (20): # SSL 連接采用 ECDHE-RSA-AES256-GCM-SHA384 密碼套件。 # ECDHE 表示密鑰交換方法采用橢圓曲線迪菲-赫爾曼交換方法 # RSA 表示密鑰交換中使用的簽名方式 # AES-256-GCM 表示的是對稱加密算法 # SHA-384 表示的是內容完整性校驗使用的哈希算法 SSL connection using TLSv1.2 / ECDHE-RSA-AES256-GCM-SHA384 # 之后的幾行包含了證書的內容,包括有效時間、常用名、證書簽發機構等。 Server certificate: # Common Name 為 ymfe.orgsubject: CN=ymfe.org# 在此時間之前無效start date: Aug 31 05:50:00 2017 GMT# 在此時間之后無效expire date: Nov 29 05:50:00 2017 GMT# 域名和證書的域名匹配subjectAltName: host "ymfe.org" matched cert's "ymfe.org"# 簽發者是 Let's Encryptissuer: C=US; O=Let's Encrypt; CN=Let's Encrypt Authority X3握手的詳細步驟
client_hello
這一步中,瀏覽器會向服務器發出建立 HTTPS 的請求,在請求中,瀏覽器會帶上一些建立連接的必要信息(注意:這一步的信息全都是明文的),包括:
server_hello
客戶端在發出 client_hello 消息之后,會等待服務器返回 server_hello 消息,包含和 client_hello 相同的參數。一般來說,參數結構如下:
(certificate) (+ server_key_exchange) (+ certificate_request) + server_hello _done
通常來說,服務器會在 certificate 消息中發送其自身的證書供客戶端進行驗證,這個消息包含一個或一組 X. 509 證書。如果采用的是固定 Diffie-Hellman 方法(更多關于 Diffie-Hellman 的內容請查看維基百科上的詞條 Diffie-Hellman),此 certificate 消息將使用服務器 Diffie-Hellman 公鑰參數作為服務器的密鑰交換消息。有一種密鑰交換方法——匿名 Diffie-Hellman 方法,不需要 certificate 消息。該算法使用基本的 Diffie-Hellman 算法,在向對方發送其 Diffie-Hellman 公鑰參數時,不進行認證(因為沒有認證,所以這種方法容易受到中間人攻擊)。
接著,除了兩種情況:(1) 服務器發送了帶有固定 Diffie-Hellman 參數的證書;(2) 使用 RSA 密鑰交換;不需要發送 server_key_exchange 消息,其余情況服務器均會發送 server_key_exchange 消息,用以向客戶端發送之后用以生成密鑰的各項參數。該消息的內容根據密鑰交換算法不同而不同,具體內容不在本文討論之列。該項參數作為 server_key_exchange 消息的第一項值。
然后,服務器將對消息使用散列函數(根據不同簽名算法選擇不同的散列函數,例如 MD5 或者 SHA-1 等)對客戶端發來的隨機數、服務器生成的隨機數以及 server_key_exchange 消息中的各項參數進行求值并使用服務器的私鑰進行簽名。該值作為 server_key_exchange 消息中的第二項值。
如果服務器需要驗證客戶端的身份(即雙向認證),則會發送 certificate_request 消息,請求客戶端發送其自身的證書。
最后,服務器發送 server_hello_done 消息,表明服務器的 hello 相關的消息結束。在發送此消息之后,服務器會等待客戶端應答,該消息沒有參數。
(certificate) + client_key_change (+ certificate_verify)
客戶端在收到服務器發來的 server_hello_done 消息之后,會驗證服務器提供的證書是否合法,并檢查 server_hello 的各項參數。如果驗證通過,則客戶端會向服務器發送一條或多條消息。
如果服務器發送了 certificate_request 消息,且客戶端有合適的證書,則客戶端會發送一條 certificate 消息,否則會發送一個“無證書警報”。
然后客戶端會發送 client_key_exchange 消息,其內容取決于密鑰交換的類型:
最后,如果客戶端具有證書且證書具備簽名能力(即除了帶固定 Diffie-Hellman 參數外的所有證書),可以發送一個 certificate_verify 消息來提供對客戶端證書的精確認證。
change_cipher_spec + finished
經過以上步驟,客戶端和服務器已經可以通過得到的消息計算出 Master Key 了。從現在開始,客戶端和服務器都將開始使用協商好的加密算法、密鑰進行通信,在正式傳遞消息之前會計算 Master Key 及 Master Key 和之前握手過程中收到的所有信息的 hash,并通過協商好的加密算法使用 Master Key 加密,作為 change_cipher_spec 消息的內容,接著發送 finished 消息。服務器在收到客戶端發來的 change_cipher_spec 和 finished 消息之后,也會計算 Master Key 并使用協商好的加密算法和 Master Key 計算 Master Key 和之前握手過程中收到的所有信息的 hash,發回給客戶端用以驗證。至此,握手階段結束,之后就可以交換應用層的內容了。
如何加密應用層內容
細心的讀者可能發現,在建立連接的過程中,交換的僅僅是密鑰而不是內容,為什么這么復雜的過程僅僅交換了密鑰呢?
對稱加密和非對稱加密
常用的加密方式分為兩種:
- 對稱加密:加密和解密使用的是相同的密鑰。
- 非對稱加密:加密和解密使用的不是相同的密鑰,而是一對密鑰對,分別稱為公鑰和私鑰。
對稱加密最大的特點就是通信雙方都需要知道加密密鑰,這對于互聯網上的服務器和客戶端來說,在事先沒有建立安全信道的情況下安全地傳送密鑰幾乎是不可能的。不過相較于非對稱加密來說,對稱加密的優點就是快。
非對稱加密最大的特點就是通信雙方只需要知道密鑰對中的一個,而且使用公鑰加密的內容只能通過私鑰解密,使用私鑰加密的內容只能通過公鑰解密。對于一個實體來說,公鑰可以公開給任何人,只需要保證自身的私鑰的安全,就可以保證其與另一個實體的通信內容不會被第三者竊取。
HTTPS 用了哪種加密方法?
在 HTTPS 中,對稱加密和非對稱加密都用到了。非對稱加密可以在不安全的信道上傳遞秘密內容,但是由于通常使用的非對稱加密方法相較于對稱加密算法慢很多,因此在 HTTPS 中僅使用非對稱加密算法交換對稱密鑰,交換密鑰之后的通信內容均使用對稱加密算法加密和解密,這樣既可以保證密鑰的安全也可以保證內容的加解密速度,這對于移動端設備來說至關重要。
擴展閱讀
中間人攻擊
中間人攻擊(Man In The Middle Attack,簡稱 MITM),是指攻擊者與原本通信的兩個實體分別建立通信,并交換攻擊者所收到的數據,讓原本通信的兩個實體誤以為自己在一條加密的信道中和對方直接對話,但其實整個對話過程都直接受到攻擊者的控制,攻擊者可以隨意增加、刪除、修改通信的內容。一般來說,加密協議都會加入一些特殊的方法來實現通信雙方的互相認證,以避免中間人攻擊。比如 HTTPS 中就使用證書機制來防止中間人攻擊。
之前提到匿名 Diffie-Hellman 方法易受中間人攻擊,就是因為其不對秘密內容(即 Diffie-Hellman 公鑰參數)進行簽名認證導致的。在這個過程中,服務器向客戶端發送的公鑰參數如果被攻擊者截獲并替換為自己的公鑰參數,那么攻擊者就可以偽裝成服務器和客戶端分別和客戶端和服務器進行對話,而且可以完全控制通信的內容。
SNI
當進行 SSL/TLS 握手時,服務器會提供自身的證書給客戶端以供客戶端驗證。但是如果一臺服務器上托管了多個 HTTPS 站點,那么服務器提供的證書可能就是錯誤的。而對于 SSL/TLS 握手連接來說,服務器名稱不匹配會導致客戶端斷開連接,因為服務器名稱不匹配表明可能有人正在進行中間人攻擊。
一張證書中包含多個主機名是可以的,在之前提到的 curl -v -I -L https://ymfe.org 命令中出現了一個叫做 subjectAltName 的字段,這個字段就是用于指定多個域名的,在 common name 和 subjectAltName 中還可以使用通配符。
一臺服務器托管多個站點是非常常見的。在被托管的站點中,它們可能是同一個域名下的子域名,也可能完全不是同一個域名。如果多個站點都用同一張證書的話,那么服務器需要知道所有被托管的站點的域名,維護一個域名列表,這在很多情況下是不切實際的。而如果為每個 HTTPS 服務器都分配一個獨立的 IP 的話,不但會增加成本,還會惡化 IPv4 地址的枯竭情況。
基于名稱的虛擬主機可以讓多個 DNS 主機名指向同一個服務器。在客戶端發起請求的時候,會在 HTTP 頭部添加正在請求的主機名稱,服務器根據這個主機名稱來提供不同的服務。而 HTTPS 中,在建立握手之前是不會發送任何 HTTP 頭部的,也就無法獲取客戶端正在請求的主機名稱了。因此,無法使用 HTTP 頭部來決定給客戶端提供哪張證書。
SNI(Server Name Indication,服務器名稱指示)是一個擴展的 TLS 協議。這個協議允許客戶端在建立 HTTPS 握手時告知服務器它正在連接的服務器的名稱。這就允許服務器根據客戶端在握手消息發來的服務器名稱來提供正確的 HTTPS 證書,避免因為證書錯誤導致握手失敗。
總結
- 上一篇: django官方文档1.11编翻:1-1
- 下一篇: 服务器出问题排查