Skip to main content

Command Palette

Search for a command to run...

Lesson 9: 守門員的識別證:驗證 - Session vs JWT

Updated
3 min read
B

一個很忙的工程師,比起寫code更喜歡股票,QQQ買起來,謝謝!

在Lesson 7的時候有提到 「HTTP是無狀態性的」,那大家都有豐富的網站使用經驗,如果每次請求都是無狀態,那我們大家如何分辨自己的訂單?自己的購物車呢?

所以其實我們會有 認證身分的方式,所以這篇文章就來介紹:Session vs JWT。

Session

Session 是一種歷史悠久且穩定的身分識別方式,雖然我們叫Session,但其實多半也會需要依靠Cookie的幫忙,但其實Cookie是可以且容易被竄改的,因此實際上還會有其餘的資安防護手法。

Session像是健身房的置物櫃鑰匙。鑰匙本身沒價值,價值在於它能打開 Server 端對應的那個櫃子。

運作模式

我們將使用者資訊在Server上生成Session id 後讓前端寫到Cookie之中,之後的請求瀏覽器會自動把符合條件的Cookie 塞到Request後請求給後端Server,這樣一來我們就可以透過解析cookie中的session_id方式來讓無狀態的Http 可以包含客製化的條件或資訊。

  • 建立會話:當使用者登入成功後,Server 會在記憶體 (或資料庫/Redis) 中建立一筆資料,包含使用者的 ID、權限等資訊。

  • 發放識別證:Server 生成一個唯一的 session_id,並透過 HTTP Response Header 的 Set-Cookie 指令,將這個 ID 寫入使用者的瀏覽器 Cookie 中。

  • 自動攜帶:瀏覽器之後對該 Domain 的每一次請求,都會自動在 Header 帶上這個 Cookie (session_id)。

  • 驗證身分:Server 收到請求後,解析 Cookie 中的 session_id,去 Server 端的儲存空間查找對應的資料。如果找得到,就代表驗證通過。

優點

  • 控制權在 Server 手上:如果你想踢掉某個使用者(例如:強制登出),Server 只要把手上的 Session 資料刪除,使用者的 session_id 瞬間就會失效。

  • 資料隱密性高:真正敏感的資訊(如使用者權限、個資)都存在 Server 端,Client 端只拿得到一串沒有意義的亂碼 ID。

缺點

  • Server 負擔較重:每個在線上的使用者都需要佔用 Server 的記憶體或儲存空間。

  • 擴展性挑戰:當你的網站流量變大,需要增加 Server (Load Balancer) 時,如果 Session 存在 Server A 的記憶體,使用者的下一個請求被導流到 Server B,Server B 會因為找不到 Session 而判定使用者未登入。

    • 解決方案:通常需要搭配 Redis 來集中管理 Session。
  • CSRF 風險:因為瀏覽器會「自動」攜帶 Cookie,這導致了 CSRF (跨站請求偽造) 的攻擊風險(即使 JWT 存在 Cookie,一樣會有 CSRF)

既然 Session ID 通常是存在 Cookie 裡的,那如果這個 Cookie 被駭客偷走,駭客就能偽裝成你的身分(Session Hijacking)。因此,瀏覽器提供了多個 Cookie 屬性 (Attributes) 來限制 Cookie 的存取範圍與傳輸方式,這就是我們保護 Session ID 的第一道防線。

當後端發送 Set-Cookie Header 時,通常會長這樣: Set-Cookie: session_id=xyz123; HttpOnly; Secure; SameSite=Lax; Path=/; Domain=example.com

  1. HttpOnly (防禦 XSS 的關鍵)
  • 意義:設定了這個屬性的 Cookie,無法透過 JavaScript (如 document.cookie) 讀取。

  • 為什麼重要:這是防禦 XSS (跨站腳本攻擊) 最重要的設定。假設駭客在你的網站留言版植入了一段惡意 JS 代碼來竊取 Cookie,如果 Session ID 有開啟 HttpOnly,駭客的程式碼就讀不到該 Cookie,有效降低帳號被盜用的風險。

  • 結論:敏感資訊 (如 Session ID) 務必開啟此選項。

  1. Secure (只許加密傳輸)
  • 意義:設定此屬性後,Cookie 只能透過 HTTPS 協定傳輸。如果是普通的 HTTP (明文) 請求,瀏覽器就不會帶上這個 Cookie。

  • 為什麼重要:防止攻擊者在網路節點中進行監聽 (Man-in-the-Middle attack) 攔截到明文的 Cookie。

  • 結論:在正式環境 (Production) 中,必須開啟。

  1. Domain & Path (限制作用範圍)
  • Domain:指定 Cookie 在哪個網域下有效。

  • Path:指定 URL 路徑下有效。

    • 通常設定為 / (根目錄),代表整站都通用。

    • 若設定 /admin,則只有在進入 /admin 開頭的網址時,瀏覽器才會帶上此 Cookie。

⚠️ 注意:Domain 不能隨便設! 設定 Cookie 的 Domain 屬性時,你只能設定「當前網域」或「當前網域的父網域」。 例如 api.example.com 可以設 Domain=.example.com,但不能設 Domain=google.com。 如果你的前後端網域完全不同 (例如前後端分離部署在不同平台),建議改用 JWT,會比硬要設定 Cookie 來的省事許多。

  1. SameSite (防禦 CSRF 的新星)
  • 意義:限制 Cookie 是否可以隨著「跨站請求」發送。這是現代瀏覽器防禦 CSRF (跨站請求偽造) 的重要機制。

  • 三種模式:

    • Strict (嚴格模式):只有「第一方 context」(網址列是原本的網站) 才會發送 Cookie。如果你從 Facebook 點擊連結連回你的網站,因為來源不同,Cookie 不會被發送 (導致使用者看起來像沒登入)。安全性最高,但使用者體驗較差。

    • Lax (寬鬆模式/預設值):允許部分安全的跨站請求 (如 GET 導航) 攜帶 Cookie。例如從外部點連結進入你的網站,可以保持登入狀態;但如果是外部表單 POST 你的 API,Cookie 就不會帶上。這是目前大多數瀏覽器的預設值。

    • None (無限制):無論同站或跨站都會發送 Cookie。但現代瀏覽器強制要求設定 SameSite=None 時,必須同時設定 Secure,否則會被瀏覽器阻擋。

這裡說明比較容易混淆的 Domain+Path / SameSite

在SameSite的出現非常有效的緩解了CSRF的攻擊。

  • Domain + Path:決定 Cookie 「能不能送去哪裡」

  • SameSite:決定 Cookie 「能不能從別人家發出去」

至於為什麼 SameSite + Domain + Path 可以有效降低CSRF攻擊,我們會在之後的章節提到。

關於JWT

JWT 的全稱是:JSON Web Token。隨著前後端分離 (SPA) 與微服務架構的普及,JWT 成為常見的身份驗證機制之一。

JWT 的核心概念是「狀態儲存在 Client 端,Server 僅負責驗證簽名」。Server 不再像 Session 模式那樣儲存登入狀態,而是把資訊打包並「簽名」後發給 Client。

需要強調的是:JWT 本身不是安全機制,也不一定比 Session 更安全。它只是另一種身份驗證模式。

JWT 很像護照。護照上寫了你的資料,而海關(Server)只需檢查它是否被篡改(簽名驗證)以及是否過期,不需要再向後端查詢你是誰。


JWT 的資料結構

JWT 由三個部分組成:

  1. Header(演算法,例如:HS256)

  2. Payload(資料內容,例如:user_id、nick_name…)

  3. Signature(簽名,將 Header + Payload + 密鑰 (Secret) 雜湊後得出)

JWT 最終長得像這樣:

aaaaa.bbbbb.ccccc


運作模式

  • 登入與簽發:使用者登入成功後,Server 會依使用者資訊製作 JWT 並回傳給 Client。

  • 客戶端儲存:前端自行決定將 JWT 存放於 LocalStorage、SessionStorage,或 Cookie。

  • Token 攜帶方式

    • 若 Token 可被 JS 讀取,前端會放在 Authorization: Bearer <token> Header。

    • 若存於 HttpOnly Cookie,則改由 Cookie 自動附帶,前端無法自行塞入 Header。

  • 驗證簽名:Server 收到 Token 後,以相同算法與密鑰計算 Signature,若結果一致且 Token 未過期,即視為合法。


JWT 存放位置的取捨

JWT 常見的儲存方式主要有兩種:

  • 前端可自由存取 JWT

  • 前端可手動塞入 Authorization Header

  • 開發彈性高、最常用於 SPA 與行動 App

  • 缺點:高度暴露於 XSS 風險

  • 前端讀不到 Token → 不能放進 Authorization Header

  • 攜帶完全依賴瀏覽器 Cookie(像 Session)

  • 安全性較高(防止 JS 盜取)

  • 需額外搭配 CSRF 防禦機制

注意:

一旦選擇 HttpOnly Cookie,

JWT 就無法再透過 Authorization: Bearer 傳遞

因為前端根本無法讀取 Token。


優點

  • 無狀態 (Stateless)

    Server 不需要儲存登入狀態。Token 中已包含所有必要資訊,只要每台 Server 使用相同的密鑰即可驗證。非常適合 Horizontal Scaling。

  • 跨平台、跨網域彈性高(當 Token 可由前端讀取時)

    若 Token 存於 LocalStorage / SessionStorage / 可讀 Cookie,前端可手動加到 Authorization Header。

    適用於 SPA、App、跨網域 API 等場景。

    (若採 HttpOnly Cookie,此優點即不適用。)


缺點

  • 無法被撤銷 (Hard to Invalidate)

    Token 一旦發出,在過期前都有效,Server 無法主動使其失效。

    解決方式:

    • 黑名單 (Blacklist)

    • 超短效 Access Token + Refresh Token

  • 頻寬負擔較高

    JWT 包含多段資訊,字串比 session_id 大很多。

    存在 Cookie 或每次放 Header 都會增加傳輸成本。

  • Payload 可被解碼(非加密)

    Base64URL 編碼任何人都能解開,因此不可放密碼、個資、敏感欄位。

  • 若 Token 存在 LocalStorage / 可讀 Cookie,會受到 XSS 威脅

    XSS 一旦發生,攻擊者能直接取得 Token。

    安全需求高的情境應改用 HttpOnly Cookie,但這會使前端無法再用 Authorization Header。

  • 若 Token 存在 HttpOnly Cookie,將承受 CSRF 風險

    因為 Cookie 會自動夾帶,仍需 SameSite、CSRF Token 等機制。

    並且會失去 JWT 作為純 API Token 的靈活性。

比較整理

#

從Rookie到Junior,一個後端成長的30堂課

Part 20 of 31

“從 Rookie 到 Junior:一個後端成長的 30 堂課” 是一套專為後端新手所設計的成長型技術系列文章。內容以實務為導向,逐步拆解後端工程的核心能力,包括程式語言基礎、架構思維、框架運作原理、業務邏輯設計、資料庫操作、以及常見的開發模式。 本系列的目標是協助讀者從零散的學習堆疊,建立成體系的後端知識框架。讀者能夠理解各語言背後不變的工程思維與設計原則。這套內容旨在讓學習者從「會寫程式」進入「能理解系統設計」的階段,逐步具備勝任 Junior Backend Engineer 的能力。

Up next

Lesson 8:API 設計哲學-RESTful API 的資源 概念與動詞運用

RESTful API 的核心哲學 REST(Representational State Transfer)是一種架構風格,它不是一個標準,而是一組設計原則。RESTful API 的設計哲學主要圍繞在兩個核心概念:資源(Resources) 和 狀態轉移(State Transfer)。 REST 提倡以 資源(Resource)為中心,透過 HTTP 的既有語意(HTTP verbs)定義操作方式,而不是把 API 設計成模糊的 endpoints。 這和傳統 action-style 的...

More from this blog

Lesson 26 : 系統韌性的守護者-限流、熔斷與背壓的設計模式

當這幾個名詞出現後,代表我們進到了一個高併發/大流量的系統了。在這個章節中,我們一起來看看如何透過一些方式來避免高併發導致我們的系統crash掉。 限流(Rate Limiting) 相信大家對這個名詞並不陌生,限流其實就是字面上的含意,限制流量。 限流的目的是保護「接收方」,確保系統不會因為瞬間的高併發請求而癱瘓。 限流通常發生在 API Gateway 或服務的最前端。它像是一個夜店門口的保全

Mar 26, 20262 min read

Lesson 25: 淺談 單體架構、微服務架構與單/多租戶架構

過去我們討論了 要把程式寫在哪、程式要怎麼拆的題目,接下來我們來看看「如何服務不同客戶」,這些架構反映了軟體開發在擴充性與複雜度之間的權衡。 軟體架構深度解析:從系統拆分到商業規模化 在軟體工程的演進中,架構的選擇往往是在「開發效率」、「系統擴充性」與「營運成本」之間尋求平衡。我們可以從兩個核心維度來觀察這些架構:系統如何運行(單體 vs. 分散式) 以及 如何服務客戶(單租戶 vs. 多租戶)。

Mar 26, 20262 min read

Lesson 24: 資料庫擴展術-讀寫分離、複寫機制與快取一致性挑戰

為什麼要讀寫分離? 大多數的 Web 應用都是 「讀多寫少」(例如:看文的人多,發文的人少,Heavy Read System)。當所有的請求都塞給同一台資料庫時,磁碟 I/O 和連線數會成為瓶頸。 Master (主庫): 負責寫入 (Insert/Update/Delete),確保數據一致性。 Slave (從庫): 負責讀取 (Select),可以有多個從庫來分擔讀取壓力。 為什麼讀

Mar 25, 20262 min read

面試經驗談 2025-2026

從2025年3月開始,我陸陸續續參與了從新創到上市櫃公司的Senior - Tech Lead的相關面試,其中有不乏 尊重面試者、展現高度專業的企業(公司),當然也有遇到幾場面試鬼故事,這篇文章主要分享我對於軟體工程師面試的方向分享,以及部分鬼故事,以此警惕自己不要成為這樣的面試官。 AI的洪流,改變了SWE的生態 LLM的發展確確實實的影響到了軟體工程師的生態。 過去受限於算力與資料規模,深度學

Mar 23, 20262 min read

Lesson 23: 系統的緩衝區-Queue 佇列與非同步處理 (Asynchronous)

佇列 佇列的實作工具非常多,舉凡AWS SQS、RabbitMQ、Kafka…等。 佇列的特性,其實是一個非常強大的系統緩衝區,應用層面非常廣。 什麼是佇列? 佇列可以想像成,在既有流程中外,有另一個”水管”,來連接原有的資料流(或邏輯過程),其中 呼叫方將資料 推(Push)到水管中,接受方(監聽) 從水管中將資料拉(Pull)出處理 為什麼佇列是「強大的緩衝區」? 在同步處理中,系統像是一

Mar 23, 20262 min read

Bennett's Tech Blog | 後端架構、系統設計

32 posts

來自台灣的軟體工程師,相信軟體可以改變世界