lucafchala/restricted

GitHub: lucafchala/restricted

一个基于 Cloudflare Workers 和静态 HTML 构建的单页 CTF 解谜游戏,玩家需破解客户端混淆逻辑、计算 6 位访问码并提交至共享排行榜。

Stars: 1 | Forks: 0

# restricted.lucafchala.com **在线访问:** [restricted.lucafchala.com](https://restricted.lucafchala.com) · **Worker:** `ctf-leaderboard.lucafchala.workers.dev` · **技术栈:** 静态 HTML + Cloudflare Worker (TypeScript) + Workers KV 它是 [lucafchala.com 生态系统](https://github.com/lucafchala/lucafchala.com#the-ecosystem) 的一部分(它**不**使用共享设计系统 —— 详情见[设计](#design))。 ## 这是什么 **一句话概括:** `restricted` 是一个基于浏览器的 CTF,你需要通过客户端的门禁,计算出一个固定的 6 位访问码,并将其连同用户名提交给 Cloudflare Worker。Worker 会在服务端进行验证,并将你添加到存储在 KV 的共享排行榜中。 **一段话概述:** 整个挑战作为一个静态的 `index.html` 文件发布 —— 门禁、门禁后的“金库”以及排行榜 UI 都包含在其中,而逻辑和线索被故意混淆在客户端。获取密码只会解锁下一个阶段;真正的获胜条件是提交那唯一正确的访问码。提交操作会发送给一个用 TypeScript 编写的轻量级 Worker(`src/index.ts`),它保存着标准答案和排行榜。任何人都可以读取排行榜 —— 包括那些尚未解开谜题的人 —— 这也是游戏的一部分。 ## 架构 ``` restricted.lucafchala.com (static index.html) │ gate → vault → leaderboard form (all client-side, obfuscated) │ │ GET ?action=list ─────────────► ┌─────────────────────────────────────┐ └─ POST {username, code} ───────────► │ Worker: ctf-leaderboard │ │ src/index.ts │ │ • validates code server-side │ │ • appends entry to leaderboard │ │ • CORS: Access-Control-Allow-Origin: * │ • KV binding "KV" → key "entries" │ └─────────────────────────────────────┘ ``` - **前端:** 静态的 `index.html`,部署在 `restricted.lucafchala.com`。它实现了门禁逻辑(通过 `localStorage` 追踪带有冷却时间的硬编码尝试限制)和金库提交 UI。包含用于标记自动化探测的蜜罐路径/链接。 - **后端:** 单个 Cloudflare Worker(`src/index.ts`): - `GET …?action=list` → 以 JSON 格式返回排行榜。 - `POST` 并附带 `{ username, code }` → 根据标准值验证 `code`,如果正确,则追加该条目。 - CORS 是开放的(`*`),以便静态页面可以进行跨域调用。 - **存储:** 一个 Workers **KV** 命名空间(绑定名为 `KV`),排行榜存储在 key `entries` 下。 - **`robots.txt`** 禁止访问 `/vault`、`/api/` 和 `/session`(诱饵路径)。 ## 前置条件 - **Node.js** 和 **[Wrangler](https://developers.cloudflare.com/workers/wrangler/)** v4 (`npx wrangler`)。 - 一个 **Cloudflare** 账号,并绑定一个名为 `KV` 的 **KV 命名空间**(其 ID 已提交在 `wrangler.jsonc` 中;如果你 fork 了该项目,请替换为你自己的命名空间 ID)。 - 对于 CI 部署:需要一个 `CLOUDFLARE_API_TOKEN` GitHub Actions 密钥。 ## 配置 `wrangler.jsonc`: ``` { "$schema": "node_modules/wrangler/config-schema.json", "name": "ctf-leaderboard", "main": "src/index.ts", "compatibility_date": "2025-02-04", "observability": { "enabled": true }, "kv_namespaces": [ { "binding": "KV", "id": "c240dc4587cd4ee890a20761aae811e5" } ] } ``` | 名称 | 类型 | 描述 | |---|---|---| | `KV` | KV 绑定 | 存储排行榜(`entries` key) | | `CLOUDFLARE_API_TOKEN` | GitHub Actions 密钥 | 供部署工作流用于发布 Worker | 无需其他密钥;访问码是 Worker 源码中的一个常量,而不是环境变量。 ## 安装与部署 ``` git clone https://github.com/lucafchala/restricted.git cd restricted # 本地运行 Worker npx wrangler dev # serves src/index.ts; open index.html separately for the front end # 部署 Worker npx wrangler deploy ``` **CI:** `.github/workflows/deploy-worker.yml` 会在每次推送到 `main` 分支时(以及在手动触发 `workflow_dispatch` 时)部署 Worker,它使用 `cloudflare/wrangler-action@v3`,并配置了 `wranglerVersion: '4'` 和 `CLOUDFLARE_API_TOKEN` 密钥。静态的 `index.html` 由 `restricted.lucafchala.com` 提供(拥有独立的 Pages/托管设置)。 ## 排行榜 API ``` GET https://ctf-leaderboard.lucafchala.workers.dev/?action=list → 200 { entries: [ { username, … } ] } POST https://ctf-leaderboard.lucafchala.workers.dev/ body: { "username": "...", "code": "..." } → 200 on a correct code (entry appended) / rejected otherwise ``` 响应包含宽松的 CORS 头,以便解谜页面可以直接从浏览器调用 Worker。 ## 文件结构 ``` . ├── index.html # The full CTF: gate + vault + leaderboard UI (obfuscated client logic) ├── src/ │ └── index.ts # Cloudflare Worker: GET leaderboard, POST validated submission, KV "entries" ├── wrangler.jsonc # Worker config (name "ctf-leaderboard", KV binding, compat date) ├── robots.txt # Disallows decoy paths (/vault, /api/, /session) └── .github/workflows/ └── deploy-worker.yml # Deploy Worker on push to main / manual dispatch (wrangler-action v4) ``` ## 设计 故意采用**非品牌化**风格 —— 这个谜题使用的是复古的、印刷文档的外观,而不是[共享的生态系统设计系统](https://github.com/lucafchala/lucafchala.com#design-system): - **字体:** *IM Fell English*(衬线体)+ *Courier Prime*(等宽体),来自 Google Fonts。 - **调色板:** 暖纸色 —— `--bg #f5f0e1`, `--paper #ede8d5`, `--ink #1a1612`, `--rule #c8b89a`,并使用 `--red #8b1a1a` / `--green #1a4a1a` / `--link #00008b` 表示状态。 这种不协调是刻意为之的:`restricted` 是一个独立游戏,而不是网络页面,因此它不继承黄底黑字的视觉特征。 ## 状态 **已投入生产环境**,作为一个在线谜题。排行榜是公开的,并持久化存储在 KV 中。
标签:Serverless, TypeScript, Web安全, 后端开发, 多模态安全, 安全插件, 排行榜, 程序员工具, 蓝队分析, 高对比度