URLの数字を1つ変えてみる — 他人のデータが見えたら、サーバーが「本人か」を確認していない

AIで作ったアプリを公開する前の5分チェック①。URLの番号を1つ変えて他人のデータが見えたら、サーバーが本人確認をしていない状態。なぜ起きるのか、AIにどう頼んで直すのか、直ったかをどう確かめるのか。

動画で見る

結論から

AIで作ったアプリを公開する前に、自分の手でできる5分チェックの1つ目。URLの中の数字を1つ変えて開いてみてください。他人のデータが見えたら、サーバーが「本人かどうか」を確認していません。

この記事で伝えたいのは、この一点だけです。

  • 自分の予約・注文・プロフィールのページを開くと、URLに /reservations/12 ?id=12 のような番号が入っている
  • 12 を 13 に変えて開き、他人のデータが表示されたら未修正。まだ公開できない
  • 原因は、サーバーが「13番は、いまログインしている人のものか」を確認せず、頼まれた番号をそのまま返していること
  • 直すのはサーバー側の一行。AIへの頼み方と、直ったかの確かめ方を下に書いた

動画版(約2分)は本文の上に埋め込んであります。

① 何が起きるのか:数字を変えたら、他人の予約が見えた

例として、AIで作った美容室の予約アプリ(デモ用に用意したもの)で試します。

  1. 佐藤さんとしてログインし、自分の予約ページを開く。URLは .../reservations/12
  2. アドレスバーの 12 を 13 に書き換えて Enter
  3. 表示されたのは 田中さんの予約。氏名・メールアドレス・電話番号・「ご要望」欄まで、そのまま読めた
  4. 14 にすると別の人の予約で、「ご要望」欄には アレルギー情報 が書かれていた

このアプリは、予約する・変更する・確認する、すべての機能が問題なく「動いて」います。壊れているのは機能ではなく、「これを誰に見せてよいか」の判断です。

② なぜ起きるのか:理由は2つ

理由1:サーバーは、頼まれた番号を返しているだけ。

画面(クライアント)は「13番の予約をください」とサーバーに頼み、サーバーがデータを返しています。このとき、サーバーが「13番は、いまログインしている佐藤さんのものか」を確かめていなければ、13番でも14番でも同じように返してしまいます。前回の記事で見た「判断はサーバー側で」が、まさにここで抜けています。

理由2:URLは「お願い」であって、「鍵」ではない。

画面に13番へのリンクを出していなくても関係ありません。URLは誰でも自由に打てます。「リンクを置いていないから見えないはず」は、隠しているのではなく、「見に来ないでください」とお願いしているだけです。頼まれれば渡してしまう状態に変わりはありません。

この抜けは珍しいものではありません。Webアプリの代表的なリスクを毎年まとめている OWASP の「Top 10:2025」では、この種の「アクセス制御の不備」が第1位で、テストされたアプリの100%に何らかの不備が見つかったと報告されています。AIが書くコードでも同じで、Veracode が100以上のAIモデルに書かせたコードを検査した2025年の報告では、約45%にセキュリティ上の欠陥がありました。AIは「予約を表示する」という依頼は正しくこなしますが、「本人以外には表示しない」は、頼まれない限り書かないことが多いのです。

URLの番号を変えたとき、サーバーは「本人か」を確認しているか

③ どう直すのか:サーバー側の一行を、AIに頼む

直すのは、「この予約は、ログインしている本人のものか」をリクエストごとに確認する一行です。以下をそのまま AI ツール(Cursor / Claude Code / Lovable のチャットなど)に貼ってください。

「IDで取得するすべての処理(予約・注文・ユーザー情報・アップロードファイルなど)で、そのデータがログイン中のユーザーのものかをサーバー側で確認してください。他人のデータなら403を返し、存在しないIDなら404を返すようにしてください。フロントエンド側でIDを隠すだけの対応は不要です。対象になった処理の一覧も出してください。」

Supabase を使っている場合は、もう一段深い対策として RLS(行レベルセキュリティ) をテーブルごとに有効にし、「user_id = auth.uid() の行だけ読める」ポリシーを付けます。これは「AIに頼むための言葉」シリーズで別途扱います。

④ 直ったかを確かめる:同じ手順で、もう一度

「直しました」というAIの返事は、確認の代わりになりません。もう一度 `13` を開いてください。

  • 「表示できません」「見つかりません」などが出た → 合格
  • ログイン画面に戻された → 合格(ログアウト状態で試した場合)
  • さっきと同じ画面が出た → まだ直っていない

確認する場所は、数字のURLだけではありません。アップロードした画像のURL、請求書PDF、注文番号、招待リンク など、IDで取り出しているものはすべて同じ一行が必要です。先ほどのデモアプリでは、/uploads/license.jpg のようなURLを、ログインしていないシークレットウィンドウで開いたら、免許証の画像がそのまま表示されました。Supabase や Firebase のストレージも、設定によっては同じことが起きます(→ 個人情報の記事で詳しく)。

自分のアプリで試すときの手順は、①で見たとおりです。ログインして自分のデータのページを開く → URLの中の数字や長い文字列を見つける → 1つ変えて開く。別のアカウントか、シークレットウィンドウで 試すと、より確実です。

自分で直せないとき:エンジニアに送る一文

「なんとなく危ない気がする」では、依頼される側も何を見ればよいか分かりません。次の一文をそのまま送ってください。

「URLのIDを変えると他人のデータが見えます(例:/reservations/12 → /reservations/13)。本人確認をサーバー側に入れてください。画像URLやPDFなど、IDで取得している他の場所も同じ確認をお願いします。」

これだけで、直すべき場所が正確に伝わり、見積もりも具体的になります。逆に、この一文に「フロントでIDを隠せば大丈夫です」と返ってきたら、理由2をもう一度読んでください。その相手は避けたほうがよいです。

よくある質問

IDを推測しにくい長い文字列(UUID)にすれば、確認は不要になりますか。

推測は難しくなりますが、確認の代わりにはなりません。UUIDはメールの通知、共有リンク、ブラウザの履歴、スクリーンショットなどから漏れます。漏れた瞬間に同じ問題が起きるので、本人確認の一行は別途必要です。UUIDは「おまけの防御」と考えてください。

自分ひとりで使うアプリでも、この確認は必要ですか。

自分のデータしか入っていないなら、見えて困る相手がいないので優先度は低いです。ただし「あとで友人にも使ってもらう」「問い合わせフォームを付ける」と、他人のデータが入った瞬間に必要になります。公開の予定があるなら、最初から確認する習慣にしておくほうが早いです。

Lovable / Bolt / v0 で作りました。どこを見ればよいですか。

やることは同じです。デプロイ済みのURLを開いて、①の手順を試してください。多くの場合、データは Supabase に置かれています。その場合は Supabase の管理画面で対象テーブルの RLS が「Disabled」になっていないかも合わせて確認します。ここが赤い表示なら、URLを変える以前に、データベースそのものが外から読める状態です。

このチェックが通れば、公開して大丈夫ですか。

これは「公開前5分チェック」の1つ目にすぎません。管理画面がログインなしで開けないか、APIキーがフロントに書かれていないか、といった別のチェックが続きます。ひととおり自分で確かめたうえで、それでも不安が残る部分だけをエンジニアに見てもらうのが、いちばん費用対効果の高い順番です。VibeCode本番化では、そのための診断も行っています。