EmDashの初期設定は公開直後が危ない|誰でも管理者になれる時間を作らない
EmDashは公開した直後、最初の管理者を「誰でも」作れる状態になります。本体のプログラムを読んで分かった仕組みと、編集部が src/worker.ts に足した合言葉の門(初期設定APIを403で止める方法)を説明します。
EmDash を Cloudflare へ公開した直後のサイトは、まだ管理者が1人もいない状態です。このとき、最初に管理画面を開いた人が管理者を作れます。自分より先に他人が開けば、その人がサイトの持ち主になってしまいます。
本体のプログラムで確かめたこと
編集部は EmDash 本体の初期設定の部分(emdash/src/astro/routes/api/setup/)を読みました。
/_emdash/api/setup… サイト名などを受け取り、seed/seed.jsonの記事をデータベースへ入れる/_emdash/api/setup/adminと/_emdash/api/setup/admin-verify… 最初の管理者を作る(パスキーの登録)/_emdash/api/setup/status… 初期設定が済んだかを返す
どれも、初期設定が済んだ印(emdash:setup_complete)が付くまではログインなしで呼べます。済んだあとは Setup has already been completed で断られます。
つまり危ないのは、公開してから自分が管理者を作るまでの間だけです。
合言葉の門を足す
編集部は、雛形の src/worker.ts に数十行を足して、初期設定の入口を「合言葉を知っている人だけ」にしました。考え方は次のとおりです。
- 合言葉を Cloudflare のシークレット
SETUP_GATEに入れる(npx wrangler secret put SETUP_GATE) /_emdash/api/setupで始まるアドレス(statusを除く)は、合言葉が無ければ403を返す- 合言葉は、ヘッダー
X-Setup-Gateで渡すか、ブラウザで/_emdash/admin?setup_gate=<合言葉>を開いて cookie として受け取る
src/worker.ts の中では、元の処理(handler.fetch)を呼ぶ前に、次のように確かめています(一部)。
const isSetupApi =
url.pathname.startsWith("/_emdash/api/setup") && !url.pathname.startsWith("/_emdash/api/setup/status");
if (isSetupApi && (!secret || gateValue(request) !== secret)) {
return new Response("Forbidden", { status: 403 });
}
return (handler as any).fetch(request, env, ctx);シークレットを入れ忘れたときも止まる(!secret のとき 403)ようにしてあります。入れ忘れて門が開きっぱなしになるより、誰も通れないほうが安全だからです。
手元で確かめた結果
wrangler dev で動かし、合言葉なしで呼ぶと次のとおりでした。
POST /_emdash/api/setup→ 403POST /_emdash/api/setup/admin→ 403GET /_emdash/api/setup/status→ 200(状態を見るだけなので通す)- トップページ
/→ 200(読者の画面には影響しない)
合言葉をヘッダーに付けて /_emdash/api/setup を呼ぶと、seedComplete: true が返り、記事がデータベースへ入りました。記事の数が多いと1回で入りきらず seedComplete: false が返ることがあるので、その場合は true になるまで繰り返し呼びます。
管理者を作ったあと
管理者ができれば、初期設定は二度と開きません。門はそのまま残しておいても読者には影響しません。