Ana içeriğe geç
友田 陽大
Running the app you built with AI
バイブコーディング
セキュリティ
認証・認可
個人開発
Next.js

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

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

Published
Reading time
9 min read
Author
友田 陽大
Paylaş

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

それでも、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で作ったアプリを公開する前に にまとめています。

Sıkça sorulan sorular

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

Kaynaklar

友田

友田 陽大

Bakanlık (METI) Ödülü kazanan bir ürünün geliştiricisi. TypeScript + Python + AWS ile SaaS, endüstriyel dijital dönüşüm ve üretime hazır üretken yapay zekâyı (RAG) gereksinimlerden altyapıya ve işletmeye kadar tek başıma uçtan uca sunuyorum.

Already live, or handling money or personal data?

An engineer reads your AI-built app and tells you what is actually dangerous

No coding knowledge required. I read the repository, list what to fix first in priority order, and hand back a report written for non-engineers plus fix instructions you can paste into your AI tool. I build production B2B SaaS with Claude Code myself, so I am not going to tell you that using AI was the mistake.

Hem proje bazlı işler hem de teknik danışmanlık için uygunum. 30 dakikalık ücretsiz bir görüşmeyle başlayın.

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

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

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

Bunlar da ilginizi çekebilir