跳轉到

身份驗證與 Session 管理

4pass 採用分層身份驗證架構:JWT Token 用於 Session 識別、Argon2id 用於密碼驗證、每個 Session 獨立的 CSRF Token、Cloudflare Turnstile 用於機器人防護,以及有序的中介層堆疊在每個請求上強制執行安全策略。

關鍵安全特性

JWT 令牌僅存在於 HttpOnly Cookie 中 — JavaScript 永遠不會看到、儲存或傳輸令牌,消除了基於 XSS 的令牌竊取。每個狀態變更請求都需要透過同步器令牌模式的逐工作階段 CSRF 令牌,以常數時間比較驗證。所有 Cookie 的 SameSite=strict 即使瀏覽器漏洞繞過 CSRF 也能阻止跨來源提交。


JWT Token 架構

sequenceDiagram participant Browser as 瀏覽器 participant API as FastAPI participant CF as Cloudflare Turnstile participant DB as PostgreSQL participant Redis as Valkey Browser->>API: POST /auth/login<br/>{email, password, turnstile_token} API->>CF: 驗證 Turnstile Token CF-->>API: ✓ 人類驗證通過 API->>DB: SELECT user WHERE email = ? DB-->>API: 使用者記錄 API->>API: Argon2id verify(password, hash) API->>DB: INSERT user_session<br/>(csrf_token, ip, user_agent) DB-->>API: Session 已建立 API->>API: 簽發 JWT Access Token(30 分鐘)<br/>簽發 JWT Refresh Token(30 天) API-->>Browser: Set-Cookie: access_token (HttpOnly, Secure, SameSite=strict)<br/>Set-Cookie: refresh_token (HttpOnly, Secure, SameSite=strict)<br/>Body: {user, csrf_token} Note over Browser: CSRF Token 儲存於<br/>sessionStorage Browser->>API: GET /trading/accounts<br/>Cookie: access_token<br/>X-CSRF-Token: {token} API->>API: 驗證 JWT 簽章 + 過期時間 API->>DB: 載入使用者 + Session API->>API: 驗證 CSRF(hmac.compare_digest) API-->>Browser: 200 OK + 資料

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 來源依序檢查:

  1. Cookie(主要)—— access_token Cookie,供瀏覽器客戶端使用
  2. Bearer 標頭 —— Authorization: Bearer <token>,供 API 客戶端及 Swagger UI 使用

Token 刷新流程:

sequenceDiagram participant Browser as 瀏覽器 participant API as FastAPI participant DB as PostgreSQL Browser->>API: POST /auth/silent-refresh<br/>Cookie: refresh_token API->>API: 驗證 Refresh Token JWT API->>DB: 驗證 Session 仍為有效 DB-->>API: Session 有效 API->>API: 簽發新的 Access Token(30 分鐘) API->>DB: 選擇性輪替 Refresh Token API-->>Browser: Set-Cookie: access_token(新)<br/>Set-Cookie: refresh_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
受保護方法 POSTPUTDELETEPATCH
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 偵測,可旁通以進行測試
sequenceDiagram participant Browser as 瀏覽器 participant Turnstile as Cloudflare Turnstile participant API as FastAPI participant CF as Cloudflare API Browser->>Turnstile: 載入隱形小工具 Turnstile-->>Browser: 驗證 Token(自動) Browser->>API: POST /auth/login<br/>{email, password, turnstile_token} API->>CF: POST siteverify<br/>{secret, token, ip} CF-->>API: {success: true} API->>API: 繼續登入流程

IP 白名單

使用者可選擇將交易端點限制為特定 IP 位址:

  • 透過控制面板設定頁面為每位使用者個別設定
  • 儲存於 user_allowed_ips 資料表
  • 在 Webhook 和交易請求時檢查
  • 違規記錄: webhook_ip_blocked 稽核事件,包含完整請求上下文
  • 電子郵件警報: 當請求被白名單阻擋時即時通知(限速每 30 分鐘最多 1 封)

何時使用 IP 白名單

IP 白名單建議用於從固定 TradingView IP 或專用伺服器發送 Webhook 的使用者。它增加了一個網路層級的限制,獨立於所有其他身份驗證層運作。


中介層堆疊 (Middleware Stack)

安全中介層在每個請求上以固定順序執行。每一層全域套用——沒有任何路由能意外繞過保護。

flowchart LR REQ["傳入<br/>請求"] --> CORS["1. CORS<br/>來源驗證"] CORS --> BROWSER["2. 僅瀏覽器<br/>封鎖 curl/Postman"] BROWSER --> CSRF["3. CSRF<br/>Token 驗證"] CSRF --> HEADERS["4. 安全標頭<br/>HSTS、CSP、X-Frame"] HEADERS --> APP["應用程式<br/>路由處理"]

1. CORS 中介層

屬性
允許的來源 正式環境網域 + localhost:5173(開發環境)透過 ALLOWED_ORIGINS 環境變數
允許憑證 true(HttpOnly Cookie 傳輸所需)
允許的方法 所有標準 HTTP 方法
公開標頭 有限的集合

2. BrowserOnly 中介層

在正式環境中,透過驗證瀏覽器自動傳送的 OriginReferer 標頭來拒絕非瀏覽器請求。

排除檢查的路徑: /webhook/*/public/*/setup/*/health/static/*/docs、著陸頁面、SEO 檔案。

3. CSRF 中介層

在狀態變更方法(POSTPUTDELETEPATCH)中擷取 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