記事一覧

EmDashの初期設定は公開直後が危ない|誰でも管理者になれる時間を作らない

EmDashは公開した直後、最初の管理者を「誰でも」作れる状態になります。本体のプログラムを読んで分かった仕組みと、編集部が src/worker.ts に足した合言葉の門(初期設定APIを403で止める方法)を説明します。

EmDashの初期設定は公開直後が危ない|誰でも管理者になれる時間を作らない

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 に数十行を足して、初期設定の入口を「合言葉を知っている人だけ」にしました。考え方は次のとおりです。

  1. 合言葉を Cloudflare のシークレット SETUP_GATE に入れる(npx wrangler secret put SETUP_GATE)
  2. /_emdash/api/setup で始まるアドレス(status を除く)は、合言葉が無ければ 403 を返す
  3. 合言葉は、ヘッダー 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 → 403
  • POST /_emdash/api/setup/admin → 403
  • GET /_emdash/api/setup/status → 200(状態を見るだけなので通す)
  • トップページ / → 200(読者の画面には影響しない)

合言葉をヘッダーに付けて /_emdash/api/setup を呼ぶと、seedComplete: true が返り、記事がデータベースへ入りました。記事の数が多いと1回で入りきらず seedComplete: false が返ることがあるので、その場合は true になるまで繰り返し呼びます。

管理者を作ったあと

管理者ができれば、初期設定は二度と開きません。門はそのまま残しておいても読者には影響しません。

関連記事