メインコンテンツへスキップ
友田 陽大
AIで作ったアプリの運営
バイブコーディング
セキュリティ
認証・認可
個人開発
Next.js

AIが作ったログイン機能は本物か — 「ログインしてる風」と本物の権限チェックの違い

ログイン画面があるのに他人のデータが見えてしまう——AI生成アプリで最も多い脆弱性を、非エンジニア向けに解説します。URLの番号を書き換えるだけで確認できる手順、OWASPがAPIリスク第1位に挙げる理由、そしてAIに貼れる修正指示まで。

公開日
読了時間
9分
著者
友田 陽大
シェア

ログイン画面があります。パスワードも要求します。ログインしないと何も表示されません。

それでも、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回やってください。

手順

  1. テスト用アカウントを2つ作る(Aさん・Bさん)
  2. Aさんでログインして、何かデータを作る(投稿、注文、メモ、なんでも)
  3. そのデータの詳細画面を開き、URLを控える。番号やIDが入っているはずです
  4. ログアウトして、Bさんでログインする
  5. 控えた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ポリシーになっていれば、自分で自分を管理者にできます。

正しい形

  1. 管理者判定はサーバー側で行う
  2. 管理者用のAPIすべての入口で権限を確認する(1箇所でも漏れると意味がない)
  3. 権限の情報はユーザーが書き換えられない場所に持たせる(データベースの列+書き込み禁止の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で作ったアプリを公開する前に にまとめています。

よくある質問

認証と認可は何が違うのですか?
認証(Authentication)は「あなたが誰か」を確かめること、認可(Authorization)は「あなたがそれをしてよいか」を確かめることです。ログイン画面は認証です。認証が済んでいても、そのユーザーが他人の注文を見てよいかは別の判断で、それが認可です。AIが書き漏らすのはほぼ常に認可のほうです。
画面に他人へのリンクが出ていなければ安全ではないですか?
安全ではありません。リンクが表示されていなくても、URLを直接入力すればリクエストは飛びます。ブラウザの開発者ツールを使えばアプリが送っている通信の形も分かるので、URLを組み立てることは誰にでもできます。「画面に出ていない=アクセスできない」は成立しません。
自分で確認する方法はありますか?
あります。テスト用アカウントを2つ作り、Aさんでログインしてデータを作り、そのURL(番号やIDを含むもの)を控えます。ログアウトしてBさんでログインし、控えたURLをアドレスバーに直接入力してください。Bさんの画面にAさんのデータが表示されたら脆弱性です。エラーや「権限がありません」になれば正常です。
RLSを設定していれば認可チェックは不要ですか?
不要にはなりません。RLSはデータベース層の防御、サーバー側の所有者チェックはアプリケーション層の防御で、守る範囲が違います。たとえば service_role キーを使う管理処理はRLSを素通りするため、そこではサーバー側チェックだけが防御になります。両方あるのが正しい状態です。
管理者機能はどう守ればいいですか?
管理者かどうかの判定を必ずサーバー側で行い、管理者用のすべての入口で権限を確認します。判定材料はユーザーが書き換えられない場所(データベースの列やIDトークンのカスタムクレーム)に持たせてください。メニューから管理者リンクを隠すだけでは、URLを知っている人は入れてしまいます。

参考文献

友田

友田 陽大

経済産業大臣賞 受賞プロダクト開発者。TypeScript + Python + AWS で、SaaS・業界DX・実用レベルの生成AI(RAG)を、要件定義からインフラ・運用まで一人で完遂します。

すでに公開している/お金や個人情報が動いているなら

AIで作ったアプリを、エンジニアが読んで危険な箇所を教えます

コードが読めなくても大丈夫です。リポジトリを見て「今すぐ直すべきこと」を優先度つきで洗い出し、非エンジニア向けのレポートと、AIにそのまま貼れる修正指示をお渡しします。私自身 Claude Code で本番のB2B SaaSを作っている実務者なので、AIで作ったこと自体を否定はしません。

プロジェクト単位(請負)・技術顧問のどちらにも対応可能です。まずは30分の無料技術相談から。

最短ルート:カレンダーから直接予約

相談内容が固まっている方は、フォーム送信よりその場で日程を確定する方がスムーズです。下記から空き時間をお選びください。

  • 30分のオンライン無料相談
  • Google Meet / Zoom / Microsoft Teams
  • NDA 商談前締結可・無理な営業はいたしません
無料相談の空き枠を予約する

あわせて読みたい