結論から
「ログインできる」と「それを見ていい」は、別の確認です。前者を認証(authentication:あなたは誰か)、後者を認可(authorization:あなたに何が許されるか)と呼びます。AIで作ったアプリの穴で一番多いのは、認証はあるのに、認可が無い形です。ログイン画面は立派。でも、ログインさえすれば、他人の予約も、管理者のページも、見えてしまう。
この記事で伝えたいのは、この一点だけです。ログインは「入口の確認」。その後の一操作ごとに「この人のものか」「この人に許されているか」を確認するのが認可。AIは入口は作るが、一操作ごとの確認を忘れる。 OWASP Top 10 では、この「認可の抜け」(Broken Access Control)が2021年版・2025年版と連続で1位です。
- 何が起きるか:ログインした佐藤さんが URL の数字を変えると、田中さんの予約が見える。一般ユーザーが
/adminを開くと、管理画面が出る。ログインは通っている。だから「誰でも見られる」わけではない。でも「ログインした誰でも見られる」 - なぜ:AI は「ログインしていなければ弾く」(認証)は書く。でも「その予約は本人のものか」「この人は管理者か」(認可)は、頼まれない限り書かない。動くので気づかない
- 直す:データを返す・変える処理すべてで、「誰のものか」と「誰に許されているか」をサーバー側で確認する。Supabase なら RLS、自前 API なら
where user_id = 本人と役割チェック - 確かめる:2つのアカウントを作り、A でログインしたまま B のデータの URL を開く。一般ユーザーで
/adminを開く。見えたら ✗
動画版(約2分半)は本文の上に埋め込んであります。
① 何が起きるのか:ログインは通った。でも、他人のものが見える
例として、AIで作った予約アプリがあるとします。ログイン画面があり、メールとパスワードで入ります。ログインしていない人が /reservations/12 を開くと、ログイン画面に飛ばされます。ここまでは正しい。認証は効いています。
では、佐藤さんがログインして、自分の予約 /reservations/12 を開いたあと、URL を /reservations/13 に変えたら。13番は田中さんの予約です。
田中さんの予約が、そのまま表示されます。
サーバーは「ログインしているか」は確認した。でも「12番・13番は、この人のものか」は確認していない。ログインした誰でも、全員の予約が見られる状態です。同じことが /admin でも起きます。「ログインしているか」だけ見ているので、一般ユーザーがログインしたまま /admin を開くと、管理画面が出る。
これが「認証はあるが、認可が無い」状態です。以前の記事で扱った「URL の数字を1つ変える」「シークレットウィンドウで /admin」「RLS が無い」は、全部この一種類の穴の、別の顔です。
② なぜ起きるのか:AIは「入口」は作るが、「一操作ごと」は忘れる
認証と認可は、確認する場所が違います。
認証(あなたは誰か) は、入口で1回。ログイン画面、パスワード、セッション。AIに「ログイン機能を付けて」と言えば、ほぼ必ず作られます。目に見える機能だからです。
認可(あなたに何が許されるか) は、データに触る一操作ごと。「予約12を見る」「予約13を消す」「全ユーザー一覧を出す」。そのたびに「この予約はこの人のものか」「この人は管理者か」を確認する。目に見えない、地味な1行です。
AI はどう書くか。多くの場合、「ログインしていなければ弾く」という共通の処理(middleware)を1つ書いて、それで済ませます。それは認証です。各操作の中に where user_id = ログイン中の本人 や if (!user.isAdmin) return 403 を入れるかどうかは、頼まれなければ入れないことが多い。入れなくても、自分で試す限り「自分のデータは見える」ので、動いて見えます。
OWASP Top 10 の 2021年版で「Broken Access Control(認可の不備)」は初めて1位になり、2025年版でも1位のままです。OWASP のデータでは、テストされたアプリの大多数で何らかの認可の不備が見つかっています。プロが作っても抜ける場所で、AI はもっと抜けます。
さらに、AI の認可にはもう1つの落とし穴があります。AI にデータベースを触らせるとき(→ プロンプトインジェクションの記事、AI エージェントの記事)、AI 自身の権限を絞るのも認可の話です。「AI は本人の行しか読めない」を設計しないと、AI が全員のデータを返してしまいます。
③ どう直すのか:AIに「一操作ごとに確認して」と頼む
以下をそのまま AI ツールに貼ってください。
「このプロジェクトで、データを読む・作る・更新する・削除する API エンドポイント(または Server Action・RPC)をすべて列挙してください。それぞれについて、①ログインしているかの確認(認証)、②そのデータがログイン中のユーザー本人のものかの確認(所有者チェック)、③管理者専用の操作なら役割の確認(ロールチェック)が、サーバー側で行われているかを表にしてください。抜けている箇所は、すべて追加してください。Supabase を使っている場合は、各テーブルの RLS ポリシーが auth.uid() で本人の行に絞られているかも確認してください。」
ポイントは2つ。「すべて列挙」と「表にして」(抜けが目で見える)。そして「サーバー側で」(画面でボタンを隠すのは認可ではない。→「動く」と「安全に使える」の記事)。
④ 直ったかを確かめる:アカウントを2つ
- アカウント A・B を作る。A で予約を1つ作り、その URL(
/reservations/xx)を控える - B でログインしたまま、A の URL を開く。A の予約が見えたら ✗。「見つかりません」「403」なら ◯
- B で、A の予約を消す・変える操作を試す(ボタンが無ければ、A の画面で押したときの通信を開発者ツール → Network で見て、同じものを B で送る)。通ったら ✗
- 一般ユーザー(B)で `/admin` を開く。管理画面が出たら ✗
- Supabase なら、ダッシュボード → Authentication → Policies で、全テーブルに RLS が有効か、ポリシーに
auth.uid()が入っているか - 分からなければ、AIに「ユーザー B がユーザー A のデータを読めない・変えられないこと、一般ユーザーが管理者 API を呼べないことを確認するテストを書いて」
自分で直せないとき:エンジニアに送る一文
「ログインしていれば、URL を変えるだけで他のユーザーのデータが見えます(認可がありません)。全エンドポイントの所有者チェックと役割チェックをサーバー側で追加し、Supabase の RLS ポリシーも確認してください。」
よくある質問
画面上では、他人のデータへのリンクは出していません。それでも危ないですか。
危ないです。リンクが無くても、URL は誰でも打てます。「画面で隠す」は見た目、「サーバーで確認する」が認可です。
認証さえしっかりしていれば(2段階認証など)、認可は後回しでいいですか。
順番が逆です。2段階認証は「本人であること」を強くするだけで、「本人が他人のデータを見られる」穴は塞ぎません。認可の抜けは、ログインした普通のユーザーが、悪意なく URL をいじるだけで踏みます。
「ロール」と「所有者」は何が違いますか。
所有者チェックは「この行はこの人のものか」(予約・注文・メッセージ)。ロールチェックは「この操作はこの種類の人に許されているか」(管理者だけが全件を見る・返金する)。両方要ります。管理者機能があるなら、ロールの確認は必須です。
この穴が塞がれば、データは安全ですか。
「誰が何を見られるか」は正しくなります。「命令を混ぜられる」(→ SQL インジェクション)、「他サイトから押される」(→ CSRF)、「Cookie を盗まれる」(→ セッションハイジャック)は別の穴です。ただ、AI 製アプリでは認可の抜けが頻度・被害ともに最大です。ここから確認してください。


