mirror of
https://github.com/Rain-kl/OpenFlare.git
synced 2026-10-09 00:56:37 +08:00
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)可能落后于中文,需后续逐篇同步
This commit is contained in:
@@ -0,0 +1,127 @@
|
||||
# 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.
|
||||
Reference in New Issue
Block a user