
セキュリティ学習イベント向けのセルフホスト型CTFコントロールプレーン:チーム登録、ライブリーダーボード、およびpatch-to-score、クイズ、jeopardy、AIチャレンジの各モジュールを1台のDocker Compose環境で提供。
セキュリティ学習イベントのためのセルフホスト型コントロールプレーン — 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 台のマシン — ノート PC、予備のデスクトップ、小さな VPS — 上の Docker Compose から実行され、 フォーク用に 1 つの無料 GitHub org があれば十分です。6 つのターゲットすべての ルーブリックがボックス内に同梱されているため、要求すべきプライベートイメージも、 書くべきスコアリングコードもありません。課金もなく、外部への通信もなく、 イベントが終わったらリポジトリをアーカイブしてスタックを停止するだけです。
対象者: このイベントを運営したいが、そのためにクラウドオペレーターに なりたくない人 — コース講師、クラブ主催者、OWASP チャプターリード、 ワークショップファシリテーター、社内トレーニングデーを運営するセキュリティチーム。
デプロイ済みでエンドツーエンドで検証済み。ただし実際のコホート向けにはまだ未実行。
完全なスコアリング経路はキット内に同梱されています — スコアラーの bearer 認証付き
POST /score、フォーク用の自己完結型スコアリングワークフロー、ポーリングトランスポート —
そして scripts/smoke.sh がそのパイプライン全体をモックに対して駆動します。さらに、
このキットはこのリポジトリが同梱するのと同じ Compose ファイルからホストされたボックス上で
継続的に稼働しており、GET /health はそれを提供している正確なリビジョンを報告し、
そのライブインスタンスに対するエンドツーエンドのパスで、多数の実際の欠陥が
発見・修正されました — モック化されたスイートでは見えない類のものです。
まだ起きていないのは実際のイベントです: 競技者のコホートが実際のフォークに対して 実際の PR を、同時に、何時間も開くこと。これが「パイプラインは動く」と 「パイプラインは 40 人で動く」の間のギャップです。2 つの注意点は埋もれさせず 明示されています: Security Shepherd の結果マッチャーには明記された残存限界があり (異常な言い回しの拒否が依然として solve と読まれる可能性がある — 正しいパッチを 過小評価することはあっても、無償の得点を与えることは決してない)、 完全なコホートの負荷プロファイルは未検証です。詳細と現在の状態: 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/dcotelo/owasp-ctf
cd owasp-ctf
./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、イベント org、管理者
ログイン、Secure Development を実行するかどうか、GitHub 認証情報 — そして
.env を書き込み、
自動化可能なすべてのステップを実行し、GitHub UI 上のステップを案内し、
中断して戻ってきても再開します。それ以外のすべて(イベント名、実行する
モジュール、対象)は実行時の /admin 設定なので、編集すべき設定ファイルは
ありません。実際に必要なものだけを尋ねます。Secure
Development のないイベントには org もフォークもスコアラーイメージも不要で、
それらについて尋ねられることはありません。変更を伴うステップは --dry-run で
プレビューできます — すでに完成した .env からステップ
4〜9 を説明し、管理者ログインがない場合、または Secure Development がオンで
org がない場合には(設計上)拒否します。ウィザードは
./setup/ctf-setup.sh doctor の実行で締めくくられます — これはいつでも再実行できる
フォークごとのステータスマトリクスです — その後、オプションの fly.io
デプロイ(デフォルトは no)を提供するので、同じイベントを公開ホスト名に載せるのも
ガイド付きのフロー — ホスト名、プレビューされたデプロイ、そして確認 — となり、
デプロイドキュメントを読み漁る旅ではなくなります。
詳細を知りたいですか? 個々のサブコマンド、各 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 org とスコアリング パイプラインが必要です。
Quiz — 単一選択および複数選択のセキュリティ問題で、回答した瞬間に
アプリ内で採点されます(複数選択は全問正解か 0 点か)、試行回数の上限と
再試行クールダウン付き。/admin から 1 問ずつ作成するか、1 つの JSON
バンドルとしてインポート・エクスポートできます。GitHub もフォークも
パイプラインも不要です。
Jeopardy — カテゴリ別に主催者が作成したフラグのボード。
提出はトリムおよび正規化され、フラグが大文字小文字を区別するよう
マークされていない限り(そのカードに明記されています)大文字小文字は
許容され、提出クールダウンとオプションの有料ヒントがあります。クイズと
同じ /admin + JSON バンドルでの作成です。こちらも GitHub は不要です。
AI — ボックスの外部でホストされるプロンプトインジェクションおよび ガードレールのチャレンジ。各参加者のチャレンジページは、外部サイトへの 個人用起動リンクを発行します。解答は、そのサイト自身のコールバックを 通じて、またはアプリに戻って入力されたフラグを通じて、リーダーボードに 報告されます。GitHub もフォークもパイプラインも不要です。
有効にしたモジュールの周囲に、プラットフォームは以下を提供します:キャプテン、
参加コード、/join/<code> リンクを備えたチームのセルフ登録(ソロプレイは
1 人のチーム。複数のチームメイトが解いたフラグは 1 回としてカウント)。
実際の解答ごとのタイムスタンプから生成される CTFd スタイルのスコア推移
グラフを備えたライブリーダーボード。許可リスト方式の /admin パネル —
フリーズ、スコアリングおよび登録の時間枠、ヒントとコスト、チーム上限、
クールダウン、モジュールコンテンツ、参加者ごとのサポートアクション、
アクティビティストリームとエンゲージメント指標 — すべて実行時で、
再ビルド不要。そしてすべての管理者アクションに対する上限付きの監査ログ。
| 参加者内訳 | チャレンジブラウザ |
|---|---|
![]() | ![]() |
| Jeopardy フラグボード | Quiz |
|---|---|
![]() | ![]() |
Captured from the contestant app running locally via scripts/dev-stack up
with seeded demo players. Targets and fork links are event-config driven; the
event name and the rest of its branding are admin-panel settings.
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 にあります。
このモジュールのコンテンツは、脆弱な対象のセットとその採点用
ルーブリックです。参加者は対象を選び、org のコピーをフォークし、
パッチを当て、PR を開きます。各対象のチャレンジは実行可能な node:test
スイートで、難易度によって価格が設定されています。
Counts are maintained by hand and pinned to the vendored rubric by
apps/web/src/lib/tests/apps-catalogue.test.ts — re-check them
after a vendor-rubric.sh bump. Reference patches
that prove a correct fix scores (the positive-direction gate) live separately
under patches/.
ルーブリックは scorer/rubric.owasp/ にあり、
OWASP-CTF/dc34-owasp-secure-development-ctf
からベンダリングされ、scorer/rubric.owasp/PROVENANCE.md に記録された
単一のアップストリームコミットに固定されています。より新しいコミットに対して
再ベンダリングするには:```sh
./scripts/vendor-rubric.sh --all --ref
2つのルーブリック形式が同時にサポートされており、1つのルーブリックディレクトリで両方を混在させることができます。`<target>.yaml` ファイルは宣言的なHTTPリクエスト/期待値プローブ文法を使用し、`<target>/tests/challenges/` ディレクトリは `catalogue.<target>.json` によって価格設定された実行可能テストを使用します。作成ガイド:
[docs/scorer.md](https://github.com/owasp/owasp-ctf/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 として 記録されています。
dcotelo.github.io/owasp-ctf でレンダリングされています。
コントリビューション歓迎です — 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/blob/main/SECURITY.md)** — ターゲットの脆弱性は意図的なものであり、対象外です。
## ライセンスとクレジット
MIT — [LICENSE](https://github.com/owasp/owasp-ctf/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 |