最初に結論を述べます。v0・Lovable・BoltといったAIアプリビルダーは「動くアプリ」を数分で吐き出しますが、その多くは『認証はするが認可しない』——つまりログインは実装されているのに、「その行を誰が読み書きしてよいか」の境界(Supabase RLS)が空いたまま公開されています。 そしてこれは仮説ではありません。Lovable製サイトのRLS不備は、未認証の攻撃者が任意のテーブルを読み書きできる欠陥として CVE-2025-48757(CWE-863・CVSS 9.3 CRITICAL) にNVDへ登録されています。
私自身、一人 × 生成AI(Claude Code)で本番SaaSを作ってきた側です。だからビルダーの速さは否定しません。むしろ言いたいのは逆で——公開済み・実ユーザーがいるなら、今日やるべきはエクスポート後のコードを「自分の責任で」検査することです。本記事は、その検査を scan → RLS手検査 → probe の3手に落とし込み、開発者の視点で実コード付きに示します。
なお、「AIが生成したコード一般の脆弱性」を4層(SCA/シークレット/SAST/DAST)で診る方法はAI生成コードの脆弱性診断【2026年版】で、発注者視点の本番化(ハードニング)はvibe codingの本番化ガイドで扱っています。本記事は**「ビルダー製 × 公開済み」に固有の穴**へ絞ります。
1. ビルダーは何を作り、何を作らないのか
まず、道具の守備範囲を正直に切り分けます。v0・Lovable・Boltは種類が違いますが、「あなたのアプリ固有の認可は作らない」という一点は共通です。
| ビルダー | 主に作るもの | 作らない/緩いままの部分 |
|---|---|---|
| v0(Vercel) | UIコンポーネント・画面 | データアクセスの認可、RLS、サーバー側の検証 |
| Lovable | フルスタック雛形+Supabase接続 | 行レベルの所有者スコープ(RLSポリシの中身) |
| Bolt | フルスタック雛形+DB接続 | テナント分離・IDOR対策・状態遷移の検証 |
ビルダーが自動で足してくれるのは、Aegisの言葉でいう 「水平の統制」に近い足回りと、ハッピーパスのUI・ログインです。ログイン画面が出て、サインアップが通り、自分のデータが表示される——ここまでは驚くほど速い。
問題は、その先の 「縦のリスク」 です。「ログインした利用者Aが、利用者BのIDを指す正規リクエストを送ったら、Bのデータが返らないか」。これは認証(誰であるか)ではなく認可(何をしてよいか)の問題で、アプリ固有の「誰が何を所有するか」に依存するため、ビルダーには原理的に埋められません。オブジェクト単位の認可欠陥(IDOR/BOLA)がOWASP API Security Top 10で2019年以来ずっと第1位であり続けているのは、ここが「人間が設計しないと閉じない」場所だからです。
2. 実例:CVE-2025-48757(Lovable)を一次情報で読む
抽象論で終わらせないために、NVDの一次情報を引きます。CVE-2025-48757の説明は、原文でこうです。
"An insufficient database Row-Level Security policy in Lovable through 2025-04-15 allows remote unauthenticated attackers to read or write to arbitrary database tables of generated sites." (Lovable(2025-04-15まで)の不十分な行レベルセキュリティポリシにより、リモートの未認証攻撃者が、生成されたサイトの任意のデータベーステーブルを読み書きできる)
要点を表に落とします(出典: NVD — CVE-2025-48757)。
| 項目 | 値 |
|---|---|
| CWE | CWE-863 Incorrect Authorization(不適切な認可) |
| CVSS 3.1 | 9.3 CRITICAL(AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:L/A:N) |
| 影響 | Lovable through 2025-04-15 |
| 公開日 | 2025-05-29 |
| 状態 | Disputed(ベンダーが異議) |
読み解くべきは3点です。
- 前提条件が「未認証(PR:N)」。つまりログインすら不要で任意テーブルに手が届いた。これはまさに「認証の外側で認可が空いている」典型で、『認証はするが認可しない』の構造そのものです。
- 根本原因が「不十分なRLSポリシ」。RLSを完全に切っていたか、
USING (true)のように全開のポリシだったかは事案ごとに違いますが、いずれも「行を所有者に絞る」設計が欠けていた、という一点に収束します。 - 状態が Disputed ——ベンダーは「生成されたアプリのデータ保護は各利用者の責任だ」と主張しています。これは逃げ口上ではなく、責任境界の宣言として読むべきです。ビルダーは足回りを作るが、行レベルの認可は「あなたの責任」——CVEが公式に、そう言っているのです。
だからこそ、次章以降の「自分で検査する」が要になります。
3. なぜビルダー製アプリは、ここで漏れるのか
構造的な理由は3つに整理できます。
① 「ログインが動く」が「安全そう」を演出する
Supabase Authを繋いでサインイン画面が出た瞬間、多くの人は「認証できたから守られた」と感じます。しかし認証は入り口の身元確認にすぎません。入館証を配ることと、各部屋の施錠は別の仕事です。RLSという施錠が甘いと、正規の入館証(有効なログイン、時には匿名アクセス)で全部屋に入れてしまう。
② anonキーは「クライアントに出る前提」で配られる
Supabaseの匿名(anon)キーは、ブラウザに埋め込まれる前提の公開キーです。それ自体は設計通りで、露出=脆弱ではありません。 危険なのは「RLSが正しくないまま anonキーが世界に配られている」状態です。守りの境界はキーの秘匿ではなく、RLSポリシの中身にあります。逆に、RLSを完全にバイパスする service_role キーがクライアント側やビルダーのプレビューに紛れ込んでいたら、それは一発アウト——Supabase公式が明記する通り、service_roleはPostgreSQLの BYPASSRLS で動き、RLSを完全に無視します。
③ ビルダーの既定は「まず動く」に最適化される
生成物の第一目標は「デモが動く」です。だからRLSが未設定のテーブルや、USING (true) の全開ポリシが、疑われないまま残りがちです。これは体感ではなく、公開Supabaseアプリの一次調査で見えている傾向です——
公開Supabaseアプリ1,000件の一次調査で、RLSを持つアプリのうち約9.2%が『認証はするが行を所有者に絞らない』ポリシを持っていた(公開リポジトリ標本ゆえ下限値。所有者非スコープ=即脆弱ではなく要確認)。
「約1割が下限値」で、しかもこれはセキュリティ意識が相対的に高い公開リポジトリの数字です。ビルダーで作って非公開のまま公開している実プロダクト群では、体感的にこれより高いと私は見ています。
4. 責任境界を正しく引く——「あなたの責任」の中身
CVE-2025-48757がDisputedであることを、ネガティブに受け取る必要はありません。むしろ契約として明快です。境界を図にするとこうなります。
[ ビルダー/プラットフォーム側 ] [ あなた(開発者/事業者)側 ]
・UI・画面の生成 ・行レベルの認可設計(RLS の中身)
・Auth(サインイン)の配線 ・auth.uid() による所有者スコープ
・ホスティング・足回り ・service_role 経路の所有権チェック
・anon キーの発行 ・テナント分離・IDOR 対策
───────────────────── ─────────────────────
「動く」までを速く 「漏れない」を担保する
つまり、ビルダーを使う判断そのものは正しい。**ただし「エクスポート/公開した瞬間、右列はすべてあなたに移る」**という自覚が要ります。ここを埋めるのが、次章の検査手順です。
5. エクスポート後の検査手順:scan → RLS手検査 → probe
公開済み・実ユーザーがいる前提で、最短で最も刺さる穴から潰す3手を示します。順番に意味があります——静的で疑い、目で認可の意味を確かめ、動的で裏を取る、です。
Step 1. scan で「水平の穴」と「RLSの疑い」を機械的に洗う
エクスポートしたコード(またはビルダーが繋いだリポジトリ)の直下で、まず静的解析を回します。インストールも設定も不要です。
# いまのプロジェクトをそのまま静的解析する
npx @aegiskit/cli scan
scan が拾うのは、ビルダー製アプリで頻出する次のような穴です。
- RLS未設定テーブル /
USING (true)の全開ポリシ /WITH CHECK欠落(supabase/migrations/**.sqlを読む) - 秘密情報の混入(
service_roleキーやトークンがコードに焼き込まれていないか) - セキュリティヘッダー / CSP・レート制限・CSRF の欠落(=水平の統制)
- 所有権スコープのない汚染入力=IDOR の疑い(source→sink のデータフローで追跡)
ここで重要なのは、Aegisが正直に線を引くことです。ヘッダーやレート制限のような 水平の統制は自動化・自動修正の対象ですが、認可/IDOR・RLS設計のような 縦のリスクは「検出・警告」にとどめます。直せないものを直したとは決して主張しません。だから scan の出力は「安全証明」ではなく「疑いのリスト」として読みます。
Step 2. RLS を手で読む——「認証」と「認可」を分けて確かめる
scan が疑ったRLSは、最後は人間が意味を確かめます。機械はポリシの“形”を見ますが、「このテーブルは共有が意図か、所有者限定が意図か」という事業ルールの意味は知らないからです。読むべきは1点——auth.uid() で行が所有者に絞られているかです。
-- ❌ ありがちなビルダー既定:認証はするが、行を所有者に絞らない
-- ログインさえしていれば、他人の行も全部読める(IDOR の温床)
create policy "read" on public.orders
for select
using ( auth.role() = 'authenticated' );
-- ✅ 所有者スコープ:自分が所有する行だけを読める
create policy "read own" on public.orders
for select
using ( auth.uid() = user_id );
-- ✅ 書き込みは USING だけでなく WITH CHECK も要る(なりすまし INSERT を止める)
create policy "insert own" on public.orders
for insert
with check ( auth.uid() = user_id );
USING (true) や auth.role() = 'authenticated' が 常に悪いわけではありません。掲示板の公開投稿のように「そのテーブルは本当に全員に見せる」意図なら正解です。だから判断基準はただ一つ——「このテーブルは公開が意図か?」。意図が“所有者限定”なら、auth.uid() によるスコープが要ります。この「認証 vs 認可」の切り分けと、WITH CHECK を落としたときの書き込みバイパスは、それぞれ認証はするが認可しない問題で詳しく扱っています(ここでは再説しません)。
あわせて、次の“ビルダー固有”のチェックも目視します。
| 見る場所 | 確認すること |
|---|---|
service_role キーの置き場所 | クライアント・プレビュー・公開リポジトリに出ていないか(出ていたら即ローテート) |
| SECURITY DEFINER 関数 | search_path を固定しているか(未固定は権限昇格の入口) |
| Edge Function / サーバー処理 | anon経由の呼び出しでも、内部で所有権を再チェックしているか |
| RLS未設定テーブル | 「まだ作っただけ」のテーブルにこそポリシ空白が残る |
Step 3. probe で「疑い」を実行時に裏取りする
静的解析は「疑い」、動的解析は「確証」です。最後に、自分が所有するステージング環境へ非破壊のプローブを送り、Step 1–2で見つけた疑いが実際に再現するかを確かめます。
# 自分のアプリ(所有物)へ、安全・スコープ固定・非破壊で裏取りする
# テストIDを渡すと missing-auth / IDOR の再現も確認できる
npx @aegiskit/cli probe https://staging.example.com --correlate
ここは強く念を押します——probe を向けてよいのは、自分が所有・管理する環境だけです。他人のサイトへ許可なく送るのは、たとえ非破壊でも不正アクセスになり得ます。probeはlocalhost既定・スコープ固定・リクエスト予算つきで安全側に倒す設計ですが、**「所有物にのみ」**は運用者の責任です。
再現したものだけを「確定した露出」として最優先で直す——この静的×動的の相関が、ノイズに溺れず実害だけを潰す肝です。
そして CI に固定する
一度潰した穴を再発させないために、scan をCIゲートに組み込みます。高信頼の検出だけがビルドを止め、SARIFがGitHubコードスキャニングに残ります。ビルダーで“また生成し直す”たびに同じ穴が開くのを、機械が止めてくれます。CIゲートの設計思想はAI駆動開発の品質ゲートに、そもそも生成前に仕様で認可を固定するやり方は仕様駆動開発のワークフローにまとめてあります。
6. 正直なスコープ——ツールが「しないこと」
最後に、油断を生まないために境界を明記します。「scanを回したから安全」は、最悪のセキュリティ結果を生む考え方です。
npx @aegiskit/cli scanは 水平の統制を自動化し、縦のリスク(認可/IDOR・RLS設計・テナント分離)は検出・警告にとどめます。閉じるのはあなたの設計判断です。- クリーンな結果は「よくある罠は踏んでいない」であって、「このコードは安全だ」ではありません。
- データフロー解析は関数内(intraprocedural)が中心で、モジュールやフレームワークを跨ぐ流れは見逃し得ます。
- RLS設計・コードレビュー・脅威モデリング・手動ペネトレーションテストを置き換えず、補完します。
それでも、最頻出の穴を機械的に潰せる価値は大きい。ビルダーの速さを殺さずに、公開後でも「認証はするが認可しない」状態を体系的に閉じにいけます。まずは無料のOSSで、いまのプロジェクトを1コマンド——Aegis(npx @aegiskit/cli scan)から始めてください。そして scan が炙り出した縦のリスクを実際に塞ぐ設計まで手が要るなら、そこは私が承ります。
まとめ
- v0・Lovable・Boltは「動く」までを速く作るが、行レベルの認可(RLS)はあなたの責任。CVE-2025-48757(Lovable、CWE-863・CVSS 9.3、Disputed)が、その責任境界を公式に示している。
- 公開済み・実ユーザーありでも手遅れではない。**scan(静的で疑う)→ RLS手検査(認可の意味を確かめる)→ probe(所有物で裏取り)**の3手で、最頻出の穴を機械的に潰せる。
- anonキーの露出は正常、危険なのはRLSが甘いまま露出していること。service_roleキーは絶対にクライアントに出さない。守りの境界はキーではなくRLSにある。
- ツールは水平の統制を自動化し、縦のリスクは検出・警告のみ。閉じるのは人間の設計——ツールは置き換えず、補完する。