mirror of
https://github.com/Rain-kl/OpenFlare.git
synced 2026-09-28 05:46:36 +08:00
454542c1d0
- 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)可能落后于中文,需后续逐篇同步
128 lines
5.6 KiB
Markdown
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.
|