一、HTTP基礎知識
HTTP全稱Hyper Text Transfer Protocol
,即超文本傳輸協議。HTTP是一個應用層協議,可視爲一個在計算機世界裏專門在兩點之間傳輸文字、圖片、音頻、視頻等超文本數據的約定和規範。
1. HTTP請求流程
我們這裏就直接以一個常見的面試題引入啦。
在瀏覽器中輸入 www.baidu.com 後會發生什麼?
當一個用戶在瀏覽器裏輸入 www.baidu.com
這個 URL 時,將會發生很多操作。首先它會請求 DNS 把這個域名解析成對應的 IP 地址,然後根據這個 IP 地址在互聯網上找到對應的服務器,向這個服務器發起一個GET 請求,由這個服務器決定返回默認的數據資源給訪問用戶。在服務器端實際上還有很複雜的邏輯:服務器可能有好多臺,到底指定哪臺服務器來處理請求,這需要一個負載均衡設備來平均分配所有用戶的請求;還有請求的數據是存儲在分佈式緩存裏還是一個靜態文件中,或是在數據庫裏;當數據返回瀏覽器時,瀏覽器解析數據發現還有一些靜態資源(如 CSS、JS 或者圖片)時又會發起另外的 HTTP 請求,而這些請求有可能會在 CDN 上,那麼 CDN 服務器又會處理這個用戶的請求,大體上一個用戶請求會涉及這麼多操作。每一個細節都會影響這個請求最終是否會成功。
我們不去涉及其中過多的知識,單說HTTP的請求流程即可,從上面我們知道,HTTP協議是由客戶端發起的,由請求和響應構成,是一個標準的客戶端服務器模型(C/S),它的具體流程如下:
-
地址解析。域名系統DNS解析域名得到主機的IP地址;
-
封裝HTTP請求數據包。封裝的內容有以上部分結合本機自己的信息;
-
封裝成TCP包,建立TCP連接(TCP的三次握手);
-
客戶機發送請求命令。 建立連接後,客戶機向服務器發送一個請求;
-
服務器響應。服務器接到請求後,給予相應的響應信息;
-
服務器關閉TCP連接。一般Web服務器向瀏覽器發送了請求數據,它要關閉TCP連接;
-
客戶端解析報文。客戶端接收到響應報文後解析HTML代碼,並渲染。
2. 常見狀態碼
HTTP常見的狀態碼分爲五大類,如下表所示:
狀態碼類別 | 具體含義 | 常見狀態碼 |
---|---|---|
1xx | 提示信息,表示目前是協議處理的中間狀態,還需要後續的操作 | |
2xx | 成功,報文已經收到並被正確處理 | 200、204、206 |
3xx | 重定向,資源位置發生變動,需要客戶端重新發送請求 | 301、302、304 |
4xx | 客戶端錯誤,請求報文有誤,服務器無法處理; | 400、403、404 |
5xx | 服務器錯誤,服務器在處理請求時內部發生了錯誤。 | 500、501、502、503 |
-
1xx:
1xx
類狀態碼屬於提示信息,是協議處理中的一種中間狀態,實際用到的比較少。 -
2xx:
2xx
類狀態碼錶示成功處理了客戶端需求,也是我們瀏覽器發起請求時常見的狀態:- 【200 OK】:最常見的成功狀態碼,表示一切正常。如果是非
HEAD
請求,服務器返回的響應頭都會有 body 數據; - 【204 No Content】:也是常見的成功狀態碼,與 200 OK 基本相同,但響應頭沒有 body 數據;
- 【206 Partial Content】:是應用於 HTTP 分塊下載或斷點續傳,表示響應返回的 body 數據並不是資源的全部,而是其中的一部分,也是服務器處理成功的狀態;
- 【200 OK】:最常見的成功狀態碼,表示一切正常。如果是非
-
3xx:
3xx
類狀態碼錶示客戶端請求的資源發生了變動,需要客戶端用新的 URL 重新發送請求獲取資源,也就是重定向:- 【301 Moved Permanently】:表示永久重定向,說明請求的資源已經不存在了,需改用新的 URL 再次訪問;
- 【302 Found】:表示臨時重定向,說明請求的資源還在,但暫時需要用另一個 URL 來訪問;
注:301 和 302 都會在響應頭裏使用
Location
,指明後續要跳轉的 URL,瀏覽器會自動重定向新的 URL。 -
4xx:
4xx
類狀態碼錶示客戶端發送的報文有誤,服務器無法處理,也就是錯誤碼的含義:- 【400 Bad Request】:表示客戶端請求的報文有錯誤,但只是個籠統的錯誤;
- 【403 Forbidden】:表示服務器禁止訪問資源,並不是客戶端的請求出錯;
- 【404 Not Found】:表示請求的資源在服務器上不存在或未找到,所以無法提供給客戶端。
-
5xx:
5xx
類狀態碼錶示客戶端請求報文正確,但是服務器處理時內部發生了錯誤,屬於服務器端的錯誤碼:- 【500 Internal Server Error】:與400類似,是個籠統通用的錯誤碼,服務器發生了什麼錯誤,我們並不知道;
- 【501 Not Implement】:表示客戶端請求的功能還不支持,類似”即將開業,盡情期待“的意思;
- 【502 Bad Gatwy】:通常是服務器作爲網關或代理時返回的錯誤碼,表示服務器自身工作正常,訪問後端服務器發生了錯誤;
- 【503 Service Unavailable】:表示服務器當前很忙,暫時無法響應服務器,類似”網絡服務正忙,請稍後重試“的意思。
3. 常見字段
首先我們瞭解一下HTTP的報文結構,大概如下:
這是常見的請求報文,當然還有響應報文,兩者之間並不完全一致,這裏只簡單提及一下請求報文的格式。
首先是請求方法,常見的請求方法有 GET和POST兩種,之後跟着的是URL,即要訪問的地址,再後面跟着的就是協議版本,如:HTTP/1.1。
我們主要講解之後跟着的字段,即請求頭,請求頭的字段常以key-value
的形式,即”屬性名:屬性值“的形式傳遞若干數據,服務端據此獲取客戶端的信息。接下來我們就來了解常見的字段:
Accept
字段與Content-type
字段:
Accept字段用於客戶端向服務器發送報文時表示自己可接收的響應內容類型,如:Accept:text/plain
(文本類型);
類似的字段還有Accept-Charset
表示可接收的字符集;Accept-Encoding
表示可接受的響應內容的壓縮方式 ;Accept-Language
表示可接受的響應內容語言列表; Accept-Datetime
表示可接受的按照時間來表示的響應內容版本。
Content-Type字段用於服務器迴應時,告訴客戶端,本次數據的格式是什麼。
類似的字段還有Content-Encoding
字段表示數據的壓縮方法,表示服務器返回的數據使用什麼壓縮格式。
-
Host
字段Host字段用於客戶端發送請求時,用來指定服務器的域名。例如:
Host:www.baidu.com
。這裏需要與報文中的請求行的 URL 區分,Host字段與 URL 組成完整的請求URL,例如請求行中的URL爲/getPerson
,而Host字段爲www.baidu.com
,那麼兩者結合起來就是www.baidu.com/getPerson
。 -
Connection
字段Connection字段最常用於客戶端要求服務器使用 TCP 持久連接,以便其他請求複用。
Connection: keep-alive
擴展:
-
HTTP是無狀態的面向連接的協議,無狀態並不代表HTTP不能保持TCP連接,HTTP使用的不是UDP(無連接);
-
HTTP/1.1 版本的默認連接都是持久連接,但爲了兼容老版本的 HTTP,需要指定
Connection
首部字段的值爲Keep-Alive
。簡單的說,當一個網頁打開完成後,客戶端和服務器之間用於傳輸的 HTTP 數據的 TCP 連接不會立刻關閉,如果客戶端再次訪問這個服務器上的網頁,會繼續使用這一條已經建立的連接; -
Keep-Alive不會永久保持連接,它有一個保持時間,可以在不同的服務器軟件(如Apache)中設定這個時間。
-
-
Content-Length
字段服務器在返回數據時,會有
Content-Length
字段,表明本次迴應的數據長度。例如:Content-Length: 1000
,這表明了服務器本次迴應的數據長度是1000個字節,後面的字節就屬於下一個迴應了。
二、 HTTP存在的問題
1. 性能問題
在 HTTP/1.0 中有很大的性能問題,每次發起一個HTTP請求,都需要去建立一次TCP連接,而且還是串行請求,這使得 HTTP 在 TCP 的連接建立上花費了大量的開銷。對於這種問題,HTTP/1.1 中提出了長連接的通信方式,也叫持久連接。這種連接的好處在於減少了 TCP 連接的重複建立和斷開所造成的額外開銷,減輕了服務器端的負載:
HTTP/1.1 採用了長連接的方式,這使得管道(Pipeline)網絡傳輸成爲了可能。即可在同一個 TCP 連接裏面,客戶端可以發起多個請求,只要第一個請求發出去了,不必等其回來,就可以發第二個請求出去,可以減少整體的響應時間。
但是服務器還是按照順序,先回應第一個請求,完成後再回應第二個請求,以此類推。要是前面的請求迴應得特別慢,後面就會有許多請求阻塞着,這就是所謂的【隊頭阻塞】。
所以 HTTP/1.0 或是 HTTP/1.1 性能都不是很完美,所以後續會有其他加強。
2. 安全問題
HTTP的內容是明文傳輸的,明文數據會經過中間代理服務器、路由器、WIFI熱點、通信服務運行商等多個物理節點,如果信息在傳輸過程中被劫持,傳輸的內容久完全暴露了,劫持者還可以篡改傳輸的信息且不被雙方察覺,這就是中間人攻擊。
總結一下,HTTP在安全方面有以下三個問題:
- 使用明文通信,一些重要的內容會被竊聽;
- 不能驗證對方身份,可能是僞造的信息;
- 無法驗證報文的完整性,有可能被修改;
三、HTTPS的實現
針對上面我們提到的HTTP的安全問題,HTTPS 在 HTTP 的基礎上增加了加密處理、認證機制和完整性保護,我們可以將 HTTPS = HTTP + 加密 + 認證 + 完整性保護;
1. 加密
因爲 HTTP 使用明文傳輸,中間會經過多個物理節點,可能會被劫持竊聽,針對這一問題,HTTPS 採用了加密的方式解決。最容易理解的就是對稱加密。
1.1 對稱加密
對稱加密好理解,就是我們擁有一個密鑰,它可以用來對一段內容進行加密,同樣的,在內容被進行加密後,需要用同一個密鑰對加密內容進行解密,才能看到原本的內容,可以看作我們日常生活中的鑰匙。
HTTP 可以直接使用對稱加密嗎?
當然不可以。如果通信雙方各自持有同一個密鑰,且沒有第三方知曉,那麼這兩方之間的通信安全是可以被保證的(畢竟密鑰被破解可能性不大)。問題是”如何使得這個密鑰可以讓傳輸的雙方知曉,同時不被別人知道“?
假如我們現在瀏覽器生成一個密鑰然後發送到服務端,告訴服務端我們雙方用這個密鑰來加密傳輸文件。或者是放過來,由服務器生成密鑰然後發送給瀏覽器。很明顯這就不現實,我們知道 HTTP 傳輸時中間是需要經過許多箇中間節點的,在經過中間節點時這個密鑰被劫持下來是一件十分容易的事,所以這種方式不可取。由此引入非對稱加密。
1.2 非對稱加密
非對稱加密有兩把密鑰,通常一把叫做公鑰,另外一把叫做私鑰。用公鑰加密的內容必須用私鑰才能解開,同樣的,私鑰加密的內容需要用公鑰才能解開。
HTTP 可以直接使用非對稱加密嗎?
還是不可以。鑑於非對稱加密的性質,我們可能會有這種思路:服務器先把公鑰直接明文傳輸給瀏覽器,之後瀏覽器向服務器傳數據前都先用這個公鑰加密好再傳輸,這條數據似乎可以保障了,因爲只有服務器端的相應私鑰能解開這條數據。但是這樣還是有問題,密鑰還是可以被劫持的。
如果服務器用它的的私鑰加密數據傳給瀏覽器,那麼瀏覽器用公鑰可以解密它,而這個公鑰是一開始通過明文傳輸給瀏覽器的,如果這個公鑰被誰劫持到的話,他也能用該公鑰解密服務器傳來的信息了。所以這種方式的實現還是會有問題,似乎只能保證由瀏覽器傳輸數據時的安全性(其實還有漏洞)。
1.3 改良版非對稱加密
通過一組公鑰、私鑰已經能保證單個方向傳輸的安全性,那用兩組公鑰私鑰是不是就能保證雙向傳輸都安全了,以下面流程爲例:
- 某網站擁有用於非對稱加密的公鑰 A、私鑰 A‘,瀏覽器擁有用於非對稱加密的公鑰 B、私鑰 B’ ;
- 瀏覽器向網站服務器發起請求,服務器把公鑰 A 明文傳輸給瀏覽器;
- 瀏覽器將公鑰 B 明文傳輸給服務器;
- 之後瀏覽器向服務器傳輸的所有東西都用公鑰 A 加密,服務器收到後用私鑰 A’ 解密。由於只有服務器擁有這個私鑰 A’ 可以解密,所以能保證這條數據的安全;
- 服務器向瀏覽器傳輸的所有東西都用公鑰 B 加密,瀏覽器收到後用私鑰 B’ 解密。同上也可以保證這條數據的安全。
這種實現方式理論上確實可行,拋開這裏面仍有的漏洞不談(下文再述),HTTPS 的加密卻沒有使用這種方案,爲什麼?
最主要的原因是非對稱加密算法非常耗時,特別是加密解密一些較大數據的時候有些力不從心。相比之下,對稱加密就要快很多,那能不能同時運用對稱加密與非對稱加密的性質來實現對 HTTP 的加密呢?
1.4 混合加密
既然非對稱加密耗時,那麼就用“對稱加密 + 非對稱加密”結合的形式來實現對 HTTP 的加密,而且還得儘量減少非堆成加密的次數 ,這樣是否能實現呢?
這種方式是可以實現的,而且非對稱加密、解密各只需要用一次即可。請看以下過程:
- 某網站擁有非對稱加密的公鑰 A、私鑰 A‘ ;
- 瀏覽器向網站服務器發起請求,服務器把公鑰 A 明文傳輸給瀏覽器;
- 瀏覽器隨機生成一個用以對稱加密的密鑰 X,用公鑰 A 加密後傳給服務器;
- 服務器端拿到加密的密鑰後,用公鑰 A 解密得到密鑰 X;
- 這樣雙方就都擁有密鑰 X 了,且別人無法知道它,之後雙方所有數據都用密鑰 X 進行加密解密。
HTTPS 基本上就是採用了這種方案了,當然這種方法還是有漏洞,我們接着往下講。
2. 認證
2.1 中間人攻擊
根據上面的混合加密過程,中間人確實無法擁有瀏覽器生成的對稱密鑰 X,這個密鑰本身就被公鑰 A 給加密了,只有服務器才能通過私鑰 A‘ 對其進行解密。然而在這個過程中中間人完全不需要獲取到密鑰 A’ 就能進行攻擊了。如下流程所示:
- 某網站擁有用於非對稱加密的公鑰A、私鑰A’ ;
- 瀏覽器向網站服務器發起請求,服務器把公鑰 A 明文給傳輸瀏覽器;
- 中間人劫持到公鑰A,保存下來,把數據包中的公鑰A替換成自己僞造的公鑰B(它當然也擁有公鑰B對應的私鑰B’);
- 瀏覽器隨機生成一個用於對稱加密的密鑰X,用公鑰B(瀏覽器不知道公鑰被替換了)加密後傳給服務器;
- 中間人劫持後用私鑰 B’ 解密得到密鑰 X,再用公鑰 A 將 X 加密後傳給服務器;
- 服務器拿到後用私鑰A’解密得到密鑰X。
這樣在雙方都不會發生異常的情況下,中間人得到了密鑰 X,這其中的根本原因就是瀏覽器無法確認自己收到的公鑰是不是網站的。那麼接下來就是要解決這一問題。
2.2 數字證書
如何證明瀏覽器收到的公鑰一定是該網站的公鑰?這裏就需要有一個公信機構給網站頒發一個“身份證”了。網站在使用 HTTPS 前,需要向“CA機構”申請頒發一份數字證書,數字證書裏有證書持有者、證書持有者的公鑰等信息,服務器把證書傳輸給瀏覽器,瀏覽器從證書裏取公鑰就行了,證書就如同身份證一樣,可以證明“該公鑰對應該網站”。
然而到這裏還是有一個問題,如何保證證書在傳輸的過程不會被篡改,身份證本身有防僞的技術,那麼如何保證證書的防僞呢?
2.3 數字簽名
如何保證證書不被篡改?
我們把證書內容生成一份“簽名”,比對證書內容和簽名是否一致就能察覺是否被修改,這種技術就稱爲數字簽名;
數字簽名的製作過程?
- CA 擁有非對稱加密的私鑰和公鑰;
- CA 對證書明文信息進行 Hash;
- 對 Hash 後的值用私鑰加密,得到數字簽名S;
將明文和數字簽名共同組成數字證書,這樣一份證書就可以頒發給網站了。
瀏覽器得到證書後如何驗證這份證書的真實性?
- 拿到服務器發送過來的證書,得到明文T,數字簽名S;
- 用CA機構的公鑰對 S 解密(由於是瀏覽器信任的機構,所以瀏覽器保有CA的公鑰),得到S‘;
- 瀏覽器用證書說明的 Hash 算法對明文 T 進行 Hash 得到 T’;
- 比較 S‘ 是否等於 T’,等於則代表證書可信。
瀏覽器如何得到權威機構的公鑰?
上面提到,如何要對服務器發過來的證書進行解密,那麼就需要到CA的公鑰,因爲其被CA的私鑰給加密了。那麼瀏覽器是如何擁有CA的公鑰呢?
實際上權威機構的公鑰並不需要傳輸,因爲權威機構會和主流的瀏覽器或操作系統合作,將他們的公鑰內置在瀏覽器或操作系統環境中。客戶端收到證書之後,只需要從證書中找到權威機構的信息,並從本地環境中找到權威機構的公鑰,就能正確解密A公鑰。當然實際情況要比這個複雜得多,這裏簡單介紹就行。
中間人有可能篡改證書嗎?
上面我們提到,權威機構的公鑰是可能在瀏覽器或操作系統中的,那麼中間人劫持到證書後是可以解密得到原文的。相應的,他也可以去篡改證書的原文,但是由於他沒有 CA 機構的私鑰,無法相應地篡改簽名。所以瀏覽器收到證書後會發現原文和解密後的值不一致,說明證書已經被篡改,證書不可信了,所以中間人不可能去篡改證書了。
四、HTTPS的請求流程
- 客戶端向服務器發起 HTTPS 請求,連接到服務器的 443 端口;
- 服務器端有一個密鑰對,即公鑰和私鑰,是用來進行非對稱加密使用的,服務器端保存着私鑰,不能將其泄露,公鑰可以發送給任何人;
- 服務器將自己的公鑰包含在權威機構發佈的證書中發送給客戶端;
- 客戶端收到服務器端的證書之後,會對證書進行檢查,驗證其合法性,如果發現發現證書有問題,那麼HTTPS傳輸就無法繼續。嚴格的說,這裏應該是驗證服務器發送的數字證書的合法性,關於客戶端如何驗證數字證書的合法性。如果公鑰合格,那麼客戶端會生成一個隨機值,這個隨機值就是用於進行對稱加密的密鑰,我們將該密鑰稱之爲
client key
,即客戶端密鑰,這樣在概念上和服務器端的密鑰容易進行區分。然後用服務器的公鑰對客戶端密鑰進行非對稱加密,這樣客戶端密鑰就變成密文了,至此,HTTPS中的第一次HTTP請求結束; - 客戶端會發起 HTTPS 中的第二個 HTTP 請求,將被公鑰所加密之後的客戶端密鑰發送給服務器;
- 服務器接收到客戶端發來的密文之後,會用自己的私鑰對其進行非對稱解密,解密之後的明文就是客戶端密鑰,然後用客戶端密鑰對數據進行對稱加密,這樣數據就變成了密文。
- 然後服務器用對稱加密的密鑰(即客戶端密鑰)對報文進行加密,並將加密後的報文發送給客戶端;
- 客戶端收到服務器發送來的密文,用客戶端密鑰對其進行對稱解密,得到服務器發送的數據。這樣 HTTPS 中的第二個 HTTP 請求結束,整個 HTTPS 傳輸完成。
文章內容絕大數來源網絡,我只是個搬運工,若有哪裏出錯,請評論區指出。
參考資料: