在一開始定義範圍有提到,此系列文將會專注在討論 web 的身分驗證,因此我們第一步應該先了解 web 的基本協定--HTTP,它的規範與特性,後續才會知道,基於此協定的基礎上做身分驗證,需要考量些什麼。
HTTP 的全名為 Hypertext Transfer Protocol,在 OSI 模型裡,它屬於應用層的協定,可以透過 TCP 或 TLS 來發送或接收資訊。
參考 MDN 講 HTTP 的歷史,有分下列幾個版本:
引用: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 Framework 與 REST;甚至 2014 年時,IETF 還將 HTTP/1.1 整理過並重寫成六份 RFC,由此可見最開始設計上的穩定性與擴充性是非常高的。
這次的主題若沒有特別說明的話,都是在討論 HTTP/1.1。
即便現在 web 的生態如此多樣化,但 HTTP 基本特性是不變的。
一個常見的 HTTP 流程,是由客戶端(client)發起請求(request)開始。服務端(server)接收請求後再將回應(response)傳送給客戶端後結束。
@startuml
Alice -> Bob: Request
Bob --> Alice: Response
@enduml
基礎協定往往就是這麼樸實無華,且枯燥。
請求與回應的傳送方法與內容,RFC 都有清楚定義,比方說請求與回應都共同有 Header Fields 與 Message 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 來傳帳號密碼可以嗎?當然目的可以達成,只是會造成下面的問題:
相信這是一個容易理解的例子。因此 IETF 後來定義了狀態管理機制--RFC 6265 - HTTP State Management Mechanism 來讓無狀態的 HTTP 開始有了狀態的概念。是的,它也是基於 HTTP 上的擴充功能。
這個特性非常可怕,這代表在輸入信用卡帳號密碼時,如果有中間人監聽,則信用卡的資料就會外流出去。當然,這麼嚴重的問題,早早就有解法了--TLS,後面的文章會再簡單介紹。
HTTP/1.1 經過多年的考驗,且多數協定也都是基於此協定。因此短時間內,多了解 HTTP/1.1,不管是寫身分驗證,還是其他 web 服務,都是絕對有幫助的。