相信你們都會注意到不少網站的地址欄上會出現一個綠色的小鎖,好比:算法
若是地址欄中出現了這樣子的綠色小鎖,則表示當前頁面是經過 HTTPS 傳遞的,只要證書是正確的,那麼目前來講能夠保證網頁內容沒有被篡改以及即便第三者截取到了通訊內容也沒法從密文得到明文。數組
HTTPS 相似於 HTTP 協議的「安全版」,其全稱爲超文本傳輸安全協議(英文:HyperText Transfer Protocol Secure,縮寫 HTTPS),如下引用自維基百科:瀏覽器
超文本傳輸安全協議是一種透過計算機網絡進行安全通信的傳輸協議。HTTPS 經由 HTTP 進行通信,但利用 SSL/TLS 來加密封包。HTTPS 開發的主要目的,是提供對網站服務器的身份認證,保護交換資料的隱私與完整性。這個協議由網景公司(Netscape)在1994年首次提出,隨後擴展到互聯網上。
HTTPS 的用途就是在不安全的網絡上創建安全的信道,用於傳輸敏感數據(好比信用卡號、密碼等)或者用於保護互聯網帳戶不被盜取、保證網頁數據的正確性等。安全
經過 HTTP 協議傳輸的數據就像 Tom 給 Jerry 寫信,Tom 把寫好的信直接交給了郵遞員,信封沒有密封甚至直接沒有信封,郵遞員能夠隨意查看 Tom 給 Jerry 的信件、修改信件裏的內容甚至冒充 Tom 與 Jerry 進行通訊。服務器
這明顯是不安全的。網絡
Tom 爲了避免讓郵遞員偷看他給 Jerry 的信件並確保本身的信件不會被替換,決定在信的最後附上本身的簽名,並在信封上加上火漆印章(一旦信件被私自拆閱則印章就會損毀),這時,若是 Tom 再給 Jerry 寫信,Jerry 只須要在收到信件的時候檢查信封上的火漆印章和信件最後的簽名,就能夠確保收到的信件沒有被郵遞員拆閱或者替換過。curl
在這個例子中,Tom 和 Jerry 分別表明客戶端和服務器(或者服務器與服務器、客戶端與客戶端),郵遞員至關於不安全的網絡信道(好比包含 ISP、代理服務器等),而信件就至關於 HTTP 請求中的內容。Tom 簽名及蓋火漆印章的過程至關於加密,驗證簽名和火漆印章完整性的時候就至關於身份驗證和解密。函數
在實際中,不使用 HTTPS 的風險不只是數據被隨時監視,無良的 ISP 可能還會給網頁上加上廣告,甚至能夠隨意增刪經過 HTTP 傳輸的網站代碼,這很是危險。而 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.org subject: 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 Encrypt issuer: C=US; O=Let's Encrypt; CN=Let's Encrypt Authority X3
這一步中,瀏覽器會向服務器發出創建 HTTPS 的請求,在請求中,瀏覽器會帶上一些創建鏈接的必要信息(注意:這一步的信息全都是明文的),包括:
客戶端在發出 client_hello
消息以後,會等待服務器返回 server_hello
消息,包含和 client_hello
相同的參數。通常來講,參數結構以下:
client_hello
中發來的隨機數的另外一個獨立的隨機數。一般來講,服務器會在 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 相關的消息結束。在發送此消息以後,服務器會等待客戶端應答,該消息沒有參數。
客戶端在收到服務器發來的 server_hello_done
消息以後,會驗證服務器提供的證書是否合法,並檢查 server_hello
的各項參數。若是驗證經過,則客戶端會向服務器發送一條或多條消息。
若是服務器發送了 certificate_request
消息,且客戶端有合適的證書,則客戶端會發送一條 certificate
消息,不然會發送一個「無證書警報」。
而後客戶端會發送 client_key_exchange
消息,其內容取決於密鑰交換的類型:
最後,若是客戶端具備證書且證書具有簽名能力(即除了帶固定 Diffie-Hellman 參數外的全部證書),能夠發送一個 certificate_verify
消息來提供對客戶端證書的精確認證。
通過以上步驟,客戶端和服務器已經能夠經過獲得的消息計算出 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 中僅使用非對稱加密算法交換對稱密鑰,交換密鑰以後的通訊內容均使用對稱加密算法加密和解密,這樣既能夠保證密鑰的安全也能夠保證內容的加解密速度,這對於移動端設備來講相當重要。
中間人攻擊(Man In The Middle Attack,簡稱 MITM),是指攻擊者與本來通訊的兩個實體分別創建通訊,並交換攻擊者所收到的數據,讓本來通訊的兩個實體誤覺得本身在一條加密的信道中和對方直接對話,但其實整個對話過程都直接受到攻擊者的控制,攻擊者能夠隨意增長、刪除、修改通訊的內容。通常來講,加密協議都會加入一些特殊的方法來實現通訊雙方的互相認證,以免中間人攻擊。好比 HTTPS 中就使用證書機制來防止中間人攻擊。
以前提到匿名 Diffie-Hellman 方法易受中間人攻擊,就是由於其不對祕密內容(即 Diffie-Hellman 公鑰參數)進行簽名認證致使的。在這個過程當中,服務器向客戶端發送的公鑰參數若是被攻擊者截獲並替換爲本身的公鑰參數,那麼攻擊者就能夠假裝成服務器和客戶端分別和客戶端和服務器進行對話,並且能夠徹底控制通訊的內容。
當進行 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 證書,避免由於證書錯誤致使握手失敗。