
セルフホスト型 OWASP CTF キット:1 台のマシン、1 つの無料 GitHub org、クラウド依存なし
セキュリティ学習イベントのためのセルフホスト型コントロールプレーン — 1 台のマシン、1 つの無料 GitHub org で完結。
大学、高校、OWASP チャプター、ミートアップで運用できます。
コードを書く前に AGENTS.md を読んでください。これは運用マニュアルです。CI が実行する正確なコマンド、このリポジトリがすでに遭遇した障害モード、そして docs/reviewing.md にあるレビュー不変条件が記載されています。CLAUDE.md は同じファイルへのポインタです。
変更は、CI がグリーンであり、かつ最新コミットに対するすべての実行可能な CodeRabbit スレッドが解決済み(または記録上で却下)であるときに準備完了となります。コミットは Conventional Commits に従い、AI による帰属表示を含みません。
小さく、明確に仕様化された作業には good first issue タグが付いています。新しいモジュールは PR ではなく issue として始まります — CONTRIBUTING.md を参照してください。
単一のゲームではなく、コントロールプレーンです。 このボックスはイベントに共有の背骨を与えます — GitHub org、チーム登録、ライブリーダーボード、オーガナイザー管理パネル、そしてそれを支えるスコアリングパイプラインです。モジュールがチャレンジコンテンツをその背骨に接続し、任意のサブセットを単独または組み合わせて実行できます。パッチ・トゥ・スコアの Secure Development、Quiz バンク、Jeopardy ボード、そして外部ホスト型の AI チャレンジです。モジュール契約が背骨とコンテンツの境界であり、そのためこのボックスは、フォレンジック、API セキュリティ、クラウドといったさらなるモジュールが登場するたびにホストできるように作られています。
なぜ存在するのか。 Secure Development モジュールは攻撃ではなく防御を教え、安全なコーディングを教えるための真に優れた方法です。これまでは、1 回実行するには Vercel、Upstash、Lambda、DynamoDB を立ち上げ、クラウドの請求を負担し、プライベートなスコアリングイメージへのアクセスを持つ必要がありました。予算のあるカンファレンスにとっては妥当な要求です。しかし、大学のセキュリティコース、高校のクラブ、OWASP チャプターの夜、週末のワークショップにとっては不合理な要求です。
このキットはそれを取り除きます。すべては、すでに手元にある 1 台のマシン — ラップトップ、予備のデスクトップ、小さな VPS — 上の Docker Compose から実行され、フォーク用に 1 つの無料 GitHub org があれば十分です。6 つのターゲットすべてのルーブリックがボックス内に同梱されているため、要求すべきプライベートイメージも、書くべきスコアリングコードもありません。請求は発生せず、外部への通信もなく、イベントが終わったらリポジトリをアーカイブしてスタックを停止するだけです。
対象者: このイベントを運営したいが、そのためにクラウドオペレーターになることを望まないすべての人 — コース講師、クラブオーガナイザー、OWASP チャプターリード、ワークショップファシリテーター、社内トレーニングデーを運営するセキュリティチーム。
デプロイ済みでエンドツーエンドで検証済み。ただし実際のコホート向けにはまだ運用されていません。 完全なスコアリング経路がキット内に同梱されています — スコアラーの bearer 認証された POST /score、フォーク用の自己完結型スコアリングワークフロー、ポーリングトランスポート — そして scripts/smoke.sh がそのパイプライン全体をモックに対して駆動します。さらに、このキットはこのリポジトリが同梱するのと同じ Compose ファイルからホストされたボックス上で継続的に稼働しており、GET /health はそれを提供している正確なリビジョンを報告し、そのライブインスタンスに対するエンドツーエンドのパスで、一連の実際の欠陥が見つかり修正されました — モック化されたスイートでは見えない種類のものです。
まだ起きていないのは実際のイベントです。すなわち、競技者のコホートが実際のフォークに対して実際の PR を、一度に、何時間も開くことです。これが「パイプラインは動く」と「パイプラインは 40 人で動く」の間のギャップです。2 つの注意点は埋もれさせずに明示されています。Security Shepherd の結果マッチャーには明記された残存限界があり(異常な言い回しの拒否が依然として解決として読まれる可能性があります — 正しいパッチを過小評価することはあっても、無料のポイントを与えることは決してありません)、完全なコホートの負荷プロファイルは未検証です。詳細と現在の状態: Status and upstream dependencies。
これらがやらないことを、このキットはやります。GitHub プルリクエストを通じて採点されるパッチ・トゥ・スコアの防御トレーニング、1 つのリーダーボード上でゲームタイプを混在させるためのモジュール契約、そしてエンドツーエンドで自分が所有するコントロールプレーン — 1 台のボックス、1 つの無料 org、クラウド請求なし、テレメトリなし。
このプロジェクトは OWASP Foundation と提携しておらず、承認も受けていません。6 つの脆弱なターゲットのうち 4 つは OWASP プロジェクトです(Juice Shop、WebGoat、Security Shepherd、VulnerableApp)。DVWA と VAmPI はコミュニティプロジェクトです。
2 分で動作を確認 — GitHub org も OAuth アプリも不要で、設定するものは何もありません。必要なのは Compose v2 付きの Docker と openssl だけです:```sh
git clone https://github.com/OWASP/owasp-ctf-in-a-box
cd owasp-ctf-in-a-box
./scripts/dev-stack up
使い捨てのローカルシークレットを書き込み、スコアラーとアプリのイメージをビルドし、スタックを起動し、スコアラーの実際のスコアリングAPIを通じてデモリーダーボードをシードし、開くべきURLを表示します。シードされたチームとスコアの時系列グラフが表示されたリーダーボードが確認できるはずです。`./scripts/dev-stack score <login> juice-shop 3` を実行すると、さらに3件のsolveがライブで反映されます。`./scripts/dev-stack down` でスタックを破棄します。
**ガイド付きウィザードで実際のイベントを実行する**。**[`gh`
CLI](https://cli.github.com)**(認証済み)を追加し、イベントでSecure Developmentを実施する場合は**無料のGitHub orgを1つ**追加します。`./setup/ctf-setup.sh check` が最初にツールを検証します:```sh
./setup/ctf-setup.sh # guided, prompts for values, resumable
各値を順に尋ねていきます — ボックスの URL、イベントの組織、管理者
ログイン、Secure Development を実行するかどうか、GitHub の認証情報 — そして
.env を書き込み、自動化できるすべてのステップを実行し、GitHub UI 上の
ステップを案内し、中断して戻ってきた場合には再開します。それ以外のすべて
(イベント名、実行するモジュール、対象) は実行時の /admin 設定なので、
編集すべき設定ファイルはありません。実際に必要なものだけを尋ねます。
Secure Development のないイベントには組織もフォークもスコアラーイメージも
不要で、それらについて尋ねられることはありません。変更を伴うステップは
--dry-run でプレビューできます — すでに完成した .env からステップ 4〜9 を
説明し、管理者ログインがない場合、または Secure Development がオンで組織が
ない場合には (設計上) 拒否します。ウィザードは ./setup/ctf-setup.sh doctor
の実行で締めくくられます — これはいつでも再実行できるフォークごとのステータス
マトリクスです — その後、オプションの fly.io デプロイ (デフォルトはいいえ)
を提供するので、同じイベントを公開ホスト名に載せるのもガイド付きのフロー
(ホスト名、プレビューされたデプロイ、そして確認) となり、デプロイドキュメントを
読み漁る旅にはなりません。
詳細を知りたいですか? 個々のサブコマンド、各 UI 専用ステップ、そして
2 つの GitHub アプリの違い:
docs/hosting.md。
クラウドでやりたい? docs/aws.md (Terraform: ECS Fargate、
ElastiCache、ALB — apply で起動 / destroy で破棄) または
docs/fly.md (Fly マシン 1 台)。
Secure Development — 意図的に脆弱なアプリをフォークし、欠陥を見つけ、 パッチを当て、PR を開きます。フォーク内の GitHub Action がパッチに対して 対象のルーブリックを実行し、スコアがリーダーボードに反映されます (ポーリング モードでは約 30 秒後)。6 つの対象、321 のチャレンジ。未修正は 0 点、正しい パッチはその点数を獲得 — 両方向でゲートされます。GitHub 組織とスコアリング パイプラインが必要です。
Quiz — 単一選択および複数選択のセキュリティ問題で、回答した瞬間に
アプリ内で採点されます (複数選択は全問正解か 0 点か)、試行回数の上限と
再試行クールダウン付き。/admin から 1 問ずつ作成するか、1 つの JSON
バンドルとしてインポート・エクスポートできます。GitHub もフォークも
パイプラインも不要です。
Jeopardy — カテゴリ別に主催者が作成したフラグのボード。提出は
トリムおよび正規化され、フラグが大文字小文字を区別するようマークされて
いない限り (そのカードに明記されています) 大文字小文字は許容され、提出
クールダウンとオプションの有料ヒントがあります。クイズと同じ /admin +
JSON バンドルでの作成です。こちらも GitHub は不要です。
AI — ボックスの外部でホストされるプロンプトインジェクションおよび ガードレールのチャレンジ。各参加者のチャレンジページは、外部サイトへの 個人用起動リンクを発行します。解答は、そのサイト自身のコールバックを 通じて、またはアプリに戻って入力されたフラグを通じて、リーダーボードに 報告されます。GitHub もフォークもパイプラインも不要です。
有効にしたモジュールを問わず、プラットフォームは以下を提供します:
キャプテン、参加コード、/join/<code> リンクを備えたチームの自己登録
(ソロプレイは 1 人のチーム。複数のチームメイトが解いたフラグは 1 回だけ
カウントされます)、実際の解答ごとのタイムスタンプに基づく CTFd 形式の
スコア推移グラフ付きライブリーダーボード、許可リスト方式の /admin
パネル — フリーズ、スコアリングおよび登録の時間枠、ヒントとコスト、
チーム上限、クールダウン、モジュールコンテンツ、参加者ごとのサポート
操作、アクティビティストリームとエンゲージメント指標 — すべて実行時で
再ビルド不要、そしてすべての管理者操作に対する上限付きの監査ログ。
| 参加者内訳 | チャレンジブラウザ |
|---|---|
![]() | ![]() |
| Jeopardy フラグボード | クイズ |
|---|---|
![]() | ![]() |
シードされたデモプレイヤーとともに scripts/dev-stack up で
ローカル実行中の参加者アプリからキャプチャ。対象とフォークリンクは
イベント設定で制御され、イベント名とその他のブランディングは管理パネルの
設定です。
1 つの Docker Compose スタック: Caddy が Next.js アプリの前段で TLS を
終端し、アプリは srh (Upstash 互換の REST プロキシ) を通じてのみ Redis と
通信します — ネットワークは分割されているため、インターネットに面する
ものは redis:6379 へのルートを持ちません。Quiz、Jeopardy、AI はアプリ内で
採点し、ポイントを直接 Redis に記録します。Secure Development はボックスの
外部で採点されます: 参加者のフォークが GitHub Action を実行し、対象を
起動してパッチに対してルーブリックを実行し、PR に機械可読なスコアコメントを
投稿します。sync ポーラーがそれらのコメントを取得します — 受信ネットワーク
面はゼロなので、ボックスは NAT の背後や会場の wifi でも動作します (これが
唯一のトランスポートです: プッシュ取り込みは v0.6 で削除されました。
#377 を参照)。
スコアは単一の監査済みライターを通じて入ります: スコアラーのベアラ認証
された POST /score で、これは検証し単調に書き込みます — 解答が後の
失敗した実行によって未解答に戻されることはありません。
全体像 — コンポーネント、9 ステップのスコアデータフロー、セキュリティ モデル — は docs/architecture.md にあります。
このモジュールのコンテンツは、脆弱な対象のセットとその採点用
ルーブリックです。参加者は対象を選び、組織のコピーをフォークし、
パッチを当て、PR を開きます。各対象のチャレンジは実行可能な node:test
スイートで、難易度によって価格が設定されています。
数値は手作業で維持され、ベンダー化されたルーブリックに
apps/web/src/lib/tests/apps-catalogue.test.ts によって
固定されています — vendor-rubric.sh の更新後は再確認して
ください。正しい修正がスコアになることを証明する参照パッチ
(正方向のゲート) は別途 patches/
にあります。
ルーブリックは scorer/rubric.owasp/ にあり、
OWASP-CTF/dc34-owasp-secure-development-ctf
からベンダー化され、scorer/rubric.owasp/PROVENANCE.md に記録された
単一の上流コミットに固定されています。より新しいコミットに対して再ベンダー
化するには:```sh
./scripts/vendor-rubric.sh --all --ref
2つのルーブリック形式が同時にサポートされており、単一のルーブリックディレクトリでそれらを混在させることができます。`<target>.yaml` ファイルは宣言的な HTTP リクエスト/期待プローブ文法を使用し、`<target>/tests/challenges/` ディレクトリは `catalogue.<target>.json` によって価格設定された実行可能テストを使用します。作成ガイド:
[docs/scorer.md](https://github.com/owasp/owasp-ctf-in-a-box/blob/main/docs/scorer.md)。
**ルーブリックの秘匿性について。** これらのルーブリックは公開されています。ターゲットはオープンソースであり、その解答はすでに公開されているため、このキットはルーブリックの秘匿性を、答えを知られることに対する保護ではなく、チェックゲーミングに対する保護として扱います — セルフホスト型イベントにおける許容されたトレードオフです。いつでも独自のプライベートルーブリックで上書きできます:```sh
cp -r /path/to/private-rubric scorer/rubric
docker build -t ghcr.io/<org>/score:latest --build-arg RUBRIC_DIR=rubric scorer/
scorer/rubric/ は gitignore され、まさにこの用途のために予約されています。
スタックが EVENT_URL で起動したら:
/admin を操作します:リーダーボードの凍結、登録の開始と終了、スケジュールの設定、クイズ問題・クラシックチャレンジ・ai チャレンジの作成 — そして 1 人の参加者が行き詰まったときは、イベントをリセットするのではなくその参加者だけを修正します。docker compose logs -f sync を使います(secure-development を有効にして実行されます)。すべての状態は名前付き Docker ボリュームに保存されるため、マシンを再起動しても何も失われません。./setup/ctf-setup.sh teardown でターゲットリポジトリをアーカイブします — その後、GitHub App をアンインストールし、org の Actions シークレットを自分で削除してください。secure-development のないイベントにはアーカイブするフォークがありません。チーム、管理パネル、当日前のキット検証、ローカル開発スタックについては docs/operations.md で説明しています。前提条件、スコアの転送、OAuth のセットアップ、イベント設定については docs/hosting.md を参照してください。
完全な理由、代替案、トレードオフは docs/decisions.md に番号付きの ADR として記録されています。
owasp.github.io/owasp-ctf-in-a-box でレンダリングされています。
コントリビューションを歓迎します — CONTRIBUTING.md では開発環境、CI ゲート、モジュールの提案方法を説明しています。CODE_OF_CONDUCT.md が適用されます。
エージェントは AGENTS.md に従う必要があります。以下のコマンドは CI と一致しています。make help は同じターゲットを一覧表示します。
各サービスは独立してテストされます(すべて Node 22):```sh (cd sync && npm ci && npm test) (cd scorer && npm ci && npm test && node tools/vacuous-sweep.mjs) ./scripts/acceptance-scorer.sh # from the repo root — the script lives in scripts/ (cd apps/web && corepack pnpm install --frozen-lockfile && corepack pnpm lint && corepack pnpm test) ./scripts/smoke.sh # the full poll pipeline, end to end
キット自体に脆弱性を見つけましたか? **[SECURITY.md](https://github.com/owasp/owasp-ctf-in-a-box/blob/main/SECURITY.md)** — ターゲットの脆弱性は意図的なものであり、対象外です。
## ライセンスとクレジット
MIT — [LICENSE](https://github.com/owasp/owasp-ctf-in-a-box/blob/main/LICENSE) を参照してください。`scorer/rubric.owasp/` 配下のルーブリックコンテンツは、上流の
[OWASP-CTF](https://github.com/OWASP-CTF/dc34-owasp-secure-development-ctf)
イベントからベンダリングされ、`scorer/rubric.owasp/PROVENANCE.md` のコミットに固定されています — このキットが存在するのは、あのイベントが一度ならず開催する価値があったからです。脆弱なターゲットはベンダリングされていません。イベントはそれぞれの上流からフォークします
([Juice Shop](https://github.com/juice-shop/juice-shop)、
[WebGoat](https://github.com/WebGoat/WebGoat)、
[DVWA](https://github.com/digininja/DVWA)、
[Security Shepherd](https://github.com/OWASP/SecurityShepherd)、
[VulnerableApp](https://github.com/SasanLabs/VulnerableApp)、
[VAmPI](https://github.com/erev0s/VAmPI))、そしてそれぞれが独自のライセンスを保持しています。
OWASP® は OWASP Foundation の登録商標です。このプロジェクトは同財団と提携しておらず、承認も受けていません。
| 対象 | チャレンジ数 | ポイント | 備考 |
|---|
vulnerableapp | 110 | 187 | 最大の対象; 8 並列で採点 |
webgoat | 69 | 137 | 2 段階ビルド: Maven、次にフォークのランタイム専用 Dockerfile |
dvwa | 55 | 108 | MariaDB のサイドカーとスキーマ初期化が必要 |
securityshepherd | 40 | 79 | HTTPS、3 コンテナスタック、厳密に直列 |
juice-shop | 38 | 141 | 難易度が 6 つ星に達する唯一の対象 |
vampi | 9 | 16 | 自己完結型; 最速のエンドツーエンド検証 |
| 合計 | 321 | 668 | すべてのイベントが 6 つすべてをプロビジョニング; /admin → Secure Development → Targets でサブセットを選択 |
| こんなときに読む | ドキュメント |
|---|
| キットを立ち上げるとき | docs/hosting.md — 前提条件、ウィザードとすべての個別ステップ、スコアがマシンに届く仕組み、GitHub OAuth アプリ、イベント設定 |
| クラウドにデプロイするとき | docs/aws.md(Terraform: ECS Fargate + ElastiCache + ALB) · docs/fly.md(Fly マシン 1 台) |
| 開場しようとしているとき | docs/security-checklist.md — 1 ページのイベント前ウォークスルー |
| イベントを運営するとき | docs/operations.md — チーム、管理パネル、クイズ/クラシック/ai の主催者ガイド、検証、teardown |
| システムを理解するとき | docs/architecture.md — 図、スコアデータフロー、Redis キー、セキュリティモデル、テスト戦略 |
| ルーブリックを書くとき | docs/scorer.md — serve + judge モード、両方のルーブリック文法、作成とビルド |
| 新しいモジュールを作るとき | docs/modules.md — プラットフォーム/モジュールの契約 |
| 「なぜこうなっているのか?」を問うとき | docs/decisions.md — 番号付き ADR |