「登入之後,誰負責證明『我就是我』?又是誰負責記錄『我做了什麼』?」
當我們談認證(authentication)時,真正想要的其實是——可稽核的信任鏈。
我們從實務角度出發,帶你拆解 Laravel 11 裡 Sanctum 的兩種主流用法:SPA (Session/Cookie) 模式與 API Token 模式。除了流程、優缺點,我也把「實作後怎麼做到 accountability」的經驗放進來,讓你的系統不只「能用」,還能在出事時留下清楚的稽核線索。
SPA 模式(Session / Cookie-Based Authentication)
流程速寫
- 前端先打
GET /sanctum/csrf-cookie
- Laravel 會同時種下兩顆 cookie:
laravel_session
(HttpOnly,之後用來綁定使用者)與XSRF-TOKEN
(供 JS 讀取、寫入X-XSRF-TOKEN
header)
- Laravel 會同時種下兩顆 cookie:
- POST /login——送帳密 +
X-XSRF-TOKEN
- 認證成功後,
laravel_session
與使用者 ID 綁定完成。
- 認證成功後,
- 所有後續 API
- axios/fetch 必須
withCredentials: true
,瀏覽器才願意帶 cookie。 - Laravel 透過
auth:sanctum
middleware 自動驗證。
- axios/fetch 必須
優點
面向 | 說明 |
---|---|
XSS 風險低 | laravel_session 被 HttpOnly 保護,JS 永遠拿不到,攻擊面大幅縮小 |
內建 CSRF 保護 | 有 VerifyCsrfToken + XSRF-TOKEN ,前端只要先 call /sanctum/csrf-cookie 就安全上路 |
快速上手 | 不必自己塞 Authorization header,只要 cookie 正常帶就過關 |
層級更高的「責任追蹤」
- Session ID = 行為指紋 在
sessions
table 開agent
、ip_address
、last_activity
欄位,任何可疑行為都能順著 Session 追到裝置、IP、時間。 - 單裝置登出 如果安全政策要求「一支手機失竊要立即失效」,可以在登入後記錄
session()->getId()
,並在「登出其他裝置」功能裡呼叫Auth::logoutOtherDevices()
來清空同帳號其他 Session。
雷區
SANCTUM_STATEFUL_DOMAINS
漏填就 401- 記得把本機 (
localhost,127.0.0.1
) 和所有正式網域(含 www / 非 www)通通列進去。
- 記得把本機 (
- CORS 不能用 搭配 cookie
- 瀏覽器規範:帶憑證的請求若
Access-Control-Allow-Origin: *
會被擋下。必須明寫來源網域。
- 瀏覽器規範:帶憑證的請求若
- 每個 axios/fetch 都要
withCredentials: true
- 忘一次就少一次 cookie,Debug 會懷疑人生。
API Token 模式(Personal Access Token)
流程速寫
- 登入成功後產生 token
PHP
$token = $user->createToken('api')->plainTextToken;
- 前端儲存
plainTextToken
(localStorage / IndexedDB / Secure Storage) - 每次 API:
Authorization: Bearer <token>
- 後端: Sanctum 中介層依據資料庫
personal_access_tokens
驗證
優點
面向 | 說明 |
---|---|
完全 Stateless | 不吃 Session,適合微服務、Serverless、ECS 之類易於橫向擴充的場景 |
跨域 & App 友好 | 不倚賴瀏覽器 cookie,同一顆 token 放到 iOS、Android 甚至 Postman 皆可用 |
Token 可細分權限 | abilities(['read', 'write']) 讓單一帳號生成多顆能力各異的 token,利於最小權限 |
與「責任追蹤」的連結
- Token=可吊銷的身份證 建議在使用者後台做「查看 / 撤銷 token」列表,若發現異常流量,可立即 revoke。
- 加強到期策略
tokens->where('created_at', '<', now()->subDays(30))->delete();
週期性失效降低遺失風險。 - 集中式日誌 把
Request::user()?->id
、request()->bearerToken()
寫進 ELK 或 CloudWatch,可快速從 token 反查行為。
雷區
- XSS 風險
- Token 一旦被 JS 竊取就完全失守。前端必須 CSP + Escape + SRI 多層防護。
- 沒有 Laravel 內建 CSRF 機制
- 「Bearer token」本質上可抵禦 CSRF,但如果有同源表單仍要注意 double-submit 或 Referer header 驗證。
多模式共存,怎麼分工?
客戶端 | 建議模式 | 背後思路 |
---|---|---|
企業後台(同網域 SPA) | Session/Cookie | 風險集中在瀏覽器,採 HttpOnly 最安全 |
iOS / Android | API Token | 不受 cookie 限制,且 Stateless 易於佈署 |
第三方整合 | API Token 或 Passport | 需 Scope / OAuth2 flow 時改用 Passport |
Accountability Checklist — 提交前的最後自我檢查
- 每條受保護路由都上
auth:sanctum
- Session / Token 對應表有留存(方便出事時反查)
- 登入 / 登出 / 重要 API 寫入審計日誌(誰、何時、從哪裡)
- 定期清理過期 token 或僵屍 Session
- 備好「緊急撤銷」腳本:失竊帳號可一鍵停權所有 token / Session
- 將安全設定版本化:
config/cors.php
、sanctum.php
皆進 Git,PR 時才能被同儕審閱
選擇認證方案,其實就是在選擇一條「信任鏈」的管理方式。
- 若你要最低 XSS 風險、單一前端掌控,Session/Cookie 是穩健首選。
- 若你要跨平台、Stateless 彈性,API Token 讓擴充性最大。
- 若你要完整 OAuth2 與第三方授權,Passport 會是專業解法。
確認責任歸屬、留下可追溯的軌跡,再進行技術實現——這才是讓使用者真正感到安心的關鍵。