ログイン画面があります。パスワードも要求します。ログインしないと何も表示されません。
それでも、URLの数字を1つ書き換えるだけで他人のデータが見える——AI で作られたアプリで最も多い脆弱性です。
しかもこれは、あなたが自分のアカウントで使っている限り、絶対に気づけません。完璧に正常に動くからです。
1. 認証と認可 — 2つの違う仕事
まず言葉を整理します。この2つが混ざっているのが、問題の根っこです。
認証(Authentication)= あなたが誰かを確かめる ログイン画面のことです。メールアドレスとパスワードで「確かにこの人だ」と判断します。
認可(Authorization)= あなたがそれをしてよいかを確かめる ログイン済みのAさんが、注文番号124(Bさんの注文)を見てよいか。これは別の判断です。
例えるなら、認証は社員証を見せて入館すること、認可は入った建物の中で、どの部屋に入れるかです。入館できたからといって、社長室に入れるわけではありません。
AI が書き漏らすのは、ほぼ常に 認可 のほうです。
理由はシンプルで、認証は「ログイン機能を作って」と頼めば作られる、目に見える機能だからです。一方の認可は、画面上に何も現れません。正しく実装されていても、されていなくても、見た目は同じです。
2. 何が起きるのか — 具体例
あなたのアプリに、注文詳細の画面があるとします。
https://あなたのアプリ.com/orders/123
123 はあなたの注文番号です。ここで 124 に書き換えます。
https://あなたのアプリ.com/orders/124
もし他人の注文が表示されたら、それが脆弱性です。
OWASP(Webセキュリティの標準的な団体)は、これを API1:2023 Broken Object Level Authorization(BOLA、オブジェクトレベル認可の不備) として、APIリスクの第1位 に挙げています。1位です。最も多く、最も影響が大きい。
なぜ「見えないリンク」では守れないのか
「画面に他人の注文へのリンクは出していないから大丈夫」——これが最も多い誤解です。
リンクが出ていなくても、URLを直接入力すればリクエストは飛びます。ブラウザの開発者ツールを開けば、アプリがどんな形の通信をしているかも分かるので、URLを組み立てることは誰にでもできます。
「画面に出ていない=アクセスできない」は成立しません。UIは飾りであって、鍵ではありません。
3. なぜAIが書き漏らすのか — 一覧と詳細の非対称
ここが面白いところで、AI は一覧画面はきちんと絞り込みます。
「ログイン中のユーザーの注文一覧を表示して」と頼まれれば、AI は「ログイン中のユーザーの」という条件を確かに使います。だから一覧には自分の注文しか出ません。
ところが「注文詳細を表示して」と頼まれると、番号で引いてくるだけのコードを書きがちです。
一覧: 「ログイン中の人の注文を全部持ってきて」 → 正しく絞られる
詳細: 「注文番号 123 を持ってきて」 → 誰のものか確認していない
詳細画面には「ログイン中のユーザーの」という条件が付いていない。頼まれていないからです。
そして——一覧が正しく絞られているので、動作確認では絶対に気づきません。自分のアカウントで一覧を開き、そこから詳細に飛ぶ限り、常に自分のデータしか出てこないからです。
この非対称が、AI 生成アプリでこの脆弱性が量産される理由です。
もっと危険な変種
同じ問題は、読み取りだけでなく更新や削除でも起きます。
- 他人の投稿を削除できる
- 他人のプロフィールを書き換えられる
- 他人の注文をキャンセルできる
読み取りより実害が大きいのに、確認されることは少ない箇所です。
4. 自分で確認する手順 — ブラウザだけでできる
コードを一切読まずに確認できます。公開前に必ず1回やってください。
手順
- テスト用アカウントを2つ作る(Aさん・Bさん)
- Aさんでログインして、何かデータを作る(投稿、注文、メモ、なんでも)
- そのデータの詳細画面を開き、URLを控える。番号やIDが入っているはずです
- ログアウトして、Bさんでログインする
- 控えたURLをアドレスバーに直接入力する
判定
- Aさんのデータが表示された → 脆弱性です。この記事の修正指示へ
- エラー、404、「権限がありません」になった → 正常です
応用: 更新・削除も試す
読み取りが守られていても、更新が守られているとは限りません。Bさんでログインした状態で、Aさんのデータの編集画面(/orders/123/edit のようなURL)も試してください。
さらに踏み込むなら、ログアウトした状態で同じURLを開いてみてください。ログイン画面にリダイレクトされれば正常です。データが見えたら、認証すら効いていません。
5. 何が正しい実装なのか
難しい話ではありません。データを取ってくるときに、必ず「ログイン中の人のものか」を条件に含める。それだけです。
【危険】番号だけで引く
「注文番号 123 を持ってきて」
【正しい】番号 + 所有者で引く
「注文番号 123 で、かつ 所有者が今ログインしている人のもの を持ってきて」
正しい形では、他人の注文番号を指定しても該当なしになります。「見つからない」と「権限がない」が同じ結果になるので、番号の存在自体も推測されません。
大事な原則: 「隠す」は防御ではない
フロントエンド側でボタンを非表示にする、メニューから項目を消す——これらは使いやすさの工夫であって、防御ではありません。
判定は必ずサーバー側で行います。ブラウザ側のコードは、利用者が自由に書き換えられるからです。
RLS との関係 — 二重にする
RLSを正しく設定していれば、データベース層でも同じ防御がかかります。では、サーバー側のチェックは不要でしょうか。
不要にはなりません。 守る範囲が違うからです。
- RLS = データベース層。どの経路から来ても効く
- サーバー側チェック = アプリケーション層。業務ルールを表現できる
たとえば service_role キーを使う管理処理は RLS を素通りします。そこではサーバー側チェックだけが防御になります。逆に、AIが新しいAPIを追加してチェックを書き忘れても、RLS があれば止まります。
両方あるのが正しい状態です。片方が抜けたときにもう片方が受け止める、という設計です。
6. 管理者機能 — もう一段の落とし穴
管理者向けの画面(全ユーザー一覧、削除、返金など)は、別の注意が必要です。
よくある間違い
メニューから管理者リンクを隠しているだけ。 URLを知っている人は入れます。
「管理者かどうか」をブラウザ側だけで判定している。 開発者ツールから状態を書き換えれば突破できます。
権限の判定材料を、ユーザーが書き換えられる場所に持たせている。 たとえば「ユーザーテーブルの is_admin 列」を、そのユーザー自身が更新できるRLSポリシーになっていれば、自分で自分を管理者にできます。
正しい形
- 管理者判定はサーバー側で行う
- 管理者用のAPIすべての入口で権限を確認する(1箇所でも漏れると意味がない)
- 権限の情報はユーザーが書き換えられない場所に持たせる(データベースの列+書き込み禁止のRLS、またはIDトークンのカスタムクレーム)
7. AIに貼る修正指示
確認と修正をまとめてAIに任せる場合の指示文です。
このアプリの、IDを受け取ってデータを返す/更新する/削除するすべての処理
(Route Handler、Server Action、API、ページのデータ取得)を洗い出して、
1つずつ次を確認し、表にして報告してください。
(1) サーバー側で「そのデータがログイン中のユーザーのものか」を検証して
いるか
(2) 検証していない場合、他人のIDを指定すると何が起きるか
(3) フロントエンドで表示を隠しているだけの箇所はどれか
そのうえで、検証していない箇所すべてに所有者チェックを追加してください。
データ取得のクエリ自体に所有者の条件を含める形(後からアプリ側で弾く
のではなく)にしてください。読み取りだけでなく、更新と削除も対象です。
あわせて、管理者向けの機能を全部洗い出し、それぞれサーバー側で権限
チェックが行われているか確認してください。権限の判定材料をユーザー
自身が書き換えられる状態になっていないかも確認してください。
私はコードが読めないので、各修正について「何ができてしまう状態だった
のか」を日本語で1行ずつ説明してください。
8. 修正したあと、もう一度確認する
修正後は、必ず手順4の確認をやり直してください。
AI は「修正しました」と報告しますが、実際に効いているかは別の話です。Bさんでログインして、AさんのURLを開く。エラーになれば直っています。
これは1分で終わる確認で、修正が本当に効いたかを判定できる唯一の方法です。「AIが直したと言ったから直っている」は、この領域では信用できません。
まとめ
- 認証(誰か)と認可(してよいか)は別の仕事。AIが書き漏らすのは認可
- URLの番号を1つ変えて他人のデータが見えるのは、OWASPがAPIリスク第1位に挙げる典型的な穴
- 一覧は正しく絞られるのに詳細は素通し、という非対称が起きる。だから動作確認では気づけない
- 確認はブラウザだけでできる。アカウント2つで、片方のURLをもう片方で開く
- 「隠す」は防御ではない。判定はサーバー側で、RLSと合わせて二重に
この項目は、RLSと並んで、公開前に必ず潰すべき4つのうちの1つです。全体像は AIで作ったアプリを公開する前に にまとめています。