跳至主要內容
Skip to content

JWT 是什麼

JWT 很常被拿來和 Session 比較,但它們其實不是同一層東西。Session 是一種登入狀態管理方式;JWT 是一種 token 格式,用來以 JSON claims 的形式在不同系統之間傳遞可驗證的聲明。

一句話回答:JWT(JSON Web Token)是 RFC 7519 定義的一種 compact、URL-safe claims 表示格式,常見形式是 JWS:由 Header、Payload、Signature 三段 Base64URL 組成;Signature 可以證明 token 沒被竄改,但 Payload 預設只是編碼不是加密,所以不能放敏感資料。使用 JWT 時必須驗證簽章、演算法、issuer、audience、expiration、not before、subject、jti 等 claims,並處理短效、撤銷、金鑰輪換與儲存位置問題。


一、 必考觀念

1. JWT 是 token 格式,不是登入架構本身

JWT 可以出現在很多地方:

  • access token。
  • id token。
  • service-to-service token。
  • magic link token。
  • email verification token。
  • temporary authorization token。

但不是所有 token 都是 JWT。

例如:

text
opaque session id:8cF2...random...
JWT:xxxxx.yyyyy.zzzzz

Opaque token 沒有可讀 payload,server 要查資料庫或 introspection;JWT 則把 claims 放在 token 裡,接收方可以驗證簽章後讀取 claims。

2. JWT 常見格式是三段

text
header.payload.signature

例如:

text
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
.
eyJzdWIiOiJ1c2VyXzEyMyIsImV4cCI6MTcxNjU0MDAwMH0
.
signature

三段分別是:

區段內容
Headertoken 類型、簽章演算法、key id
Payloadclaims,例如 sub、exp、iss、aud
Signature對 header + payload 的簽章

3. JWT Payload 不是加密

JWT payload 只是 Base64URL encoded。

任何拿到 token 的人都可以 decode:

text
base64urlDecode(payload)

所以不要放:

  • 密碼。
  • 身分證字號。
  • 信用卡。
  • refresh token。
  • 私密個資。
  • 內部安全註記。

Signature 只能證明資料沒有被改,不代表資料不能被看。

如果需要保密,要評估 JWE;但多數登入 access token 場景用的是 JWS,也就是簽章,不是加密。


二、 JWT 結構

1. Header

範例:

json
{
  "alg": "RS256",
  "typ": "JWT",
  "kid": "key-2026-05"
}

常見欄位:

欄位說明
alg簽章演算法,例如 HS256、RS256、ES256
typtoken 類型,常見為 JWT
kidkey id,用於金鑰輪換

要注意:不能只相信 token header 裡的 alg。Server 必須有自己的允許演算法清單。

RFC 8725(JWT Best Current Practices)明確提醒實作時要避免弱簽章與演算法混淆,並且不要讓攻擊者控制驗證流程。

2. Payload

Payload 裡是 claims:

json
{
  "sub": "user_123",
  "iss": "https://auth.example.com",
  "aud": "api.example.com",
  "iat": 1716530000,
  "nbf": 1716530000,
  "exp": 1716533600,
  "jti": "token_abc"
}

常見 registered claims:

Claim意義
ississuer,誰簽發
subsubject,token 代表誰
audaudience,token 給誰用
expexpiration time,何時過期
nbfnot before,何時之前不能用
iatissued at,何時簽發
jtiJWT ID,token 唯一識別

RFC 7519 定義了這些 registered claims,但不是每個 claim 都強制存在。實務上應根據用途要求必要 claims。

3. Signature

Signature 用來防止 token 被竄改。

概念:

text
sign(
  base64url(header) + "." + base64url(payload),
  secretOrPrivateKey
)

驗證時:

text
重新計算 signature
-> 比對 token signature
-> 驗證 claims

重要的是:驗證簽章成功還不夠,claims 也要驗證。


三、 簽章演算法

1. HS256

HS256 是 HMAC + SHA-256,使用同一把 shared secret 簽發與驗證。

優點:

  • 簡單。
  • 效能好。
  • 適合單一信任邊界。

缺點:

  • 驗證方拿到 secret,就也能簽 token。
  • 多服務共享 secret 風險大。
  • secret 輪換要管理好。

適合小型系統或同一後端內部使用,但不適合把 secret 分發給很多服務。

2. RS256 / ES256

RS256 使用 private key 簽發、public key 驗證。

優點:

  • 驗證方只需要 public key。
  • auth server 保留 private key。
  • 適合多服務驗證。
  • 適合用 JWKS 做金鑰發布與輪換。

缺點:

  • 實作與金鑰管理較複雜。
  • 效能成本較高。

企業 SSO、OIDC、微服務常見 RS256 / ES256。

3. 演算法混淆風險

常見錯誤:

  • 接受 alg: none
  • 沒有限制可用演算法。
  • 把 RS256 public key 當 HS256 secret 使用。
  • 根據 token header 動態決定驗證方式。

安全做法:

typescript
verifyJwt(token, {
  algorithms: ['RS256'],
  issuer: 'https://auth.example.com',
  audience: 'api.example.com',
})

不要讓 token 自己決定你要怎麼驗證它。


四、 Claims 驗證

1. 只驗簽章不夠

錯誤思路:

text
signature valid -> user logged in

還要檢查:

檢查為什麼
exp避免過期 token 繼續使用
nbf避免 token 提前使用
iss確認由可信 issuer 簽發
aud確認 token 是給這個服務用
sub確認 subject 格式與狀態
jti支援追蹤與撤銷
scope / permissions授權檢查

2. exp 必須短

Access token 應該短效。

例如:

text
5 分鐘
15 分鐘
30 分鐘

實際時間取決於產品風險與 refresh 機制。

不要簽一個有效一年的 access token,然後把它當 session 用。JWT 的撤銷成本高,生命週期應該更謹慎。

3. audiss 很重要

iss 防止接受其他系統簽發的 token。

aud 防止 token 被拿到不該使用的服務。

例如:

text
aud = api.example.com

就不應該拿去呼叫:

text
billing.example.com

除非 billing 也明確接受這個 audience。

4. Clock skew

分散式系統時,server 時鐘可能有少量誤差。

可以允許很小的 leeway:

text
30 秒
60 秒

但不要給太大,否則 expnbf 會失去意義。


五、 JWT 和 Session 的差異

1. Session 是查狀態,JWT 是驗聲明

Session:

text
request 帶 session id
-> server 查 session store
-> 取得登入狀態

JWT:

text
request 帶 JWT
-> server 驗簽章
-> 讀 claims
-> 根據 claims 判斷
面向SessionJWT
狀態server-side stateful常用於 stateless access token
登出刪 session 即可需要短效、denylist 或 session check
撤銷容易較難
多服務驗證要查 session store驗 public key 即可
Token 大小通常很小claims 多會變大
權限更新查 session / DB 即時token 內權限可能過期

2. JWT 不等於無狀態萬靈丹

如果你需要:

  • 立即登出。
  • 管理裝置。
  • 撤銷單一 token。
  • 權限即時變更。
  • 偵測 session 風險。

那你往往又需要:

  • token denylist。
  • session id claim。
  • refresh token store。
  • introspection。
  • jti revoke table。

這時系統就不再完全 stateless。

3. JWT 適合什麼?

JWT 適合:

  • OAuth / OIDC access token 或 id token。
  • 多服務之間傳遞可驗證 claims。
  • 短效 access token。
  • auth server 與 resource server 分離。
  • 需要 public key 驗證的場景。

不一定適合:

  • 普通單體 Web app 的長期登入 session。
  • 需要立即撤銷的高風險後台。
  • 把大量使用者資料塞進 token 的場景。

延伸讀:Session 與 Cookie 登入機制


六、 JWT 儲存在哪裡?

1. Authorization Header

常見:

http
Authorization: Bearer eyJ...

優點:

  • 不會自動附加到所有請求。
  • 比較不受 CSRF 影響。
  • 適合 mobile app、API client。

缺點:

  • Web 前端若存在 localStorage,容易被 XSS 讀走。
  • 需要前端自己附上 header。

也有人把 JWT 放 HttpOnly Cookie。

優點:

  • JS 不能直接讀 token。
  • 瀏覽器自動帶。

缺點:

  • 要處理 CSRF。
  • Cookie 大小有限制。
  • JWT 仍然可能被當作 session cookie 使用。

如果 JWT 放 Cookie,就要像 session cookie 一樣設計:

  • HttpOnly。
  • Secure。
  • SameSite。
  • CSRF 防護。
  • 合理 Path / Domain。

3. localStorage 的風險

localStorage 最大問題是 XSS。

如果攻擊者能執行 JS:

javascript
localStorage.getItem('accessToken')

就可能直接偷 token。

所以 Web app 中長期保存 token 在 localStorage 要非常謹慎。若不得不用,access token 要短效,並且整個前端要非常重視 XSS 防護。

OWASP JWT Cheat Sheet 也提醒 client-side token storage 要小心 XSS 與 token sidejacking 風險。


七、 撤銷與登出

1. JWT 最常被問:如何登出?

如果 access token 是完全 stateless,而且還沒過期,server 沒有資料可查,就很難立即讓它失效。

常見策略:

策略說明
短效 access token降低被盜後可用時間
refresh token 可撤銷登出時撤銷 refresh token
denylist把 jti 加入黑名單直到 exp
session id claimtoken 內帶 sessionId,server 查 session
passwordChangedAt拒絕早於改密碼時間的 token
token introspectionresource server 查 auth server

2. Access token 與 refresh token 分工

常見設計:

text
access token:短效 JWT,用來呼叫 API
refresh token:長效 opaque token,存在 server,可撤銷

登出時:

text
撤銷 refresh token
可選:撤銷 session
可選:denylist 目前 access token 的 jti

這樣即使 access token 還能用,也只能活很短時間。

下一篇會專門整理 Access Token 與 Refresh Token。

3. 權限變更問題

如果 JWT 裡放:

json
{
  "permissions": ["user:delete"]
}

使用者權限被移除後,舊 token 直到過期前仍可能帶著舊 permission。

解法:

  • access token 短效。
  • 高風險 API 即時查權限。
  • token 帶 permission version。
  • 權限變更時撤銷 session / refresh token。
  • 使用 introspection 或 server-side authorization。

不要把 token claims 當永遠最新的權限真相。


八、 常見安全錯誤

1. 不驗簽章

只 decode JWT:

typescript
const payload = decode(token)

這非常危險。Decode 只是解析,不代表 token 可信。

必須 verify:

typescript
const payload = verify(token, publicKey, {
  algorithms: ['RS256'],
  issuer: 'https://auth.example.com',
  audience: 'api.example.com',
})

2. 接受 alg: none

舊實作或錯誤設定可能接受未簽章 token。

安全做法:

  • 明確指定允許演算法。
  • 不接受 none。
  • 使用成熟 JWT library。
  • 測試惡意 token。

3. 把 JWT 當加密資料

不要在 payload 放:

json
{
  "password": "...",
  "creditCard": "...",
  "nationalId": "..."
}

JWT payload 可被讀取。

4. Secret 太弱

HS256 secret 如果太短或可猜,攻擊者可能暴力破解 secret 後自己簽 token。

使用:

  • 足夠長的隨機 secret。
  • Secret Manager / KMS。
  • 定期輪換。
  • 不提交到 Git。

5. 混用 token 類型

不要拿 id token 呼叫 API。

不要拿 email verification token 當 access token。

不要拿 refresh token 當 access token。

每種 token 都應該有:

  • 用途。
  • audience。
  • lifetime。
  • 驗證規則。
  • 儲存位置。

九、 追問題庫

Q1:JWT 是什麼?

JWT 是 JSON Web Token,是 RFC 7519 定義的一種 compact、URL-safe claims 表示格式。常見形式是 header、payload、signature 三段組成,用簽章保證 claims 沒被竄改。

Q2:JWT Payload 安全嗎?

Payload 預設只是 Base64URL 編碼,不是加密。任何拿到 token 的人都能 decode,所以不能放敏感資料。

Q3:JWT 和 Session 差在哪?

Session 是 server-side state,request 帶 session id 後 server 查 session store。JWT 則是 token 裡帶 claims,server 驗簽章後讀 claims。Session 容易撤銷;JWT 適合短效、多服務驗證,但撤銷較麻煩。

Q4:JWT 可以登出嗎?

可以,但要設計。常見做法是 access token 短效、refresh token 可撤銷,必要時用 jti denylist 或 session id claim。完全 stateless 的長效 JWT 很難即時登出。

沒有通用答案。Authorization header 避免 CSRF,但 Web 中若存在 localStorage 容易受 XSS 影響;HttpOnly Cookie 降低 JS 讀取風險,但要處理 CSRF。要看產品架構、XSS/CSRF 防護與 token 生命週期。

Q6:只驗 JWT signature 夠嗎?

不夠。還要驗 expnbfissaudsub、演算法、key、scope / permission 等 claims。


十、 實作題

題目:設計 JWT 驗證 middleware

需求:

  • 接受 Authorization: Bearer
  • 只允許 RS256。
  • 驗證 issuer、audience、expiration。
  • 不信任 token header 決定演算法。
  • 驗證 user 是否仍有效。
typescript
async function requireJwt(request: Request) {
  const token = getBearerToken(request.headers.authorization)

  if (!token) {
    throw new UnauthorizedError()
  }

  const payload = await jwtVerifier.verify(token, {
    algorithms: ['RS256'],
    issuer: 'https://auth.example.com',
    audience: 'api.example.com',
  })

  const user = await userRepository.findById(payload.sub)

  if (!user || user.status !== 'active') {
    throw new UnauthorizedError()
  }

  if (user.passwordChangedAt && payload.iat < toUnix(user.passwordChangedAt)) {
    throw new UnauthorizedError()
  }

  return {
    user,
    tokenId: payload.jti,
    scope: payload.scope,
  }
}

題目延伸:登出怎麼做?

typescript
async function logout(input: LogoutInput) {
  await refreshTokenRepository.revoke(input.refreshTokenId)

  if (input.accessTokenJti) {
    await tokenDenylist.add({
      jti: input.accessTokenJti,
      expiresAt: input.accessTokenExp,
      reason: 'logout',
    })
  }
}

重點是:JWT 登出通常不是只刪前端 token。Server 端要能撤銷 refresh token,必要時追蹤 access token 的 jti


十一、 資深視角

1. JWT 的價值在跨信任邊界傳遞 claims

JWT 很適合:

text
Auth Server 簽發
Resource Server 驗證
多服務不用每次查 Auth Server

但如果是單體 Web app,只是想讓使用者登入,server-side session 可能更簡單、更可控。

2. Stateless 不等於不用設計狀態

很多系統一開始說 JWT stateless,後來又加:

  • refresh token table。
  • denylist。
  • session id。
  • key rotation。
  • permission version。
  • risk state。

這些都是狀態。

所以面試時要能說出:JWT 減少了某些查詢,但不會讓登入生命週期消失。

3. Token 要按用途分開

成熟系統會區分:

Token用途
access token呼叫 API
refresh token換新 access token
id token告訴 client 使用者身份資訊
reset token重設密碼
verification token驗證 email / phone

不同 token 不應混用,也不應共用驗證規則。


總結

問題回答重點
JWT 是什麼compact、URL-safe claims token format
結構header.payload.signature
Payload可讀,不是加密,不能放敏感資料
Signature防竄改,不代表自動授權
Claims要驗 exp、iss、aud、sub、jti、scope
演算法明確限制 HS256 / RS256 / ES256,不信任 header
撤銷短效 access token + refresh token revoke + denylist
Session 差異Session 查 server state,JWT 驗 claims
儲存位置Header 避免 CSRF,Cookie 要防 CSRF,localStorage 怕 XSS

一句完整的面試回答可以是:

JWT 是 JSON Web Token,是 RFC 7519 定義的 compact、URL-safe claims 表示格式,常見是 header、payload、signature 三段。Header 描述演算法與 key id,Payload 放 claims,例如 sub、iss、aud、iat、nbf、exp、jti,Signature 用 secret 或 private key 對 header + payload 簽章,防止被竄改。但 JWT payload 只是 Base64URL 編碼,不是加密,所以不能放敏感資料。使用 JWT 不能只 decode,也不能只驗 signature,還要限制演算法、驗 issuer、audience、expiration、not before、subject、token id 和 scope。JWT 適合短效 access token、多服務驗證和 OIDC/OAuth 場景;但撤銷比 server-side session 麻煩,所以通常搭配短效 access token、可撤銷 refresh token、jti denylist 或 session id claim。對普通 Web app,Session Cookie 可能更簡單可控。

延伸閱讀