# Session-based Authentification vs Token-based Authentification ###### tags: `NodeJS` ### 什麼是認證 (Authentification)? **認證 (authentication)**:指的是當前用戶的身份,當用戶登陸過後系統便能追蹤到他的身份做出符合相應業務邏輯的操作。即使用戶沒有登錄,大多數系統也會追蹤他的身份,只是當做來賓或者匿名用戶來處理。認證技術解決的是 「**我是誰?**」的問題。 ### 什麼是授權 (authorization)? **授權 (authorization)**:指的是什麼樣的身份被允許訪問某些資源,在獲取到用戶身份後繼續檢查用戶的權限。單一的系統授權往往是伴隨認證來完成的,但是在開放 API 的多系統結構下,授權可以由不同的系統來完成,例如 OAuth。授權技術是解決「**我能做什麼?**」的問題。 ### 小明雜貨店的故事... 1. 因為認臉障礙,無法讓消費者可以快速買到想買的物品! ![](https://i.imgur.com/gyI5pZu.png) 2. 小紙條的力量! ![](https://i.imgur.com/JzaPMnf.png) 小明最大的問題就是他自己沒辦法記憶「**狀態**」,因此需要倚靠一個機制來幫他管理「**狀態**」,而這個機制我們就叫做 **Session**。 原本對小明來說,每一個客人都是新的客人,彼此之間毫無關聯,所以也沒有任何狀態可言。但有了紙條以後,兩個在小明眼中完全不同的客人被關聯了起來,小明就可以知道:「原來這個新的客人是以前那個來買木材的客人!」 所以 Session 是什麼?就是一種讓 Request 變成 stateful 的機制。以小明的例子來說,Session 就是一種讓客人之間能互相關聯起來的機制。 3. 到底誰會隨身攜帶紙條? ![](https://i.imgur.com/GxmhHh9.png) 「不會有人隨身攜帶紙條,但總會隨身攜帶手機吧!」 Cookie 是什麼?Cookie 就是故事裡面存在手機的資訊。 4. 咖啡寄杯的煩惱 一切看似非常順利,直到小明月底對帳的時候。 > 不對啊,為什麼買咖啡的數量只有 55 杯,賣出去的卻有 66 杯? ![](https://i.imgur.com/NRMZlHt.png) ### 最後的結果 ![](https://i.imgur.com/etTM3sZ.png) ### Session 就是一種讓 Request 變成 stateful 的機制。以小明的例子來說,Session 就是一種讓客人之間能互相關聯起來的機制。在故事裡面我們用了紙條跟手機裡的資訊來比喻,有多種方式可以達成 Session。 ### Cookie 在網路世界中,也有很多種方式可以來實作 Session,前面介紹過第一種是網址列,第二種就是靠 Cookie,而 Cookie 就是存在瀏覽器裡的一些資訊。常見的錯誤認知是一定要有 Cookie 才能實作 Session,這是錯誤的。 有了 Session 之後,會碰到資料被竄改的問題,這時候有兩種解決方式: 1. 「Cookie-based session」:意思是你照舊把狀態存在 Cookie,但是加密以後再存;(不建議使用,Cookie有大小上限制、增加網路流量、被解碼就完蛋!) 2. 「用 Cookie 來實作 Session 機制」:另一個方法是把狀態存在 Server 端,靠一個 SessionID 來辨識,這個狀態你可以存成檔案,可以存在記憶體裡,也可以存在資料庫,只是實作方式的不同而已,但原理都是一樣的。 ### 回想 Java / Servlet 如何做認證? ![](https://i.imgur.com/D2drobU.png) ![](https://i.imgur.com/nUVfz1r.png) 每個 HttpSession 各有特殊的Session ID,當瀏覽器請求應用程式時,會將Cookie中存放的Session ID一併發送給應用程式,Web 容器根據 Session ID 來找出對應的 HttpSession 物件,如此就可以取得各瀏覽器個別的會話資料。 ### 過濾器 Filter Filter其實是一個Design pattern,同時他也實現了AOP(Aspect Oriented Programming)的概念。假設今天我們有20個Servlet都只有登入過的才能夠觀看,那麼這20頁Servlet都有一個共同的Cross-cutting Concern,那就是需要先驗證此request是否已經有登入過,如果有,顯示內容。沒有,轉向登入頁面。 因此,Filter就如同他的名字一般,是一個過濾器,只有通過的才能過,沒通過的,抱歉請去另外一邊。 ### 以上介紹的都是 Session-based Authentification ![](https://i.imgur.com/HKkDjUg.png) Session-Based Authentication,顧名思義就是利用 Session 來進行驗證的機制,整體流程如上圖所示。客戶端先將帳號密碼丟給伺服器端,伺服器端透過帳密驗證,確定是正確的使用者登入,就開始建立一個伺服器端與你之間連線的 ==Session(會期)資料==,裡面會放置一些用來辨識這個 Session 是被哪個使用者建立的資料,接著會回傳該 Session 的 ID 回來,並要求客戶端未來要驗證時需要帶這個 Session 的 ID 在 ==HTTP Cookie== 上。 這個 ==HTTP Cookie== 是位於 HTTP request 的 header 中的一個小區塊,它會**自動**被存在客戶端(通常是瀏覽器)上,並且 Cookie 會記載該資料是來自於哪個網址的資料,在瀏覽器對該網址為基底的網址做 HTTP request 的時候,都會自動帶上這個 Cookie ,藉以讓伺服器能夠驗證身份,並透過伺服器端儲存的 Session 資料,去回傳相對應身份可以得到的資料,這一整個就是基本的 Session-Based Authentication 架構。 ### Token-based Authenticaiton ![](https://i.imgur.com/9J5Pj9m.png) Token-Based Authentication,顧名思義就是利用 Token 來進行驗證的機制,整體流程如上圖所示。基本上流程和 Session-Based Authentication 類似,但是**伺服器端不需要另外儲存任何資料**,而是把相關的資料透過==密鑰簽署==組合成 ==Token==,交由客戶端保管。客戶端拿到 Token 後,只要在每次 HTTP request 發送的時候,在 header 中的 Authorization 區塊帶上 Token 給伺服器端,即可被伺服器端再次透過密鑰**驗證是否 Token 沒有被修改過**,進而從中抽取資料,了解是哪個使用者的請求,給予相對應身份可以得到的資料。 在 Token-Based Authentication 中,目前最常見的方式是用 ==JWT(JSON Web Token)== 去做為 ==Token== 的格式,JWT 會將 Token 分成三段,分別是 **Header**、**Payload** 和 **Signature**,而其中 Payload 就是上述所說到會放置能夠辨識使用者資料所儲存的地方。另外要注意的是,JWT 所使用的 Token 只會使用密鑰進行簽署的動作,簽署完後形成的就是 Signature,伺服器在收到 Token 時**僅使用密鑰去進行 Signature 驗證,確定該 Token 是否有被修改過,並不會對 Token 進行加密**。如果有要對 Token 進行加密的話,可以選擇 JWE(JSON Web Encryption)的加密方式。 ### JSON Web Token(JWT) 它更符合設計 RESTful API 時 **「Stateless 無狀態」** 原則:意味著每一次從客戶端向伺服器端發出的請求都是獨立的,使用者經驗證後,在伺服器端不會將用戶驗證狀態透過 Session 儲存起來,因此每次客戶端發出的請求都將帶有伺服器端需要的所有資訊 — 從客戶端發出給伺服器端的請求將帶有 JWT 字串表明身份 。 #### JWT 組成 JWT 是一組字串,透過(.)切分成三個為 Base64 編碼的部分: **Header**:含 Token 的種類及產生簽章(signature)要使用的雜湊演算法 **Payload**:帶有欲存放的資訊(例如用戶資訊) **Signature**:編譯後的 Header、Payload 與密鑰透過雜湊演算法所產生 ``` // base64(Header) + base64(Payload) + base64(Signature) // xxxxx.yyyyy.zzzzz ``` #### Header Header 是一個包含定義 Token 種類(type)及雜湊演算法(alg)資訊的 JSON。在此設定 Token 種類為 **JWT**、產生簽章(signature)要使用的雜湊演算法為 **HS256**。此 JSON 將被轉換成 Base64 編碼,成為第一個部分: ``` { "alg": "HS256", "typ": "JWT" } ``` #### Payload Payload 也是一個 JSON,使用者和相關的資訊都可以放置其中。通常會使用 exp 設定 **Token 到期的時間**、iat 設定 **Token 簽發時間**。最後再被轉換成 Base64 編碼,成為第二個部分: ``` { "_id": "<user_id>", "name": "Mike", "exp": 1300819380 } ``` **❗️不要將隱私資訊存放在 Payload 當中** #### Signature 簽章(Signature )是將被轉換成 Base64 編碼的 **Header**、**Payload** 與**自己定義的密鑰**,透過在 Header 設定的雜湊演算法方式所產生的。 ==由於密鑰並非公開==,因此伺服器端在拿到 Token 後,能透過解碼,==確認資料內容正確,且未被變更==,以驗證對方身份。 ### 優缺點比較 兩種不同的 Authentication 皆有一些不同的優缺點,底下就列舉幾點讓大家了解一下: 1. Session-Based Authentication 由於是利用 Cookie 傳遞資料,Cookie 只要是在同網域下的請求皆會帶上,故只要別人用它的網站去連結你 API 的網址(例如:刪除題目的 API 網址),就可以讓你不知不覺對你的網站進行資料庫操作的動作,這個攻擊稱作 **CSRF(Cross Site Request Forgery**)。最簡單的防止方式通常是將 ==SameSite== 加註在 Cookie 上,讓瀏覽器僅會在發送的請求是與該網頁同網域的 ==URI== 的時候,才會帶上 Cookie 在請求中。關於詳細的攻擊內容以及防止方式可見此[文章](https://blog.techbridge.cc/2017/02/25/csrf-introduction/)。 2. Token-Based Authentication 的 Token 通常必須要由客戶端自行決定如何儲存。由於最常見的客戶端通常為瀏覽器,所以通常是使用 JavaScript 去進行儲存。那如果在你的網頁上可以被別人加上 JavaScript 程式碼的話,就可以盜取別人的 Token,又由於伺服器端完全信任 Token 的關係,幾乎完全無法防止別人使用該 Token 去當作你的身份,用此身份來進行伺服器操作,這種攻擊通常稱為 **XSS(Cross-site scripting)**。最常見的防止方式是將 Token 一樣使用 Cookie 去帶,並且對該 Cookie 使用 ==HttpOnly== 和 ==Secure Flag== 讓 Cookie 內容不能被 JavaScript 讀取。詳細關於 XSS 的內容可見 [Wiki](https://zh.wikipedia.org/wiki/%E8%B7%A8%E7%B6%B2%E7%AB%99%E6%8C%87%E4%BB%A4%E7%A2%BC)。 3. 如果你所處的網路環境正在被監聽的話,不管是 Session-Based Authentication 或是 Token-Based Authentication 都有可能因為被監聽而 Cookie 或是 Token 被其他人偷走,進而導致帳號被盜,這種攻擊一般稱作 **MitM(Man-in-the-Middle Attack)**。通常的解決方式就是讓客戶端與伺服器端連線之間透過加密資料進行溝通,最常見的加密方式是使用 ==SSL(Secure Sockets Layer)加密技術==,而溝通的 protocol 就會從 http 改成後面加上 SSL 第一個字的 ==https==。詳細關於 MitM 的部分可以參見 [Wiki](https://en.wikipedia.org/wiki/Man-in-the-middle_attack)。 4. 以 RESTful API 的角度看,我們曾經提到它有個原則叫做 ==Stateless==,就是只要我送給伺服器的參數相同,應該就會得到一樣的結果,伺服器並沒有記錄任何狀態下來,去影響客戶端送出的 Request 要回應的內容。以這個特點來看,Token-Based Authentication 就比較符合這個原則,而 Session-Based Authentication 就會比較違反這個原則。 5. Session-Based Authentication 由於在每次驗證建立後,都必須使用空間來儲存辨識使用者的資料;而 Token-Based Authentication 則是將這些資料交給客戶端儲存。如果以伺服器可以服務的用戶數量的量級來比較的話,就是 Token-Based Authentication 的方式能夠服務的用戶數量會高很多。但相對地,就會有第三點所提到的問題,伺服器端無法主動終止該 Token 的驗證,必須等到 Token 所含的過期時間到了才能被終止,所以通常的做法就是讓 Token 的過期時間很短,以防止 Token 被偷,那這樣對使用者的影響就是常常會需要重新輸入帳號密碼去做登入,就比較不適合用於需要長時間驗證的服務上。 ### 參考資料 **HTTP Session 攻擊與防護** https://devco.re/blog/2014/06/03/http-session-protection/ Servlet HttpSession 原理 https://openhome.cc/Gossip/ServletJSP/BehindHttpSession.html 白話 Session 與 Cookie:從經營雜貨店開始 https://hulitw.medium.com/session-and-cookie-15e47ed838bc 淺談 Session 與 Cookie:一起來讀 RFC https://github.com/aszx87410/blog/issues/45 Cookie 和 Session 究竟是什麼? https://tw.alphacamp.co/blog/cookie-session-difference 細說API – 認證、授權和憑證 https://kknews.cc/zh-tw/code/8pbqkol.html crpto-js AES 加密 https://www.jianshu.com/p/a47477e8126a 對稱式加密演算法 https://ithelp.ithome.com.tw/articles/10249488 [筆記] 透過 JWT 實作驗證機制 https://medium.com/%E9%BA%A5%E5%85%8B%E7%9A%84%E5%8D%8A%E8%B7%AF%E5%87%BA%E5%AE%B6%E7%AD%86%E8%A8%98/%E7%AD%86%E8%A8%98-%E9%80%8F%E9%81%8E-jwt-%E5%AF%A6%E4%BD%9C%E9%A9%97%E8%AD%89%E6%A9%9F%E5%88%B6-2e64d72594f8 Session vs Token Based Authentication https://sherryhsu.medium.com/session-vs-token-based-authentication-11a6c5ac45e4 Day 10:資料管理伺服器 (8) - 驗證機制介紹與會員系統建置 https://ithelp.ithome.com.tw/articles/10235315 聽說不能用明文存密碼,那到底該怎麼存? https://medium.com/starbugs/how-to-store-password-in-database-sefely-6b20f48def92 網站安全🔒 再探同源政策,談 SameSite 設定對 Cookie 的影響與注意事項 https://medium.com/%E7%A8%8B%E5%BC%8F%E7%8C%BF%E5%90%83%E9%A6%99%E8%95%89/%E5%86%8D%E6%8E%A2%E5%90%8C%E6%BA%90%E6%94%BF%E7%AD%96-%E8%AB%87-samesite-%E8%A8%AD%E5%AE%9A%E5%B0%8D-cookie-%E7%9A%84%E5%BD%B1%E9%9F%BF%E8%88%87%E6%B3%A8%E6%84%8F%E4%BA%8B%E9%A0%85-6195d10d4441