跳轉到

Webhook 安全

Webhook 是主要的攻擊面——它們從公開網際網路接收請求,沒有瀏覽器上下文、沒有 Cookie、也沒有 CSRF Token。任何 HTTP 客戶端都可以向 Webhook URL 發送 POST 請求。4pass 實作了四層強制驗證,每一層都必須通過才能執行訂單。任何一層的失敗都會立即終止請求。

4pass 與其他平台的比較

多數基於 Webhook 的交易平台僅依賴單一認證層(URL 令牌或 API 金鑰)。4pass 要求全部四層都通過:(1) 每帳戶唯一 URL 令牌、(2) 可配置時間窗口的時間戳記驗證、(3) 常數時間比較的 Webhook 密鑰、以及 (4) Redis 支援的 SHA-256 重送偵測。每層返回不同的 HTTP 狀態碼,實現精確的攻擊分類。


驗證流程

flowchart TB REQ["傳入的 Webhook 請求"] --> L1{"第 1 層<br/>URL Token"} L1 -->|"無效"| R404["404"] L1 -->|"有效"| L2{"第 2 層<br/>時間戳記"} L2 -->|"過期"| R401a["401"] L2 -->|"有效"| L3{"第 3 層<br/>密鑰"} L3 -->|"錯誤"| R401b["401 + 告警"] L3 -->|"有效"| L4{"第 4 層<br/>重放"} L4 -->|"重複"| R409["409"] L4 -->|"唯一"| EXEC["執行訂單"]

每一層都有獨特的安全用途,並回傳不同的 HTTP 狀態碼,使日誌和監控中的攻擊分類更加精確。


第一層:每帳戶唯一 Token

Webhook URL 本身包含一個不透明的 Token,用於識別使用者和特定的交易帳戶:

POST /webhook/{account_webhook_token}/order
屬性 詳情
Token 格式 32 位元組 URL 安全字串 (secrets.token_urlsafe(32))
熵值 256 位元
範圍 同時識別使用者和特定交易帳戶
查詢方式 Token → TradingAccountUser(單一 DB 查詢加 JOIN)
無效 Token 404 Not Found —— 不揭露帳戶是否存在
已停用使用者 403 Forbidden —— 帳戶存在但使用者已停用

為什麼回傳 404 而非 401?

對無效 Token 回傳 404 Not Found 可防止帳戶列舉攻擊 (Account Enumeration)。攻擊者無法區分「此 Token 不存在」和「此 URL 路徑不存在」。回傳 401 會確認端點存在,並引發進一步的探測。


第二層:時間戳記驗證

每個真實交易的 Webhook 請求都必須包含時間戳記。伺服器驗證請求是近期產生的,防止舊的或被擷取的請求在數小時或數天後被重放。

屬性 詳情
酬載欄位 "timestamp": "{{timenow}}" (TradingView 內建變數)
格式 UTC 時間戳記字串,伺服器端解析
驗證方式 |server_time - request_timestamp| < max_request_age
預設最大時效 300 秒(5 分鐘)
可設定 每帳戶 max_request_age 設定
失敗 401 Unauthorized + webhook_timestamp_expired 稽核事件 + 電子郵件警報
模擬模式 旁通——模擬訂單不需要時間戳記
request_age = abs(time.time() - timestamp.timestamp())
if request_age > ctx.max_request_age:
    raise HTTPException(status_code=401, detail="Request expired")

時鐘偏差 (Clock Skew)

300 秒的視窗考慮了網路延遲和 TradingView 伺服器與 4pass 之間的輕微時鐘差異。abs() 也處理了 TradingView 時鐘稍微超前的情況。持續遇到時間戳記失敗的使用者可以在帳戶設定中增加 max_request_age


第三層:密鑰驗證

每個交易帳戶都有一個唯一的 Webhook 密鑰,必須包含在請求主體中。這用於驗證發送者身份——即使攻擊者發現了 Webhook URL(第一層),沒有密鑰也無法執行訂單。

屬性 詳情
酬載欄位 "secret": "your-webhook-secret-here"
密鑰格式 32 位元組 URL 安全字串 (secrets.token_urlsafe(32))
熵值 256 位元
比對方式 hmac.compare_digest(secret, ctx.webhook_secret) —— 常數時間
失敗 401 Unauthorized + webhook_credential_attack 稽核事件 + 電子郵件警報
輪替 使用者可隨時從控制面板重新產生 Webhook 密鑰

為什麼使用常數時間比對?

簡單的 secret == expected 比對會透過計時洩漏資訊。如果第一個字元匹配,比對會比不匹配時多花一點時間。攻擊者可以透過測量回應時間逐個字元暴力破解密鑰。hmac.compare_digest() 無論差異在哪裡都以常數時間比對所有位元組。


第四層:重放偵測 (Replay Detection)

即使擁有有效的 Token、時間戳記和密鑰,擷取到合法請求的攻擊者仍可在時間戳記有效視窗內重放該請求。第四層透過在 Redis 中追蹤請求指紋來防止此攻擊。

屬性 詳情
雜湊輸入 user_id:action:symbol:quantity:timestamp:secret
演算法 SHA-256,截取為 32 個十六進位字元
儲存方式 Redis 鍵值:webhook:replay:{hash}
TTL max_request_age 秒(與時間戳記視窗相同)
重複檢查 redis.exists(key) —— 如果鍵值存在,則為重放請求
失敗 409 Conflict + webhook_replay_attack 稽核事件 + 電子郵件警報
flowchart TB REQ["Webhook 請求"] --> HASH["SHA-256 雜湊:<br/>user_id:action:symbol:quantity:timestamp:secret"] HASH --> CHECK{"Redis 鍵值<br/>存在?"} CHECK -->|"是"| REJECT["409 Conflict<br/>偵測到重放"] CHECK -->|"否"| STORE["儲存雜湊至 Redis<br/>TTL = max_request_age"] STORE --> PROCEED["繼續執行<br/>訂單"]

雜湊中包含 secret 欄位,以防止攻擊者構造具有相同雜湊值的不同請求。TTL 自動使舊雜湊過期,保持 Redis 記憶體使用量在可控範圍內。


額外保護

除了四層驗證之外,Webhook 請求在執行訂單前還會經過額外檢查:

保護措施 實作方式 失敗回應
每位使用者速率限制 每分鐘 60 次請求,透過 Redis 有序集合滑動視窗 429 Too Many Requests
IP 白名單 可選的每位使用者限制(user_allowed_ips 資料表) 403 Forbidden + webhook_ip_blocked 警報
訂閱檢查 免費方案使用者限制為模擬交易 403 Forbidden
每日訂單上限 依方案設定的每日最大訂單數(防止失控交易) 429 Too Many Requests
憑證驗證 若券商憑證尚未驗證則拒絕 400 Bad Request
服務條款 若使用者未接受最新服務條款則拒絕 403 Forbidden
稽核日誌 所有嘗試(成功和失敗)均記錄完整的 IP、User-Agent 和上下文

速率限制採故障開放 (Fail-Open) 設計

每位使用者的速率限制器使用 Redis,但在 Redis 連線錯誤時採故障開放策略。此設計確保 Redis 暫時中斷不會阻擋合法的交易操作。WAF 層級的速率限制(每 IP、AWS 管理)提供後備保護。


TradingView 設定

Webhook URL

https://4pass.io/webhook/{account_webhook_token}/order

{account_webhook_token} 替換為控制面板中交易帳戶 → Webhook 設定顯示的 Token。

警報訊息(JSON 酬載)

{
    "action": "{{strategy.order.alert_message}}",
    "symbol": "TXFR1",
    "quantity": {{strategy.order.contracts}},
    "timestamp": "{{timenow}}",
    "secret": "your-webhook-secret-here"
}
欄位 來源 說明
action {{strategy.order.alert_message}} TradingView 策略動作(buy、sell、close 等)
symbol 硬編碼或 {{ticker}} 交易標的
quantity {{strategy.order.contracts}} 口數/手數
timestamp {{timenow}} 當前 UTC 時間(TradingView 內建變數)
secret 您的 Webhook 密鑰 在控制面板 → 交易帳戶 → Webhook 設定中取得

模擬模式

在 Webhook URL 後附加 ?simulation=true 即可在模擬模式下執行。模擬訂單會旁通時間戳記和密鑰驗證(第二至三層),且不會執行真實交易。

https://4pass.io/webhook/{token}/order?simulation=true

安全性比較

4pass Webhook 安全性與常見替代方案的比較:

功能 4pass 一般 Webhook 僅 API 金鑰
基於 URL 的帳戶隔離 有 每帳戶獨立 Token 無 共用 URL
時間戳記驗證 有 可設定視窗
密鑰認證 有 常數時間比對 無或基本認證 有 標頭
重放偵測 有 Redis + SHA-256
每位使用者速率限制 有 每分鐘 60 次滑動視窗 每金鑰
IP 白名單 有 可選的每位使用者設定
即時攻擊警報 有 含上下文的電子郵件
稽核日誌 有 完整請求上下文 部分