Claude CodeでWebアプリに管理画面を追加してBasic認証でアクセス制限する
このハンズオンはD1ハンズオン(GitHub Actions版はこちら)の続編。前回作った匿名一行掲示板に、管理者だけが使える管理画面を追加する。アクセス制限は「合言葉をひとつ決めて、知っている人だけが入れる」という最も手軽な方式で実現する。
本記事は Claude Code を例に進めるが、プロンプトを渡す相手はまとめてエージェントと呼ぶ(Codex など他のコーディングエージェントでも、同じプロンプトで進められる)。
1. このハンズオンで作るもの
Section titled “1. このハンズオンで作るもの”1-1. 作るもの
Section titled “1-1. 作るもの”DBを使うWebアプリには、DB管理という問題が付きまとう。掲示板なら「不適切な投稿を削除したい」「スパムを非表示にしたい」といった操作が必要になる。そのたびに SQL を直接叩くのは手間だし、ミスのリスクもある。
こうした管理操作はWebアプリの中に管理画面として用意しておくのが定石。必要な操作をボタンひとつでできるようにしておけば、後の運用がぐっと楽になる。
ただし、管理画面は誰でもアクセスできてはまずい。一般ユーザーが投稿を操作できてしまう。特定の人(管理者)だけが使えるように制限する必要がある。
ということで、このハンズオンでは、前回作った掲示板に以下を追加する。
- 管理画面(
/admin):投稿の表示/非表示を切り替えられる - アクセス制限:Basic認証(合言葉ひとつ)でその管理画面を守る
前回作った掲示板
今回作る管理画面
1-2. 合言葉ひとつで守る:Basic認証
Section titled “1-2. 合言葉ひとつで守る:Basic認証”今回のアクセス制限は、いちばん手軽なBasic認証を使う。仕組みは単純で、サーバーに合言葉(パスワード)をひとつ置いておくだけ(単一共有シークレット方式)。保護されたページを開くと、ブラウザがユーザー名とパスワードを尋ねるダイアログを出し、入力された値をヘッダに載せてサーバーへ送る。サーバーはそれが置いてある合言葉と一致するかを照合する。ユーザー登録もメール認証も不要で、個人を識別することもない。合言葉を知っていれば誰でも入れるし、漏れたら合言葉を変えるしかない。
ブラウザが出す Basic 認証のログインダイアログ(ユーザー名 admin + パスワード)。この画面で守るのが今回のゴール
Basic認証はブラウザに昔から組み込まれている標準の仕組みなので、HTML側の実装はゼロ・サーバー側も「ヘッダの値を環境変数と照合するだけ」で済む。防御の強さは「合言葉が推測不能であること」がすべて。自分だけが使う管理画面を守るには、これで十分。
その代わり、ログイン画面はブラウザ固定のダイアログで見た目を変えられず、ログアウトも事実上できない、といった不便もある。これらを解消したい場合や、「メールアドレス単位で制限したい」「一般ユーザーそれぞれにアカウントを持たせたい」といった要件が出てきた場合は、別の守り方がある。最後の章でまとめて案内する。
1-3. 全体の流れ
Section titled “1-3. 全体の流れ”このハンズオンでやることの全体像。
- D1 スキーマ変更(エージェント)
- posts テーブルに hidden カラムを追加
- バックエンド追加(エージェント)
- 管理用 API を追加、公開側 API で hidden 投稿を除外
- フロントエンド追加(エージェント)
- /admin ページを作成
- Basic認証で守る
- 実装 → デプロイ → ADMIN_PASSWORD 設定 → 動作確認
- もっと別の守り方もある(次のステップの案内)
ブラウザでの手作業は環境変数の設定だけ。実装はすべてエージェントにプロンプトで依頼できる。
前提:作業フォルダは前回のハンズオン(D1ハンズオン / GitHub Actions版)で作った
~/claude/my-bbs。
このハンズオンのプロンプトは、掲示板の標準構成(投稿テーブル
posts/公開API/api/posts)を前提にしている。D1ハンズオンでエージェントが別の名前(messagesなど)で作っていた場合は、その名前に読み替える。プロンプトに「このアプリの投稿テーブルとAPIパスに合わせて」と添えれば、エージェントが既存コードを読んで合わせてくれる。
2. 【データベース】スキーマ変更
Section titled “2. 【データベース】スキーマ変更”「投稿を非表示にする」機能を実装するには、投稿ごとの「表示/非表示」状態をDBに保存する必要がある。エージェントに「投稿を非表示にできる管理画面を作りたい」と相談すると、hidden カラムの追加が提案される。ここではその手順を先に進める。
マイグレーションファイルの作成をエージェントに依頼する。
投稿テーブル(掲示板なら posts)にhiddenカラム(INTEGER型、デフォルト0)を追加するマイグレーションファイルを作成して。テーブル名は既存のスキーマに合わせて。ファイル名はmigrations/0002_add_hidden.sqlで作成されたファイルを確認する。内容はこのようになっているはず。
ALTER TABLE posts ADD COLUMN hidden INTEGER NOT NULL DEFAULT 0;マイグレーションファイルは wrangler.jsonc の migrations_dir で指定したフォルダに置く。GitHub Actions 方式なら push 時に自動適用される。Git連携方式なら push 前に npx wrangler d1 migrations apply my-bbs-db --remote を手元から実行する。
3. 【バックエンド】管理画面用 API の追加
Section titled “3. 【バックエンド】管理画面用 API の追加”管理操作のAPIは /api/admin/ 配下に置く。後の章で /api/admin/ 配下をまとめてアクセス制限するため、公開側API(/api/posts)と区別しておくのがポイント。
エージェントに依頼する。
既存の投稿API(掲示板なら /api/posts)と投稿テーブルに合わせて、以下の3点をfunctions/api/配下に追加・修正して。エンドポイント名・テーブル名は既存のものに揃えて。
1. GET /api/admin/posts を追加する。hiddenの値にかかわらず全投稿を返す2. PATCH /api/admin/posts/:id を追加する。リクエストボディのhiddenフィールド(0か1)で投稿テーブルのhiddenカラムを更新する3. GET /api/posts でhidden=0の投稿だけを返すようにする公開側の /api/posts からは非表示投稿が消え、管理用の /api/admin/posts では全投稿(非表示も含む)が見える、という分担になる。
4. 【フロントエンド】管理画面の作成
Section titled “4. 【フロントエンド】管理画面の作成”エージェントに依頼する。
管理画面(public/admin/index.html)を作って。GET /api/admin/posts で全投稿を一覧表示して、各投稿の表示/非表示を切り替えられるようにしたい。一覧はテーブルで、各行にボタンをつけて。非表示中の投稿はグレーアウトして。ユーザー入力にはXSS防止の処置を入れる。シンプルなデザインで。これで管理画面はできたが、今はまだ誰でも操作できる状態。このまま公開してはいけない。次の章でアクセス制限をかけてからデプロイする。
5. Basic認証で守る
Section titled “5. Basic認証で守る”5-1. Basic認証とは
Section titled “5-1. Basic認証とは”Basic認証はブラウザに標準で備わっている認証の仕組み。保護されたページを開くと、ブラウザがユーザー名とパスワードを尋ねるダイアログを出す。入力した値はリクエストのたびにヘッダに載ってサーバーへ送られ、サーバー側で照合される。
sequenceDiagram
participant B as ブラウザ
participant CF as Cloudflare Pages
B->>CF: /admin にアクセス
CF-->>B: 401(認証してね)
Note over B: ユーザー名とパスワードの<br>ダイアログを表示・入力
B->>CF: 同じURLに再リクエスト<br>(ユーザー名とパスワードをヘッダに載せる)
Note over CF: ADMIN_PASSWORD と照合
alt 一致
CF-->>B: HTML/JS を返す(ここで初めて画面が見える)
else 不一致
CF-->>B: 401(ダイアログを再表示)
end
ブラウザが全部やってくれるので、HTML側の実装はゼロ。サーバー側も「ヘッダの値を環境変数と比べる」だけで済む。最小の手間でページもAPIもまとめて守れる。
5-2. 実装
Section titled “5-2. 実装”エージェントに依頼する。
管理画面(/admin 配下)と管理API(/api/admin 配下)へのアクセスに Basic 認証をかけて。パスワードは環境変数 ADMIN_PASSWORD から読んで。ユーザー名は admin で固定でよい。5-3. push してデプロイ
Section titled “5-3. push してデプロイ”変更をコミット・push する。
今回の変更をコミットしてpushしてGitHub の Actions タブ、または Cloudflare ダッシュボードの Deployments で進行状況を確認する。
この時点で /admin を開くと、認証ダイアログが出るがまだ入れない。サーバー側にパスワード(環境変数)を設定していないため。次へ進む。
5-4. 環境変数の設定
Section titled “5-4. 環境変数の設定”Cloudflare ダッシュボード → Workers & Pages → プロジェクト名 → Settings → Variables and Secrets を開く。
Add をクリックして以下を入力し、Save をクリック。Type は Secret を選んでおくと、後から値が画面に表示されなくなる。
| Variable name | Value |
|---|---|
ADMIN_PASSWORD | 任意のパスワード |
設定後、デプロイし直す(push するか、ダッシュボードから Retry deployment)と環境変数が反映される。
5-5. 動作確認
Section titled “5-5. 動作確認”本番 URL の /admin にアクセスすると、ブラウザの認証ダイアログが表示される。ユーザー名 admin と設定したパスワードを入力すると管理画面に入れる。表示/非表示の切り替えを試し、公開側のページから非表示投稿が消えることも確認する。
APIが守られていることも確認する。ブラウザのシークレットウィンドウで https://掲示板のURL/api/admin/posts を直接開き、認証を求められる(キャンセルすると401になる)ことを確認する。
これで管理画面は Basic 認証で守られた。自分だけが使う管理画面なら、ここで完成としてよい。しばらく使うと不便も見えてくる。ログアウトのボタンがどこにもない(ブラウザを完全に終了するまで認証されたまま)し、ログイン画面の見た目も変えられない。こうした不便を解消したくなったときや、守り方を変えたくなったときの選択肢を、次の章で案内する。
6. もっと別の守り方もある
Section titled “6. もっと別の守り方もある”Basic認証は「合言葉ひとつ・最小の手間」の入り口。自分だけが使う管理画面なら、ここまでで十分。ただ、用途が変わると別の方式が向いてくる。この記事では作らないが、見当をつけておくと選べるようになる。
| Basic認証(この記事) | トークン認証 | Cloudflare Access | |
|---|---|---|---|
| 実装・設定の手間 | 最小 | 少ない | 多い(初回のみ) |
| ログイン画面 | ブラウザ固定のダイアログ | 自前(自由にデザイン可) | Google のログイン画面 |
| ログアウト | 事実上できない | できる | できる |
| パスワードマネージャ | ブラウザ依存 | 対応させやすい | Google に委ねる |
| アクセス制限の単位 | 合言葉を知っているか | 合言葉を知っているか | メールアドレス単位 |
| 守れる範囲 | ページもAPIも | API(ページは公開) | ページもAPIも |
| 外部サービス | 不要 | 不要 | Google Cloud Console が必要 |
- ログイン画面を自前で作りたい・ログアウトを付けたい・パスワードマネージャにきれいに対応させたい → トークン認証(合言葉をヘッダで照合する自前実装。仕組みは Basic 認証と同じ「合言葉ひとつ」)
- メールアドレス単位で許可したい・Google アカウントの2段階認証で守りたい → Cloudflare Access を使うハンズオン。コードを書かずに「このメールアドレスだけ許可」がかけられる
- 管理者だけでなく、一般ユーザーそれぞれにアカウント(自分のページ)を持たせたい → 合言葉方式の守備範囲を超える。ユーザーごとのログインキーで本人を見分けるログインキーのアカウント管理や、パスキー+リカバリコードのアカウント認証へ
合言葉方式(Basic認証)の限界は「誰がアクセスしたか」を区別できないこと。管理者が自分ひとりなら何の問題もないが、複数人で個人を識別したくなったら上の Cloudflare Access やアカウント管理に進むとよい。
- 合言葉(パスワード)が漏れたら、Variables and Secrets の
ADMIN_PASSWORDを変えて再デプロイするだけでよい。単一共有シークレット方式の数少ない利点のひとつ - 環境変数の変更はデプロイし直すまで反映されない。push するか Retry deployment
- 管理者が複数いる場合は同じ合言葉を共有することになる。個人を分けたくなったら Cloudflare Access やアカウント管理へ(→ 6章)


