security(auth): P2 帳號鎖定 + OAuth state 驗證

- Account lockout:連續失敗 5 次鎖定 15 分鐘(Fixed Window,Cache)
  - Member / Provider 各自獨立 namespace
  - Email 正規化(strtolower+trim)
  - 不存在帳號不累計計數(防 Cache 污染)
  - companion key 記錄 locked_until(ISO 8601)
- OAuth CSRF 防護:移除 stateless(),手動 state 生成與驗證
  - session('oauth_state') + session('state') 雙寫確保 Socialite 內建驗證通過
  - state mismatch redirect 至 /login?error=oauth_failed
- throttle 從 5,1 調整為 10,1,避免 IP throttle 在 lockout 前觸發
- NormalizesEmail Trait、config/auth_lockout.php

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-06-03 01:20:25 +08:00
parent b2e20767d8
commit 2a0e9255ae
14 changed files with 680 additions and 47 deletions
@@ -0,0 +1,38 @@
## Why
IP-based throttleP0 已實作)只能防單一 IP 暴力攻擊,無法阻擋分散式密碼暴破(攻擊者用多 IP 對同一帳號嘗試);OAuth 流程目前以 `stateless()` 繞過 CSRF 驗證,存在被偽造授權碼的風險。在金流整合上線前先補齊這兩個帳號層防護,可大幅降低帳號接管與 OAuth CSRF 風險。
## What Changes
- **新增帳號層鎖定機制**Member / Provider 登入連續失敗 N 次後,鎖定該帳號(而非 IP),鎖定期間回傳 HTTP 423;鎖定計數以 Laravel Cache 儲存,N 分鐘後自動解鎖。
- **新增 OAuth state 驗證**`SocialAuthController` 產生隨機 state 字串存入 sessioncallback 時驗證 state 是否吻合;不吻合時拒絕並回傳 HTTP 400;移除 `stateless()` 呼叫。
## Capabilities
### New Capabilities
- `account-lockout`:帳號層連續失敗鎖定,覆蓋 Member / Provider 登入流程(Admin 登入失敗計數另行討論,暫不列入)
- `oauth-state-validation`OAuth CSRF 防護,補 state parameter 生成與驗證,適用 Member Google OAuth 流程
### Modified Capabilities
- `login-rate-limiting`:無需變更需求(IP throttle 與帳號鎖定並行,各自獨立,Requirement 不衝突)
## Impact
**後端**
- `app/Http/Controllers/API/AuthController.php`Member login
- `app/Http/Controllers/API/ProviderAuthController.php`Provider login
- `app/Http/Controllers/API/SocialAuthController.php`OAuth redirect / callback
- `routes/api.php`(可能新增解鎖端點)
- `config/auth_lockout.php`(鎖定參數:最大失敗次數、鎖定時間)
- Laravel Cache(驗證失敗計數,driver 使用現有 Redis 或 file
**前端**
- `src/views/LoginView.vue` / `CoachLoginView.vue`:顯示「帳號已鎖定,請稍後再試」錯誤訊息(HTTP 423 對應)
- `src/plugins/echo.js` / `src/api/axios.js`:無需變更(OAuth callback 不走 Axios
- `src/views/AuthCallbackView.vue`:無需變更(後端回傳 400 時 redirect 到 login 並帶錯誤訊息)
**不影響**
- 現有 Sanctum token、sessionStorage、refresh interceptor
- Admin PanelAdmin 登入鎖定不在此 change 範圍)