Files
OpenFlare/docs/en/design/login-captcha.md
T
ryan 454542c1d0 docs(i18n): 恢复并补齐英文版 vitepress,README 默认改为英文
- README 默认英文:README.en.md → README.md(英文为默认),中文移至 README.zh-CN.md,语言切换链接同步
- 恢复被删除的 docs/en/ 英文文档(git 历史 cc5e53c5^),删除 4 篇已废弃文件
- 英文导航 config.ts 对齐中文结构(新增 Deployment/Changelog 侧栏,同步 Guide/Design 条目)
- 翻译 15 篇中文新增文档:guide 5 篇(certificates/pages-usage/proxy-config/uptime-kuma/zone-domain-migration)+ design 10 篇(zone-design/cloudflare-pointing/waf-orchestration/origin-error-page/edge-cache-design/pages-design/logstore/kuma-design/login-captcha/observability 三篇)
- en 首页更新(新增 Pages 特性、tagline 同步);changelog 英文入口指向中文版
- vitepress 构建验证:43 个英文页面全部渲染

注意:29 篇旧英文文档为恢复版,部分内容(如 deployment/server、reference/configuration)可能落后于中文,需后续逐篇同步
2026-08-16 23:18:29 +08:00

128 lines
5.6 KiB
Markdown

# Login CAPTCHA Integration (Cap)
This document describes the design of introducing **Cap** — an open-source CAPTCHA solution based on Proof-of-Work (PoW) and invisible browser fingerprint features — into the OpenFlare control plane, to protect the login API against brute-force attacks and credential-stuffing by crawlers.
---
## 1. Business Background and Product Scope
### Background and Pain Points
The OpenFlare login endpoint `/api/v1/user/login` lacks user-dimension protection; attackers can use proxy pools to perform credential stuffing and brute-force attacks on high-privilege accounts (such as `root`). At the same time, standard visual CAPTCHAs are unfriendly to login-page UX and accessibility.
### Product Scope and Technology Choice
* **Technology choice**: Cap (a Proof-of-Work-driven, invisible, image-free CAPTCHA solution).
- **Core principle**: the client (Widget/page) obtains a proof-of-work (PoW) challenge from the server, computes the solution in the browser background, and sends the answer back. The server verifies the answer to complete human-machine verification.
- **Advantages**: invisible, image-free, no dependency on external third-party API nodes (private), tiny package size.
* **Integration scope**: the control-plane Server login API (`/api/v1/user/login`) and the frontend login page.
* **Config granularity**: admins can toggle the CAPTCHA on/off anytime via the console Option table (`cap_login_enabled`).
---
## 2. System Architecture and Interaction Sequence
### 2.1 Module Responsibilities
1. **Frontend**:
* Introduces the `cap-widget` (React 19 custom element) on the login page.
* On form submit, accompanies the submission with the `cap-token` solved by the Widget.
2. **Server (control-plane backend)**:
* Exposes `POST /api/cap/challenge` to distribute the PoW challenge and a signed JWT token to the client.
* Exposes `POST /api/cap/redeem` to verify the submitted PoW solution and issue a login credential (Redeem Token) with an expiry time.
* Stores the Redeem Token and its expiry in the in-memory/Redis cache.
* In `POST /api/v1/user/login`, when CAPTCHA protection is enabled, first validates and consumes (single-use) the corresponding `cap-token`.
### 2.2 Verification Flow Sequence Diagram
```mermaid
sequenceDiagram
autonumber
actor User as User
participant Browser as Browser (Frontend Web)
participant Server as OpenFlare Server (Backend)
participant Cache as Memory/Redis Cache
User->>Browser: Open login page
Browser->>Server: POST /api/cap/challenge (get challenge)
Server->>Browser: Return {challenge, token, expires} (JWT format)
Note over Browser: Widget computes the PoW challenge in background (WASM/Worker)
Browser->>Server: POST /api/cap/redeem (submit solutions + token)
alt PoW solution valid
Server->>Cache: Store Redeem Token (tokenKey:expires)
Server->>Browser: Return {success: true, token} (i.e. cap-token)
else validation failed
Server->>Browser: Return {success: false, reason}
end
User->>Browser: Enter account/password, click login
Browser->>Server: POST /api/v1/user/login (with X-Cap-Token in HTTP header)
alt CapLoginEnabled = true
Server->>Server: Middleware (CapAuth) validates and consumes X-Cap-Token
alt token valid, not expired, not consumed
Server->>Server: c.Next() -> normal login logic (Bcrypt password check)
Server->>Browser: Return login success (Session Cookie)
else token invalid or already consumed
Server->>Browser: Intercept and return CAPTCHA error (401 Unauthorized)
end
else CapLoginEnabled = false
Server->>Server: c.Next() -> normal login logic
end
```
---
## 3. Core APIs and Data Model
### 3.1 API Definitions
#### 1. Get Challenge (POST /api/cap/challenge)
* **Method**: `POST`
* **Auth**: public
* **Response payload** (unified API envelope, `data` is the business payload):
```json
{
"error_msg": "",
"data": {
"challenge": {
"c": 1,
"s": 32,
"d": 4
},
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"expires": 1717660800000
}
}
```
#### 2. Redeem Challenge (POST /api/cap/redeem)
* **Method**: `POST`
* **Request payload**:
```json
{
"token": "challenge_jwt_token_here",
"solutions": [12345, 67890, 54321]
}
```
* **Response payload (success)**:
```json
{
"success": true,
"token": "random_id:ver_token",
"expires": 1717661000000
}
```
#### 3. Login API (POST /api/v1/user/login)
* **Request payload unchanged**:
```json
{
"username": "root",
"password": "your_password"
}
```
* **CAPTCHA carrier**: placed in the HTTP Request Header `X-Cap-Token`.
---
## 4. Replay Attack Protection and Security Trade-offs
1. **JWT temporary state binding**: the challenge is signed into the JWT payload at generation time, including an expiry limit (10 minutes).
2. **Replay interception (nonce consumption)**: when the client calls `/redeem` to submit the solution, the backend marks the JWT signature as used in the cache. Re-submitting the same solution package returns `already_redeemed`.
3. **Redeem single-use (one-time invalidation)**: when the client logs in and submits the `cap-token`, the backend immediately deletes the key from the cache after validating it, preventing attackers from extracting historical valid `cap-token`s for login replay.
4. **Seamless verification**: by tuning parameters like `c` (challenge count) and `d` (difficulty), you balance solve time against anti-crawler strength; users solve silently in the background without interrupting the login flow.