引言
這是在我閱讀了肖佳老師的書《HTTP 封包擷取實戰》後對 HTTP 相關內容的總結,主要還是訊息相關 (記錄:閱讀此書與寫出此文章耗時 5 天)
HTTP 協定訊息的結構
HTTP 訊息分為 2 個:一個是 HTTP 請求訊息 (Request),一個是 HTTP 回應訊息 (Response)
HTTP 請求訊息 (Request)
HTTP 請求訊息分為 3 個部分,第一部分叫起始行 (Request line),第二部分叫標頭 (Request Header),第三部分叫主體 (Body)
第一行中有 Method (請求方法)、URI 和協定版本。例如
GET https://blog.yexca.net/ HTTP/2第二部分是 Header (標頭)
第三部分是 Body (主體)
注意:Header 標頭和 Body 主體之間有一個空行
HTTP 回應訊息 (Response)
HTTP 回應訊息與請求訊息的結構基本一樣,也分為 3 個部分,第一部分叫回應行 (Response line),第二部分叫回應標頭 (Response Header),第三部分是主體 (Body)
第一行有協定版本、狀態碼和狀態碼訊息。例如 HTTP/2 200
第二部分是 Header (標頭)
第三部分是 Body (主體)
注意:Header 標頭和 Body 主體之間有一個空行
HTTP 協定請求方法和狀態碼
URL 格式
URL 的全稱為 Uniform Resource Locator,中文譯名為統一資源定位符,用於完整地描述 Internet 上某一處資源的位址
URL 的基本格式如下:
schema://host[:port#]/path/.../[?query-string][#anchor]
| 屬性 | 描述 |
|---|---|
| schema (協定) | 指定底層使用的協定 (例如:http,https,ftp) |
| host (網域) | HTTP 伺服器的 IP 位址或者網域名稱 |
| port# (連接埠) | HTTP 伺服器的預設連接埠是 80,這種情況下連接埠號可以省略。如果使用了別的連接埠,則必須指明 |
| path (資源的路徑) | 存取資源的路徑 |
| query-string (參數) | 發送給 HTTP 伺服器的資料 |
| anchor (錨點) | 錨點,頁面內部超連結 |
HTTP 請求方法
| 編號 | 方法 | 描述 |
|---|---|---|
| 1 | GET | 請求指定的頁面資訊並傳回實體主體 |
| 2 | HEAD | 類似於 GET 請求,只不過傳回的回應中沒有具體的內容,用於獲取標頭 |
| 3 | POST | 向指定資源提交資料進行處理請求 (例如提交表單或者上傳檔案),資料被包含在請求體中。POST 請求可能會導致新的資源建立和/或對現有資源的修改 |
| 4 | PUT | 從用戶端向伺服器傳送的資料取代指定文件的內容 |
| 5 | DELETE | 請求伺服器刪除指定的頁面 |
GET 與 POST 的區別
GET 提交的資料會放在 URL 之後,以
?分隔 URL 和傳輸資料 (即 query-string,鍵值對方式),參數之間以&相連而 POST 方法是把提交的資料放在 HTTP 封包的 Body 中
GET 提交的資料大小有限制 (因為瀏覽器對 URL 的長度有限制)
而 POST 方法提交的資料大小沒有限制
GET 方式需要使用 Request.QueryString 來取得變數的值
而 POST 方法透過 Request.Form 來獲取變數的值
HTTP 狀態碼
HTTP 狀態碼存在於 HTTP 的回應訊息中,其作用是 Web 伺服器用來告訴用戶端發生了什麼事
HTTP 狀態碼被分為 5 大類,隨著協定的發展,HTTP 規範中會定義更多的狀態碼吧
| 狀態碼 | 已定義範圍 | 分類 |
|---|---|---|
| 1XX | 100~101 | 資訊提示,表示請求已被成功接收,繼續處理 |
| 2XX | 200~206 | 成功,表示請求已被成功接收、理解、接受 |
| 3XX | 300~305 | 重新導向,要完成請求,必須進行進一步處理 |
| 4XX | 400~415 | 用戶端錯誤,請求有語法錯誤或請求無法實現 |
| 5XX | 500~505 | 伺服器錯誤,伺服器未能實現合法的請求 |
常見狀態碼
| 名稱 | 釋義 |
|---|---|
| 200 | OK:伺服器成功處理了請求 |
| 301/302 | Moved Permanently (重新導向):請求的 URL 已移走。Response 中應該包含一個 Location URL,說明資源現在所處的位置 |
| 304 | Not Modified (未修改):用戶端的快取資源是最新的,需要用戶端使用快取 |
| 404 | Not Found:未找到資源 |
| 401 | 禁止存取 |
| 501 | Internal Server Error:伺服器遇到一個錯誤,使其無法對請求提供服務 |
206 (Partial Content,部分內容)
206 狀態碼代表伺服器已經成功處理了部分 GET 請求 (只有發送 GET 方法的 HTTP 請求,Web 伺服器才可能傳回 206)
應用比如說使用下載工具實現斷點續傳或者線上影片播放都是使用 206 狀態碼來實現
例如現在打開影片網站的一個影片,對於影片所在的 URL
瀏覽器會發送一個 GET 請求,Header 中包含 Range: bytes=5303296-5336063,意思就是請求得到 5303296-5336063 之間的資料
Web 伺服器傳回一個 206 的 HTTP 回應。Header 中包含 Content-Range: bytes 5303296-5336063/12129376,表明這次傳回的內容範圍
301 與 302 (Moved Permanently,重新導向)
在得到 301 或 302 回應後,瀏覽器會再次請求位於 Location 中傳回的新 URL
狀態碼 301 和 302 在語法上是一模一樣的,都是在 HTTP 回應的 Location 中傳回新的 URL
區別在於:
301 表示舊位址已經被永久移除了,這個資源不可存取了,搜尋引擎會把權重算到新位址
例如:防止使用者輸錯網域或更換網域
302 表示舊位址的資源還在,仍然可以存取,這個重新導向只是臨時地從舊位址跳轉到新位址,搜尋引擎會把權重算到新位址
例如:未登入狀態下存取需要登入才能存取的頁面
304 (Not Modified,未修改)
狀態碼 304 表示上次的文件已經被快取了,還可以繼續使用
400 (Bad Request)
狀態碼 400 表示用戶端請求有語法錯誤,發送的 HTTP 請求中的資料有錯誤,例如表單有錯誤或者 Cookie 有錯誤,不能被伺服器所理解
401 (Unauthorized)
狀態碼 401 是指未授權錯誤。有些網頁採用的是 HTTP 基本認證 (Basic Authentication) ,需要在 HTTP 請求 Header 中帶上 Authentication,否則伺服器會傳回狀態碼 401
404 (Not Found)
該狀態碼表明伺服器上無法找到請求的資源。除此之外,也可以在伺服器端拒絕請求但不想說明理由時使用
例如 BV1AB4y1D7Ft 這個影片僅在登入並且收藏的情況下才可見,否則將傳回 404
403 (Forbidden)
狀態碼 403 表示 Web 用戶端發送的請求被 Web 伺服器拒絕了。如果伺服器想說明為什麼拒絕請求,可以在 Body 中描述原因。但這個狀態碼通常表示伺服器不想說明拒絕原因
500 (Internal Server Error)
狀態碼 500 代表伺服器內部錯誤。出現錯誤的原因有很多,比如程式碼的錯誤、資料庫連接語句出錯、程式內部拋出異常、空指標錯誤等
503 (Server Unavailable)
狀態碼 503 表示伺服器暫時不可用。由於伺服器維護或者過載,伺服器目前無法處理請求
這個狀況是臨時的,並且將在一段時間以後恢復
了解全部狀態碼
存取: HTTP 狀態碼 - 菜鳥教程
HTTP 協定 Header
Header 的語法格式是「key: value」,一行一個。每一個 Header 都有特殊的作用
快取相關的 Header
HTTP 請求與 HTTP 回應都有很多用於快取的 Header。HTTP 快取是指當 Web 請求抵達快取時,如果本地有「已快取的」複本,就可以從本地儲存設備而不是原始伺服器中獲取該檔案
Cookie
Cookie 是一種 HTTP 快取,是 HTTP 中非常重要的內容。它由 key=value 的形式組成,比如 ip_country=CN
瀏覽器把 Cookie 透過 HTTP 請求中的「Cookie: header」發送給 Web 伺服器,Web 伺服器透過 HTTP 回應中的「Set-Cookie: header」把 Cookie 發送給瀏覽器
Accept
Accept 表示瀏覽器用戶端可以接受的媒體類型。例如 Accept: text/html 代表可以接受伺服器傳回 html
萬用字元 * 代表任意類型,例如 Accept: text/html,*/*;q=0.8 代表瀏覽器可以處理所有的類型。一般瀏覽器用戶端給 Web 伺服器發送的都是類似這個
Accept-Encoding
Accept-Encoding 跟壓縮有關,瀏覽器發送 HTTP 請求告訴 Web 伺服器瀏覽器支援的壓縮形式,例如 Accept-Encoding: gzip, deflate
Accept-Language
Accept-Language 作用是聲明自己接受的語言。注意語言與字元集的區別,中文是語言,中文有多種字元集,例如 GB2312、GBK 等。例如 Accept-Language: en-US,en;q=0.8,zh-CN;q=0.6,zh;q=0.4,zh-TW;q=0.2
User-Agent
User-Agent 的作用是瀏覽器用來告訴伺服器,用戶端使用的作業系統及版本、CPU 類型、瀏覽器及版本、瀏覽器轉譯引擎、瀏覽器語言、瀏覽器外掛程式等
例如 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:103.0) Gecko/20100101 Firefox/103.0 代表 64 位元 Linux 系統,Firefox 是 103.0 版本
如果想要模擬各種不同的用戶端,只要修改 User-Agent,就可以偽裝成各種用戶端
Referer
Referer 主要用來讓伺服器判斷來源頁面,即使用者是從哪個頁面來的,網站通常用其統計使用者來源,也可以用作防盜連等
Connection
從 HTTP/1.1 起,系統預設都開啟了 Connection: Keep-Alive,保持連線特性。Keep-Alive 不會永久保持連線,它有一個保持時間,可以在不同的伺服器軟體 (如 Apache) 中設定這個時間
Host
Host 的作用是指定被請求的主機和連接埠號,如果是 80 連接埠號會被自動隱藏
HTTP 協定中的快取
快取無處不在,有瀏覽器端的快取、伺服器端的快取、代理伺服器的快取,還有 ASP.NET 頁面的快取、物件快取、資料庫快取等等
HTTP 中具有快取功能的是瀏覽器快取和代理伺服器快取
HTTP 快取是指當 Web 請求抵達快取時,如果本地有「已快取的」複本,就可以從本地儲存設備而不是從原始伺服器中提取這個文件
快取的優點:減少了冗餘的資料傳輸,節省了傳輸時間;減少了伺服器的負擔,大大提高了網站的效能;加快了用戶端載入網頁的速度等
如何判斷快取新鮮度
Web 伺服器透過以下兩種方式來判斷瀏覽器快取是否最新
瀏覽器把快取檔案的最後修改時間透過 Header 的 If-Modified-Since 告訴 Web 伺服器。瀏覽器收到 HTTP 請求後,在 Header 中將檔案最後修改時間 Last-Modified 與請求訊息的 If-Modified-Since 相比較。若相同則說明檔案是最新的,則發送狀態碼 304 (Not Modified) 給瀏覽器用戶端;若不同則發送狀態碼 200 把最新檔案發送給瀏覽器用戶端
瀏覽器把快取檔案的 ETag 透過 Header 的 If-None-Match 告訴 Web 伺服器
與快取有關的 Header
- HTTP 請求訊息 Header
| 名稱 | 釋義 |
|---|---|
| Cache-Control: max-age=0 | 以秒為單位 |
| If-Modified-Since: Tue, 28 Jun 2022 00:50:56 GMT | 快取檔案的最後修改時間 |
| If-None-Match: “1771e0c387823da5329c20a76bece83c” | 快取檔案的 ETag 值 |
| Cache-Control: no-cache | 不使用快取 |
| Pragma: no-cache | 不使用快取 |
- HTTP 回應訊息 Header
| 名稱 | 釋義 |
|---|---|
| Cache-Control: public | 回應被快取,並且可以被多用戶存取使用 |
| Cache-Control: private | 回應只能作為私有快取,特定使用者使用 |
| Cache-Control: no-cache | 提醒瀏覽器要從伺服器提取文件進行驗證 |
| Cache-Control: no-store | 絕對禁止快取 (用於機密、敏感檔案) |
| Cache-Control: max-age=60 | 60s 後快取過期 (相對時間) |
| Date: Thu, 01 Sep 2022 21:56:36 GMT | 目前回應發送的時間 |
| Expires: Thu, 01 Sep 2022 21:57:37 GMT | 快取過期的時間 (絕對時間) |
| Last-Modified: Tue, 28 Jun 2022 00:50:56 GMT | 伺服端檔案的最後修改時間 |
| Etag: “1771e0c387823da5329c20a76bece83c” | 伺服器檔案的 ETag 值 |
註:瀏覽器總是優先使用 cache-control,如果沒有時才考慮 Expires
ETag
ETag 是 Entity Tag (實體標籤) 的縮寫,是根據實體內容生成的一段雜湊 (hash) 字串 (類似於 MD5 或者 SHA1 之後的結果),可以表示檔案的狀態。當資源發生改變時,ETag 也隨之發生改變
使用 ETag 主要是為了解決一些 Last-Modified 無法解決的問題,比如說某些伺服器不能精確得到檔案的最後修改時間、一些檔案最後修改時間改變了但是內容不變、某些檔案修改特別頻繁甚至達到了以秒為單位以下等
註:Last-Modified 只能精確到秒
瀏覽器不使用快取
使用 Ctrl+Shift+R 快捷鍵強制重新整理瀏覽器,可以讓瀏覽器不使用快取,即瀏覽器的 HTTP 請求訊息的 Header 中帶有 Cache-Control: no-cache,明確告訴 Web 伺服器不使用快取
註:Pragma: no-cache 與 Cache-Control: no-cache 作用相同,只是 Pragma: no-cache 是 HTTP/1.0 定義的,保留為了相容性
直接使用快取,不經過伺服器驗證
使用 Ctrl+R 快捷鍵重新整理瀏覽器,瀏覽器會去 Web 伺服器驗證快取
如果在網址列直接輸入位址並存取,瀏覽器會「直接使用有效的快取」,不會發送 HTTP 請求去伺服器驗證快取,這種情況稱為快取命中 (cache hit)
公有快取與私有快取
公有快取 Cache-Control: public 可以由多個使用者共享存取,而私有快取 Cache-Control: private 只能單個使用者存取使用
HTTP 協定壓縮和 URL Encode
HTTP 壓縮是指 Web 伺服器和瀏覽器之間壓縮傳輸文字內容的方法。HTTP 採用通用的壓縮演算法,比如用 gzip 來壓縮 HTML、JavaScript、CSS 檔案
HTTP 內容編碼與壓縮的區別
在 HTTP 協定中,可以對 Body 部分進行編碼,如可以採用 gzip 這樣的編碼,從而達到壓縮的目的;也可以使用其他編碼方式把內容攪亂或加密,以此來防止未被授權的第三方看到文件的內容。所以 HTTP 壓縮其實就是 HTTP 內容編碼的一種
HTTP 壓縮的過程
瀏覽器發送 HTTP 請求 Header 中帶 Accept-Encoding: gzip,deflate 告訴伺服器瀏覽器支援 gzip 壓縮
Web 伺服器接到 HTTP 請求後,先生成原始的 HTTP 回應,包含原始的 Content-Type 與 Content-Length;然後透過 gzip 對 HTTP 回應的 Body 進行編碼,並在編碼後 Header 中的 Content-Type 與 Content-Length 替換為壓縮後的大小,以及加上編碼方式 Content-Encoding: gzip;再把 HTTP 回應發送給瀏覽器
瀏覽器接到 HTTP 回應後,根據 Content-Encoding: gzip 來對 HTTP 回應進行解碼,獲取到原始 HTTP 回應後顯示出網頁
註:HTTP 請求也是可以編碼的,但是瀏覽器一般不會對 HTTP 請求編碼
內容編碼類型
HTTP 定義了一些標準的內容編碼類型,並允許用擴展的形式添加更多的編碼
在 Header 中的 Content-Encoding 就是使用這些標準化的代號來說明編碼時使用的演算法
| 編碼 | 描述 |
|---|---|
| gzip | 表明實體採用 GNU zip 編碼 |
| compress | 表明實體採用 UNIX 的檔案壓縮程式 |
| deflate | 表明實體是用 zlib 的格式壓縮的 |
| identity | 表明沒有對實體進行編碼。當 Header 中沒有 Content-Encoding 時,預設為此情況 |
gzip、compress 以及 deflate 編碼都是無損壓縮演算法,用於減少傳輸訊息的大小,不會導致資訊缺失。其中 gzip 通常效率最高,使用最為廣泛
深入理解 Cookie 機制
HTTP 協定是無狀態的,對於瀏覽器的每一次請求,伺服器都會獨立處理,不與之前或之後的請求發生關聯。即使是同一個瀏覽器發送了 3 個請求,伺服器也會獨立處理這 3 個請求,伺服器並不知道這 3 個請求是來自同一個瀏覽器
工作階段機制與 Cookie 機制
伺服器需要識別瀏覽器請求,就必須弄清楚瀏覽器的請求狀態。既然 HTTP 協定是無狀態的,那就讓伺服器和瀏覽器共同維護一個狀態,這就是工作階段 (Session) 機制
瀏覽器第一次請求伺服器時,伺服器建立一個工作階段,並將工作階段 ID (Session ID) 作為回應的一部分發送給瀏覽器
瀏覽器儲存工作階段 ID,並在後續請求中帶上工作階段 ID
伺服器取得請求中的工作階段 ID 就知道是不是同一個使用者了
這樣後續請求與第一次請求就產生了關聯,而 Cookie 機制就是一種工作階段機制
伺服器在記憶體中保存工作階段物件,瀏覽器可以使用 Cookie 機制保存工作階段 ID
Cookie 是什麼
Cookie 是瀏覽器用來儲存少量資料的一種機制,資料以 key=value 形式儲存,多個 Cookie 之間以分號 ; 分隔,瀏覽器發送 HTTP 請求時自動附帶 Cookie 資訊
Cookie 最主要的作用是用來做使用者認證,還可以用於保存使用者的一些其他資訊。也可以用於網際網路精準廣告定向技術,例如使用者瀏覽了某些商品,就可以用 Cookie 記錄下來,然後進行大數據深度分析,實現廣告精準投放
鑒於此項,目前歐洲的一些國家已經對 Cookie 立法,並規定必須經過使用者的允許才可以保存使用者的 Cookie
Cookie 的屬性
根據網站不同, Cookie 有所不同
- Expires
表示 Cookie 失效的時間,如果不指定則在關閉瀏覽器或頁面時被瀏覽器刪除
- Path
表示 Cookie 所屬的路徑,asp.net 預設為 / 也就是根目錄
假設在同一個伺服器上的目錄如下:/test/、/test/cd/、/test/dd,Cookie1 的 Path 在 /test/,Cookie2 的在 /test/cd/,那麼 /test/ 下的所有頁面都可以存取到 Cookie1,而 /test/dd/ 的子頁面不能存取 Cookie2。因為 Cookie 只能讓其 Path 路徑下的頁面存取
- HttpOnly
這是個關乎安全方面的屬性,將一個 Cookie 設定為 HttpOnly 後,透過 JavaScript 腳本將無法讀取到 Cookie 資訊,這能有效防止用 XSS 發起攻擊
一般來說,跟登入相關的 Cookie 必須設定為 HttpOnly
Cookie 的分類與位置
| 類別 | 描述 |
|---|---|
| 工作階段 Cookie | 臨時的 Cookie,記錄了使用者存取站點時的設定與偏好 (例如存取本站時的 Cookie),關閉瀏覽器後將被刪除 |
| 持久 Cookie | 儲存在硬碟上,有過期時間。不管退出瀏覽器還是重啟電腦都存在 |
網站的自動登入就是儲存持久 Cookie,在使用者再次存取相同網站時會先在硬碟中查找相關 Cookie 然後放到 HTTP 請求訊息中發送給伺服器
那麼持久 Cookie 存在電腦哪裡呢?不同瀏覽器會在各自的獨立空間存放 Cookie,互不干擾
例如 Linux 下 Firefox 的 Cookie 位置:~/.mozilla/firefox/xxxxxxxx.default-release/cookies.sqlite
HTTP 基本認證
HTTP 協定是無狀態的,瀏覽器和 Web 伺服器之間可以透過 Cookie 來識別身分。那麼一些桌面應用程式是如何跟 Web 伺服器之間識別身分呢?
一些網站和 Web 服務使用的是 HTTP 基本認證。有些桌面應用程式也透過 HTTP 協定跟 Web 伺服器互動,桌面應用程式一般不使用 Cookie,而是把使用者名稱+冒號+密碼用 Base64 編碼放在 HTTP 請求 Header 中的 Authorization 發送給服務端,這種方式叫 HTTP 基本認證 (Basic Authentication)
在基本認證中,Web 伺服器可以拒絕一個事務,要求用戶端提供有效的使用者名稱和密碼,伺服器會傳回 401 狀態碼來初始化認證質詢,並用 WWW-Authenticate 回應首部指定要存取的安全域。瀏覽器收到質詢時,會打開一個對話框請求使用者輸入使用者名稱和密碼,然後將使用者名稱和密碼用 Base64 編碼,再用 Authorization 請求首部發送給伺服器
一般家用路由器就是使用基本認證,
RESTful API
就經常使用基本認證,使用命令 curl -u username:password URI 即可完成基本認證
HTTP 基本認證的缺點
HTTP 協定是無狀態的,同一個用戶端對伺服器的每個請求都需要認證
Base64 編碼是可逆的,非常容易破解,所以基本認證相當於以明文的方式傳輸使用者名稱和密碼。所以基本認證一定要用 HTTPS 加密傳輸,稍微安全一點
使用基本認證登入後,除非關閉瀏覽器或清除歷史記錄,否則無法登出。而 Cookie 機制的話,網站可以提供登出方式以使 Cookie 失效
無法防止 重送攻擊
摘要認證
摘要認證是針對基本認證存在的諸多問題而進行改良的方案。摘要認證是另一種 HTTP 認證協定,它試圖修復基本認證的嚴重缺陷,進行如下改進
透過傳遞使用者名稱、密碼等計算出來的摘要來解決以明文方式在網路上發送密碼的問題
透過伺服器產生隨機數 nonce 的方式防止惡意使用者捕獲並重送認證的握手過程
透過用戶端產生隨機數 nonce 的方式支援用戶端對伺服器的認證
透過對內容也加入摘要計算的方式,可以有選擇地防止對訊息內容的篡改