公開したWebアプリのセキュリティを Claude Code に点検してもらう
公開した瞬間から、狙われる対象になる。作りかけを人に見せた、身内に URL を送った、その時点で世界に開いている。「運用を始めたら考える」では遅い。だから公開したら、まず点検する。難しいことはしない。Claude に URL を渡して「チェックして」と頼むだけで、初心者のアプリで開きやすい穴はだいたい見つかる。
1. このプロンプトで点検する
Section titled “1. このプロンプトで点検する”新しいセッションを開いて、これを貼る。https://... のところは自分のサイトの URL にする。
このURLのセキュリティをチェックして: https://...
まず、このサイトが何でできているかを調べて(ホスティング・フロント・データの置き場所・外部APIの呼び出し先)。
そのうえで、前回の結果は使わず、いま実際にリクエストを送って確かめて。推測では答えないで。特にこの4つ:- 認証なしでデータベースが読めるか / 書けるか- 認証なしで叩けるAPIがあるか(とくにAIを呼ぶもの)- ログインなしで管理画面に入れるか- 課金が発生するものが無防備に置かれていないか
報告は「実際に確認した」と「コードから推測した」を分けて書いて。このプロンプトは、短いけれど1行ずつ意味がある。削ってよい行と、削ると点検が骨抜きになる行がある。以下では、なぜこの形なのかを行ごとに見ていく。自分で作り変えるときの手がかりにしてほしい。
2. なぜ「実際にリクエストを送って」と書くのか
Section titled “2. なぜ「実際にリクエストを送って」と書くのか”これを書かないと、Claude はコードや画面を眺めただけで「たぶん認証がかかっていません」「おそらく大丈夫です」と答えてしまう。
穴が開いているかどうかは、実際に試せば白黒がつく。合言葉を要求するはずのページに合言葉なしでアクセスして、中身が返ってくれば開いている。返ってこなければ閉じている。それだけの話で、推測の入る余地がない。
だから「実際にリクエストを送って確かめて」と明示して、眺めるのではなく、実際にリクエストを送らせる。報告が「実際に試したら中身が見えました」に変わって、はじめて直す気になる。
3. なぜ「確認した」と「推測した」を分けさせるのか
Section titled “3. なぜ「確認した」と「推測した」を分けさせるのか”Claude は、確かめていないことも自信ありげに書ける。「この API は認証で守られているはずです」と、試してもいないのに断定できてしまう。これをそのまま信じると、開いている穴を「閉じている」と読み違える。
分けて書かせると、確かめていないものを断定できなくなる。 「実際に確認した」の欄に書くには本当に試した事実が要るので、そこに書けないものは自然と「コードから推測した」へ回る。
読む側は、この2つを別のものとして扱う。「確認した」の欄だけを事実として受け取り、「推測」は当たりをつける材料に使う。
4. なぜこの4項目なのか
Section titled “4. なぜこの4項目なのか”初心者が公開するアプリで開きやすい穴は、だいたい決まっている。あれもこれもと網羅するのではなく、実害の大きい4つに絞って聞く。
- 認証なしでデータベースが読めるか、書けるか。 利用者の情報が丸ごと外から取れたり、勝手に書き換えられたりする。いちばん被害が大きい。
- 認証なしで呼び出せる API があるか。 とくに AI を呼ぶ API が無防備だと、他人にただ乗りされて使った分の料金だけがこちらに来る。
- ログインなしで管理画面に入れるか。 自分だけが開けるつもりの画面に、URL を知っていれば誰でも入れてしまうことがある。
- 課金が発生するものが無防備に置かれていないか。 上の API と重なるが、放っておくと青天井で請求が伸びる種類の穴を、金額の面から念押しする。
この4つに「該当なし」と言えるなら、初心者のアプリとしては大きな穴は開いていない。
5. 作った本人のセッションで点検しない
Section titled “5. 作った本人のセッションで点検しない”点検は、そのアプリを作ったのとは別の、まっさらなセッションで頼む。プロンプトが URL から始まっていて、コードの中身を渡さないのはそのためになる。
作ったセッションは「自分はこう組んだ」という前提を持っている。だから外から疑って試す動機が弱く、「ここは認証で守ったはずだ」と自分の記憶を確かめずに書きがちになる。
URL だけを渡された素のセッションは、その前提を持たない。 手元にあるのは公開されている URL だけなので、攻撃する人と同じ立場から、素直に試して確かめる。作った本人には見えなかった穴が、ここで出てくる。
同じ理由で、一度点検した会話で頼み直すのも避ける。前に調べた結果をそのまま覚えていて、試し直さずに返してくることがある。プロンプトに「前回の結果は使わず、いま実際にリクエストを送って確かめて」と入れてあるのは、この念押し。
6. 報告はどう返ってくるか
Section titled “6. 報告はどう返ってくるか”点検を頼むと、どんな文面が返ってくるのか。先に知っておくと、自分のときに慌てない。
まず、よくある誤検知を2つ覚えておく。1つめは「Firebase の API キーがソースコードに露出しています。危険です」という指摘。ほとんどの場合まちがい。あれは公開されることを前提にした値で、隠しても何も変わらない。正しい対処は、キーを隠すことではなくデータベース側のルールで閉じること。ここを鵜呑みにすると、キーを隠して「直した」つもりになり、本当の穴は開いたままになる。
2つめは「フロントの JavaScript が誰でも読めます。作りの中身が見えています」という指摘。これもたいていまちがい。ブラウザで動く JavaScript は、配った時点で誰でも読めるのが普通で、隠す手立てもない。だから、フロントに読まれて困るものを置かないのが正解になる。合言葉や API キーのような秘密はサーバ側に置く。フロントのコードが読めること自体は、穴ではない。
どちらの誤検知も、そのまま作業指示として受け取らず、中身を一度確かめる。
報告そのものは、たいてい拍子抜けするくらい静かに返ってくる。問題がなければ、4項目が並んで「該当なし」がずらりと続く。
- 認証なしでデータベースが読めるか / 書けるか → 該当なし(データベースを持たない静的サイト)- 認証なしで叩けるAPIがあるか → 該当なし- ログインなしで管理画面に入れるか → 該当なし(管理画面が存在しない)- 課金が発生するものが無防備に置かれているか → 該当なしこの「該当なし」ばかりの報告が、いちばん良い状態。何も起きていないのではなく、狙える口がそもそも無い、という意味になる。拍子抜けして「ちゃんと点検できていないのでは」と不安になる必要はない。
穴があるときは、この静かな並びの中に、確かめた事実として一行が立つ。
- ログインなしで管理画面に入れるか → 実際に確認:合言葉を要求するはずのページに、 URLの書き方を少し変えるだけで合言葉なしで入れた。中身が返ってきた「実際に確認」と付いた一行は、推測ではなく試した結果。ここが出たら直す。
7. 見つかったら、どの順で直すか
Section titled “7. 見つかったら、どの順で直すか”穴が複数出ると、どれから手をつけるか迷って動けなくなる。順番を決めておく。お金、復旧、信用の順で見る。
- お金: 勝手に使われて課金が発生するか。伸び続けるので最優先。
- 復旧: 壊されたとき、どこから戻せるか。戻せないなら、そこがいちばん痛い。
- 信用: 漏れたとき、誰に謝ることになるか。預かっているのが自分ではなく相手の情報なら、重くなる。
そのうえで、各項目を「今日できる止血」と「あとで直す本命」に分ける。混ぜると、本命の大きさに怯んで何も動けなくなる。たとえば AI を呼ぶ API が無防備なら、今日できる止血は使う金額の上限を決めておくこと(アプリが呼んでいる API の提供元のコンソールで、月額の上限を設定する。Claude の API を使っているなら Claude Console で、前払いにして自動リロードを切るのが一番手軽。ここは自分の Claude 契約の課金とは別枠なので、間違えないように)、あとで直す本命は認証を足すこと。本命に数日かかっても、上限さえあれば被害額は頭打ちになる。
8. 作る前の備えは別の記事に
Section titled “8. 作る前の備えは別の記事に”この記事は「作った後」の点検の話。作る前に地雷を避ける備えは、公開するWebアプリの地雷を避けるにまとめてある。Claude 側に安全のルールを守らせておく、という話で、こちらと対になる。
作る前に守らせて、作った後に確かめる。
9. やってみた
Section titled “9. やってみた”点検が本当に穴を拾うのか。実際に、自分が関係する新旧のサイトを10個ほど点検してみた。多くは「該当なし」ばかりで返ってきて、穴が出たのは2つ。
このサイトの記事で紹介した作例を点検したら、鍵をかけたはずのページが素通りだった。 Basic 認証をかけたページに、URL の書き方をほんの少し変えただけでアクセスできた。合言葉を聞かれないまま中身が返ってくる。作ったときには気づいていなかった穴で、点検して初めて見つかった。修正済み。
共有ランキングのあるゲームのアプリでは、もっと深い穴が出た。 ランキングに、利用者を見分けるための ID が全員分そのまま表示されていた。ところがそのアプリは、書き換えのときに「その ID を知っているか」だけで本人と認めていた。つまり、ランキングに ID を出した瞬間に、全員の鍵を配ってしまっていた。ランキングから拾った ID を使えば、他人になりすまして記録を書き換えられる状態だった。作った本人は、ランキングを「見せるだけの画面」だと思っていて、そこに鍵が載っているとは考えていなかった。
どちらも、作った本人には見えていなかった。
点検のやり方そのものでも、ひとつ気づいたことがある。上の穴を直したあと、同じ会話でもう一度点検を頼んだら、「まだ空いています」と古い報告がそのまま返ってきた。前に調べた結果を覚えていて、試し直さずに答えたからになる。別の新しい会話で頼み直したら、今度はちゃんと試して「塞がっています」と返ってきた。点検は、そのつど新しい会話で頼む。冒頭のプロンプトに「前回の結果は使わず、いま実際にリクエストを送って確かめて」と入れてあるのは、このため。
サイトを公開したら、まず冒頭のプロンプトで確認するようにしよう!