簡介 HTTP 協定

在一開始定義範圍有提到,此系列文將會專注在討論 web 的身分驗證,因此我們第一步應該先了解 web 的基本協定--HTTP,它的規範與特性,後續才會知道,基於此協定的基礎上做身分驗證,需要考量些什麼。

HTTP 的全名為 Hypertext Transfer Protocol,在 OSI 模型裡,它屬於應用層的協定,可以透過 TCP 或 TLS 來發送或接收資訊。

HTTP 的歷史

參考 MDN 講 HTTP 的歷史,有分下列幾個版本:

  1. HTTP/0.9,1991 年發布,一行指令就能發送請求,但只能使用 GET,以及格式只能回傳 HTML。
  2. HTTP/1.0,1996 年發布,但其實在這之前,各家瀏覽器與服務器都是各自做自己的,並沒有任何共識。這時發布的 1.0 就有點像是參考各家的實作,來重新定義 HTTP 如何操作。此 RFC 是 INFORMATIONAL 類型,IESG Note 也提到了此文件存在著一些隱憂,要盡早定出標準來取代它。
  3. HTTP/1.1,1997 年發布第一版 RFC 2068,1999 年發布第二版 RFC 2616 取代前一版。它在定義上保留了許多擴充空間(如 Header),讓建構在 HTTP/1.1 上的應用程式或其他基於 HTTP 的協定,更有發揮的空間。
  4. HTTP/2,Google 2012 年發表了新的傳輸協定 SPDY,雖然當時不是標準,但該開發團隊全程參與了 2015 年發布的 HTTP/2。此協定與 HTTP/1.1 高度相容,並為了減少網路延遲,定義了很多方法來提升網頁載入速度。
  5. HTTP/3,Google 2013 年發表了新的傳輸協定 QUIC 後,IETF 在 2018 年將它重新命名為 HTTP/3,打算把它當作是下一代傳輸協定的標準,並進入草稿階段。雖然它基於 UDP 協定,但目前看起來有部分會依循 HTTP/2 的定義,也有可能會是向下相容的協定。

引用:HTTP/1.0 IESG Note: “The IESG has concerns about this protocol, and expects this document to be replaced relatively soon by a standards track document.”

知道 HTTP 或協定的過去、現在與未來,以及對應的 RFC 文件為何,將有幫助了解使用協定的相容性和相對應的風險,如 OAuth 2.0 看文件應該能繼續沿用到 HTTP/2 上,而依賴 OAuth 2.0 的 OpenID Connect 也能安心升級到 HTTP/2。

目前 HTTP/1.1 仍然是廣泛使用的 HTTP 協定,許多規範抑或是風格,都是基於 HTTP/1.1 所建立的,如 OAuth 2.0 Authorization FrameworkREST;甚至 2014 年時,IETF 還將 HTTP/1.1 整理過並重寫成六份 RFC,由此可見最開始設計上的穩定性與擴充性是非常高的。

這次的主題若沒有特別說明的話,都是在討論 HTTP/1.1。

HTTP 的特性

即便現在 web 的生態如此多樣化,但 HTTP 基本特性是不變的。

Client-server 協定

一個常見的 HTTP 流程,是由客戶端(client)發起請求(request)開始。服務端(server)接收請求後再將回應(response)傳送給客戶端後結束。

@startuml
Alice -> Bob: Request
Bob --> Alice: Response
@enduml

基礎協定往往就是這麼樸實無華,且枯燥。

擴充功能

請求與回應的傳送方法與內容,RFC 都有清楚定義,比方說請求與回應都共同有 Header FieldsMessage Body;而只有請求有 Request Method,只有回應有 Response Status Codes。這些協定內容也都會反應在實作上,如 PHP 的 PSR-7

HTTP 只有定義出空間放東西,以及做一些預定義,如 Request Header Fields 有提到請求的表頭(header)有些名稱已經先被定義了。因為這東西的擴充定義非常自由,所以也有 RFC 額外定義其他名稱,如 RFC 7034 - HTTP Header Field X-Frame-Options

無狀態

無狀態(stateless),即一個 HTTP 請求,會與另一個 HTTP 請求是沒有上下文(context)關係的,對服務器來說,兩個一模一樣的請求是一視同仁的。舉一個簡單的情境:透過 HTTP 請求成功登入服務後,想使用服務。無狀態的特性會讓服務器無法知道「使用服務的請求」跟「剛剛已成功登入服務的請求」是同一個使用者。

登入狀態無法用可靠的方法維持的話,做身分驗證就會非常多問題。舉個簡單的例子,固定使用 Query 來傳帳號密碼可以嗎?當然目的可以達成,只是會造成下面的問題:

  1. Query 是明碼直接放在網址列上,這讓路人可以輕易知道如何打開使用者的銀行金庫
  2. Access log 會保存 query 資訊,這代表服務器的管理員有機會「不小心」看到使用者的密碼
  3. Query 放在網址列上,意味著散播使用者的密碼是件非常容易的事

相信這是一個容易理解的例子。因此 IETF 後來定義了狀態管理機制--RFC 6265 - HTTP State Management Mechanism 來讓無狀態的 HTTP 開始有了狀態的概念。是的,它也是基於 HTTP 上的擴充功能。

明文傳輸

這個特性非常可怕,這代表在輸入信用卡帳號密碼時,如果有中間人監聽,則信用卡的資料就會外流出去。當然,這麼嚴重的問題,早早就有解法了--TLS,後面的文章會再簡單介紹。

小結

HTTP/1.1 經過多年的考驗,且多數協定也都是基於此協定。因此短時間內,多了解 HTTP/1.1,不管是寫身分驗證,還是其他 web 服務,都是絕對有幫助的。

參考資料