身份驗證與 Session 管理¶
4pass 採用分層身份驗證架構:JWT Token 用於 Session 識別、Argon2id 用於密碼驗證、每個 Session 獨立的 CSRF Token、Cloudflare Turnstile 用於機器人防護,以及有序的中介層堆疊在每個請求上強制執行安全策略。
關鍵安全特性
JWT 令牌僅存在於 HttpOnly Cookie 中 — JavaScript 永遠不會看到、儲存或傳輸令牌,消除了基於 XSS 的令牌竊取。每個狀態變更請求都需要透過同步器令牌模式的逐工作階段 CSRF 令牌,以常數時間比較驗證。所有 Cookie 的 SameSite=strict 即使瀏覽器漏洞繞過 CSRF 也能阻止跨來源提交。
JWT Token 架構¶
JWT Token 永遠不會暴露給 JavaScript。Access Token 和 Refresh Token 都存放在 HttpOnly Cookie 中——前端不會儲存、讀取或手動傳送 Token。這完全消除了基於 XSS 的 Token 竊取風險。
Token 管理¶
| 屬性 | Access Token | Refresh Token |
|---|---|---|
| 有效期 | 30 分鐘 (ACCESS_TOKEN_EXPIRE_MINUTES) |
30 天 (REFRESH_TOKEN_EXPIRE_DAYS) |
| 儲存方式 | HttpOnly Cookie (access_token) |
HttpOnly Cookie (refresh_token) |
| 演算法 | HS256 | HS256 |
| SameSite | strict |
strict |
| Secure | true(僅限 HTTPS) |
true(僅限 HTTPS) |
| Domain | 當前網域 (COOKIE_DOMAIN) |
當前網域 |
Token 來源依序檢查:
- Cookie(主要)——
access_tokenCookie,供瀏覽器客戶端使用 - Bearer 標頭 ——
Authorization: Bearer <token>,供 API 客戶端及 Swagger UI 使用
Token 刷新流程:
靜默刷新 (Silent Refresh)
/auth/silent-refresh 端點不需要 CSRF 驗證,因為它依賴 SameSite=strict 的 Refresh Token Cookie 進行身份驗證——CSRF 攻擊者無法讓瀏覽器在跨來源請求中送出此 Cookie。
密碼安全¶
4pass 透過 pwdlib[argon2] 使用 Argon2id ——OWASP 為高價值帳戶推薦的密碼雜湊演算法。
| 屬性 | 值 |
|---|---|
| 演算法 | Argon2id(記憶體密集型 + 抗 GPU 攻擊) |
| 函式庫 | pwdlib 搭配 PasswordHash.recommended() 預設值 |
| 為什麼不用 bcrypt | bcrypt 的 72 位元組輸入限制和較低的記憶體成本使其在對抗 GPU/ASIC 攻擊時較弱 |
| 為什麼不用 scrypt | Argon2id 是 PHC(密碼雜湊競賽)的獲勝者,具有更強的側通道抗性 |
from pwdlib import PasswordHash
password_hash = PasswordHash.recommended()
hashed = password_hash.hash("user_password")
verified = password_hash.verify("user_password", hashed)
Session 管理¶
每次登入都會在 user_sessions 資料表中建立一筆新的 Session 記錄:
| 欄位 | 類型 | 用途 |
|---|---|---|
id |
UUID | 唯一 Session 識別碼 |
user_id |
FK → users | Session 擁有者 |
csrf_token |
String (32-byte) | 每個 Session 獨立的 CSRF Token,用於同步器模式 (Synchronizer Pattern) |
ip_address |
String | 登入時的客戶端 IP(透過可信代理鏈取得) |
user_agent |
String | 登入時的瀏覽器 User-Agent |
created_at |
Timestamp | Session 建立時間 |
is_active |
Boolean | 登出或撤銷時設為 false |
Session 生命週期:
- 建立: 成功登入後,在 Argon2id 驗證和 CAPTCHA 檢查之後
- 驗證: 每個已認證的請求——解碼 JWT、從資料庫載入 Session、檢查
is_active - 撤銷: 明確登出 (POST /auth/logout) 或管理員操作
- 過期: 每次請求時檢查訂閱到期狀態——已過期使用者將失去進階功能存取權
CSRF 防護¶
4pass 實作了同步器 Token 模式 (Synchronizer Token Pattern),使用每個 Session 獨立的 Token 和常數時間比對。
| 屬性 | 詳情 |
|---|---|
| 模式 | 同步器 Token(每個 Session 獨立,伺服器端驗證) |
| 標頭 | X-CSRF-Token |
| 受保護方法 | POST、PUT、DELETE、PATCH |
| Token 產生方式 | 每個 Session 建立 32 位元組的 secrets.token_urlsafe() |
| 比對方式 | hmac.compare_digest() —— 常數時間以防止計時攻擊 (Timing Attack) |
排除路徑(不需要 CSRF):
| 路徑 | 原因 |
|---|---|
/webhook/* |
TradingView Webhook 使用自己的四層驗證機制 |
/auth/login、/auth/register |
尚未建立 Session |
/auth/token |
OAuth2 標準 Token 端點 |
/auth/forgot-password、/auth/reset-password |
認證前流程 |
/auth/silent-refresh |
受 SameSite=strict Cookie 保護 |
/auth/public-key |
公鑰取得(唯讀) |
/public/* |
公開端點(無 Session) |
/health、/docs、/openapi.json |
基礎設施端點 |
縱深防禦
CSRF 防護建立在 SameSite=strict Cookie 之上。即使瀏覽器漏洞繞過了 SameSite,CSRF Token 仍提供第二道防線。反之,即使 CSRF Token 驗證有缺陷,SameSite 也能防止跨來源 Cookie 提交。
API 金鑰認證¶
針對程式化存取(腳本、整合),4pass 支援 API 金鑰認證作為 JWT Session 的替代方案。
| 屬性 | 詳情 |
|---|---|
| 產生方式 | secrets.token_urlsafe(32) —— 256 位元的熵值 |
| 儲存方式 | 僅儲存 SHA-256 雜湊——明文在建立時僅顯示一次,之後不再儲存 |
| 查詢方式 | 對傳入金鑰做雜湊,查詢 api_keys 資料表 |
| 過期設定 | 可依金鑰個別設定 |
| 追蹤 | 每次使用時更新 last_used_at |
| 撤銷 | 可透過控制面板或 API 立即撤銷 |
Authorization: Bearer <api_key>
│
├── SHA-256(api_key) → lookup in api_keys table
├── Check expiration
├── Update last_used_at
└── Resolve user → proceed
CAPTCHA¶
4pass 使用 Cloudflare Turnstile —— 一種隱形、保護隱私的 CAPTCHA,不需要使用者操作。
| 屬性 | 詳情 |
|---|---|
| 供應商 | Cloudflare Turnstile |
| 使用者體驗影響 | 零——隱形,無拼圖或勾選框 |
| 套用於 | /auth/login、/auth/register |
| 驗證方式 | 透過 Cloudflare API 伺服器端驗證(siteverify 端點) |
| 設定方式 | CAPTCHA_ENABLED 環境變數 |
| 掃描器旁通 | 安全掃描器(Wapiti、OWASP ZAP)透過 User-Agent 偵測,可旁通以進行測試 |
IP 白名單¶
使用者可選擇將交易端點限制為特定 IP 位址:
- 透過控制面板設定頁面為每位使用者個別設定
- 儲存於
user_allowed_ips資料表 - 在 Webhook 和交易請求時檢查
- 違規記錄:
webhook_ip_blocked稽核事件,包含完整請求上下文 - 電子郵件警報: 當請求被白名單阻擋時即時通知(限速每 30 分鐘最多 1 封)
何時使用 IP 白名單
IP 白名單建議用於從固定 TradingView IP 或專用伺服器發送 Webhook 的使用者。它增加了一個網路層級的限制,獨立於所有其他身份驗證層運作。
中介層堆疊 (Middleware Stack)¶
安全中介層在每個請求上以固定順序執行。每一層全域套用——沒有任何路由能意外繞過保護。
1. CORS 中介層¶
| 屬性 | 值 |
|---|---|
| 允許的來源 | 正式環境網域 + localhost:5173(開發環境)透過 ALLOWED_ORIGINS 環境變數 |
| 允許憑證 | true(HttpOnly Cookie 傳輸所需) |
| 允許的方法 | 所有標準 HTTP 方法 |
| 公開標頭 | 有限的集合 |
2. BrowserOnly 中介層¶
在正式環境中,透過驗證瀏覽器自動傳送的 Origin 和 Referer 標頭來拒絕非瀏覽器請求。
排除檢查的路徑: /webhook/*、/public/*、/setup/*、/health、/static/*、/docs、著陸頁面、SEO 檔案。
3. CSRF 中介層¶
在狀態變更方法(POST、PUT、DELETE、PATCH)中擷取 X-CSRF-Token 標頭,並儲存在 request.state 中,以便後續與 Session Token 進行驗證。
4. 安全標頭中介層¶
在每個回應中注入安全回應標頭(在 ALB 後方執行時取代 NGINX security-headers.conf):
| 標頭 | 值 | 用途 |
|---|---|---|
Strict-Transport-Security |
max-age=31536000; includeSubDomains; preload |
強制 HTTPS 1 年 |
Content-Security-Policy |
含明確白名單的嚴格策略 | 防止 XSS、資料注入 |
X-Content-Type-Options |
nosniff |
防止 MIME 嗅探 |
X-Frame-Options |
SAMEORIGIN |
防止點擊劫持 (Clickjacking) |
X-XSS-Protection |
1; mode=block |
舊版 XSS 過濾器 |
Cross-Origin-Opener-Policy |
same-origin |
Spectre 緩解措施 |
Cross-Origin-Resource-Policy |
same-origin |
Spectre 緩解措施 |
Referrer-Policy |
strict-origin-when-cross-origin |
限制 Referrer 洩漏 |
Permissions-Policy |
geolocation=(), microphone=(), camera=(), payment=() |
停用未使用的 API |