# AIで作ったアプリを公開する前に — 非エンジニアのための本番リリース全ガイド

> Claude Code・Cursor・Lovable・v0・Bolt でアプリを作ったけれど、公開して大丈夫か分からない。そんな非エンジニアの方に向けて、公開前に確認すべきことを「危険な順」に並べました。専門用語はすべて日常語に言い換え、各項目に自分で直す手順とAIに貼れる修正指示を添えています。

- 公開日: 2026-08-06
- 著者: 友田 陽大
- タグ: バイブコーディング, AI駆動開発, セキュリティ, 個人開発, Next.js
- URL: https://tomodahinata.com/blog/ai-app-pre-launch-guide-for-non-engineers
- カテゴリ: AIで作ったアプリの運営

## 要点

- AIは「動くコード」は速く書くが、「誰がどのデータを見られるか」「二重に課金されないか」「壊れたとき戻せるか」は指示しない限り書かない。公開前の事故はほぼこの3領域に集中する
- 危険度の順序は固定できる。①データベースの権限（RLS）②秘密の鍵の露出 ③サーバー側の所有者チェック ④決済の二重処理 — この4つは「あるかないか」の問題で、程度問題ではない
- CVE-2025-48757（CVSS 9.3 CRITICAL）はAIビルダー製アプリのRLS不備そのもの。ベンダーは「アプリのデータ保護は利用者の責任」として異議を出しており、つまり責任はあなたにあると公式に言われている
- Veracodeの検証ではAI生成コードの45%にOWASP Top 10相当の脆弱性が混入。GitGuardianの計測ではAI支援コミットの秘密情報漏洩率は人間のみの約2倍。これは能力ではなく構造の問題
- 「動くから大丈夫」の反対は「作り直し」ではない。優先順位をつけて上から潰せば、公開済みでもサービスを止めずに塞げる

---

「動いてる。でも、このまま公開していいのか分からない」

Claude Code や Cursor、Lovable でアプリを作った方から、いちばんよく聞く言葉です。この不安は正しい直感です。ただし、その理由はたぶんあなたが思っているものとは違います。

問題は「AIが書いたコードだから信用できない」ことではありません。**AIは頼まれた機能は実装するが、頼まれていない防御は実装しない**——これだけです。そして初めてアプリを作る人は、何を頼めばいいのかを知らない。当たり前です。知っていたら最初から自分で書けています。

この記事は、その「頼み忘れる部分」を危険な順に並べたものです。専門用語は初出時にすべて日常語に言い換えます。各項目には、自分で確認する方法と、そのまま Claude Code や Cursor に貼れる修正指示を添えました。

先に結論から言うと、公開前に必ず潰すべき項目は **4つ** です。それ以外は、あとからでも間に合います。

> この記事の内容を20問の質問形式にした[無料セルフ診断](/vibe-coding/checkup)があります。登録不要・入力内容はブラウザの外に出ません。読む前に自分の状態を知りたい方はそちらから。

---

## 1. なぜ「動く」と「安全」がずれるのか — 構造の話

まず、これがあなただけの問題ではないことを、測定結果で確認しておきます。

Veracode が100を超える大規模言語モデルを対象に80のコーディング課題で検証したところ、**45%のケースで OWASP Top 10 に該当する脆弱性が混入していました**。特にクロスサイトスクリプティング（他人が仕込んだスクリプトが自分のサイトで動いてしまう攻撃）への防御は、86%のサンプルで失敗しています。

GitGuardian の2026年の調査では、**AIの支援を受けて書かれたコミットは、人間だけのコミットに比べて約2倍の頻度でAPIキーやパスワードを漏らしていました**。

これはモデルが賢くなれば消える種類の問題ではありません。理由はシンプルで、**AIは「動作するか」を目標にしていて、「攻撃されるか」を目標にしていない**からです。

料理に例えるなら、AIは「美味しい料理を作る」ことには非常に長けていますが、「食中毒を出さない」ことは、そう言われない限り気にしません。そして食中毒は、味見では分かりません。

### 「動く」の反対は「壊れている」ではない

初めて開発する方が誤解しやすいのはここです。

- **動く / 動かない** は、自分で試せば分かります
- **安全 / 危険** は、試しても分かりません

たとえば「他人のデータが見えてしまう」状態のアプリは、あなたが自分のアカウントで使っている限り、完璧に正常に動きます。壊れて見えることは一度もありません。誰かがURLの数字を1つ書き換えるまでは。

だから公開前に、**意図的に「悪いこと」を試す** 必要があります。それがこの記事の中身です。

---

## 2. 危険度の順序 — 何から潰すか

やるべきことを全部並べると心が折れるので、順番を固定します。判断基準は「起きたときに取り返しがつくか」です。

| 順位 | 項目 | 起きること | 取り返し |
| --- | --- | --- | --- |
| 1 | データベースの権限（RLS）が無い | 全ユーザーの全データが誰でも読み書きできる | つかない（漏れたデータは戻らない） |
| 2 | 秘密の鍵がブラウザ側に出ている | 同上＋データの削除・改ざんもできる | つかない |
| 3 | サーバー側の所有者チェックが無い | URLの数字を変えると他人のデータが見える | つかない |
| 4 | 決済の二重処理を防いでいない | 顧客が二重に課金される／商品が二重に付与される | 返金はできるが信用は戻らない |
| 5 | 法務（個人情報・特商法）の不備 | 法令違反、決済審査に通らない | 是正できる |
| 6 | 従量課金の上限が無い | 想定外の請求が届く | 交渉次第、痛い |
| 7 | バックアップ・復旧手段が無い | 壊れたとき戻せない | 事前準備だけが対策 |
| 8 | エラーに気づけない | 壊れたまま何ヶ月も放置される | 気づけば直せる |

**1〜4 が「公開前に必ず」の4項目**です。5〜8 は公開直後にやれば間に合います。

以降、この順に見ていきます。

---

## 3. 【最優先】データベースの権限 — RLS とは何か

### 何の話か

**RLS（Row Level Security、行レベルセキュリティ）は「データベース側で、誰がどの行を見られるかを決める設定」です。**

「行」というのは、表計算ソフトの1行だと思ってください。ユーザーの一覧表があったとして、Aさんは自分の行だけ、Bさんは自分の行だけ見られるようにする——それがRLSです。

### なぜ致命的なのか

Supabase のようなサービスでは、ブラウザから直接データベースに問い合わせが飛びます。このとき使われる `anon`（匿名）キーは、**設計上公開されているもの**です。ブラウザの開発者ツールを開けば誰でも読めますし、それは正常な状態です。

問題は、**RLS が無効だと、その公開キーだけで全データが読み書きできてしまう**ことです。

つまりこうなります。

1. 攻撃者があなたのアプリを開く
2. 開発者ツールでデータベースのURLと `anon` キーを読む（どちらも公開情報）
3. そのままデータベースに問い合わせる
4. 全ユーザーの、全テーブルが返ってくる

ログイン画面があっても関係ありません。ログイン画面はブラウザ側の話で、データベースはそれを知らないからです。

### これは実際に起きている

CVE-2025-48757 として登録されている脆弱性の説明は、こうです。

> 生成されたサイトのデータベースの行レベルセキュリティポリシーが不十分で、認証されていない遠隔の攻撃者が任意のデータベーステーブルを読み書きできる

深刻度は **CVSS 9.3 CRITICAL**。最も高い部類です。対象は AI アプリビルダーの Lovable でした。

ここで重要なのは、この CVE に**ベンダーから異議が出ている**ことと、その理由です。ベンダー側の主張は「アプリのデータ保護は利用者の責任である」というものでした。

つまり——**あなたが作ったアプリのデータを守る責任は、あなたにある**と公式に言われている、ということです。ツール側が守ってくれることを期待してはいけません。

### 確認のしかた

Supabase を使っている場合:

1. Supabase の管理画面 → Table Editor を開く
2. テーブル一覧に「RLS disabled」「Unrestricted」といった警告が出ていないか見る
3. 出ていたら、それが全公開のテーブルです

RLS が有効でも安心できません。ポリシー（許可の条件）が `USING (true)` になっていると、「全員が全行を見られる」という意味なので、無効なのと同じです。

### AIに貼る修正指示

```text
Supabase のマイグレーション/SQL を全部読んで、次を表にして報告してください。
(1) RLS が有効になっていないテーブル
(2) RLS は有効だがポリシーが1つも無いテーブル
(3) ポリシーの条件が USING (true) など実質的に全員を許可しているテーブル

そのうえで、各テーブルについて「ログインした本人の行だけ」に絞る RLS
ポリシーの SQL を書いてください。性能のため auth.uid() は
(select auth.uid()) の形で書いてください。
```

最後の `(select auth.uid())` は性能上の定石です。この形にしないと、行数が増えたときにデータベースが極端に遅くなることがあります。

> より詳しくは [Supabase の「RLS」って何？ — AIが作ったアプリでデータが全公開になる仕組み](/blog/supabase-rls-explained-for-non-engineers) で解説しています。書いたポリシーが本当に絞れているかは、[無料のRLSチェッカー](/aegis/rls-checker)に貼り付けると判定できます。

---

## 4. 【最優先】秘密の鍵 — なぜ「APIキー」が他人に見えるのか

### 何の話か

アプリは外部サービス（データベース、決済、AI）を使うために「鍵」を持っています。この鍵の置き場所を間違えると、鍵ごと他人に渡ることになります。

いちばん多い間違いが、**`NEXT_PUBLIC_` を付けてはいけない値に付けてしまう**ことです。

Next.js では、環境変数（設定値）の名前が `NEXT_PUBLIC_` で始まると、**ビルド時にブラウザ向けのファイルへそのまま埋め込まれます**。これは公式ドキュメントに明記された仕様であって、バグではありません。ブラウザで動かす必要がある値のための仕組みです。

問題は、AI が「動かすため」に、秘密の鍵にもこの接頭辞を付けてしまうことがある点です。付ければ確かに動きます。そして誰でも読めるようになります。

### 特に危険な鍵

Supabase には2種類の鍵があります。

- **`anon` キー** — 公開前提。RLS の制限を受ける。ブラウザに置いてよい
- **`service_role` キー** — **すべての制限を無視できる鍵**。RLS を素通りする。絶対にブラウザに置いてはいけない

`service_role` キーが漏れると、RLS を正しく設定していても意味がありません。全部素通りされます。AI が生成したコードでこの鍵が使われがちなのは、**「とりあえず動く」からです**。RLS ポリシーを書かなくてもデータが取れるので、エラーが出ません。

### 確認のしかた

1. プロジェクト内を `NEXT_PUBLIC_` で検索する
2. 出てきた値のうち、「これが他人に見えても平気か？」を1つずつ考える
3. 少しでも迷ったら、それは移動すべき値です

あわせて、GitHub にコードを上げている場合は `.env` ファイルがコミットされていないか確認してください。**一度コミットした鍵は、あとからファイルを消しても履歴に残ります**。消すだけでは対策になりません。鍵そのものを作り直す必要があります。

### AIに貼る修正指示

```text
このプロジェクト全体を調べて、NEXT_PUBLIC_ が付いた環境変数に秘密情報
（Supabase の service_role キー、データベースのパスワード、各種 API の
秘密鍵）が入っていないか確認してください。

見つかった場合は、その値をサーバー専用の環境変数に移し、参照している
コードをサーバー側（Route Handler / Server Action）に移動してください。
クライアントコンポーネントから秘密情報を参照している箇所があれば、
すべて一覧にして報告してください。

なお、値そのものは出力せず「どのファイルの何行目にどの種類の鍵があるか」
だけを報告してください。
```

> 詳細は [`NEXT_PUBLIC_` と `service_role` — AIが書いた「APIキー」がなぜ他人に見えるのか](/blog/ai-app-api-key-exposure-explained) にまとめました。

---

## 5. 【最優先】所有者チェック — 「ログインしてる風」と本物の権限

### 何の話か

これは説明しづらいのですが、いちばん見落とされる項目です。

あなたのアプリに `/orders/123` のような画面があるとします。123 は注文番号です。ここで、**URLの 123 を 124 に書き換えたら何が起きるか**。

もし他人の注文が表示されたら、それが脆弱性です。OWASP（Webセキュリティの標準的な団体）がAPIリスクの**第1位**に挙げている、最も一般的な穴です。

### なぜ起きるのか

画面上に他人へのリンクが出ていなければ安全、と考えてしまうからです。しかし**URLを直接打てば、リクエストは飛びます**。リンクの有無は関係ありません。

AI は「ログイン中のユーザーの注文一覧を表示する」と頼まれれば、一覧はきちんと絞り込みます。しかし「注文詳細を表示する」と頼まれると、番号で引いてくるだけのコードを書きがちです。一覧が正しく絞られているので、動作確認では絶対に気づきません。

### 確認のしかた

これはブラウザだけで確認できます。

1. テスト用のアカウントを2つ作る（AさんとBさん）
2. Aさんでログインし、何かデータを作る。URLの番号（またはID）を控える
3. ログアウトして、Bさんでログインする
4. 控えたURLをアドレスバーに直接打ち込む

**Bさんに、Aさんのデータが見えたらアウト**です。見えずにエラーや「権限がありません」になれば正常です。

これは同じアプリの中で誰でもできる確認なので、公開前に必ず1回やってください。

### AIに貼る修正指示

```text
このアプリの、IDを受け取ってデータを返す/更新するすべての処理
（Route Handler、Server Action、API）を洗い出して、サーバー側で
「そのデータがログイン中のユーザーのものか」を検証しているか
1つずつ確認してください。

検証していないものがあれば、所有者チェックを追加した実装に修正して
ください。フロントエンドで表示を隠しているだけの箇所も、防御に
なっていない箇所として報告してください。
```

RLS を正しく設定していれば、この防御は二重になります。逆に言えば、RLS とサーバー側チェックは**どちらか一方ではなく両方**あるのが正しい状態です。

> [AIが作ったログイン機能は本物か — 「ログインしてる風」と本物の認可の違い](/blog/ai-generated-login-vs-real-authorization) で、実例を交えて詳しく扱っています。

---

## 6. 【最優先】決済 — 二重課金はなぜ起きるか

決済機能が無いアプリなら、この章は飛ばして構いません。

### 何の話か

Stripe をはじめとする決済サービスは、支払いが完了すると、あなたのアプリに「支払われましたよ」という通知（Webhook と呼びます）を送ります。あなたのアプリはそれを受けて、商品を渡したり、残高を増やしたりします。

ここに落とし穴があります。**この通知は「最低1回」届く仕組みで、同じ通知が複数回届くことがあります**。ネットワークが不安定だったり、あなたのアプリの応答が遅れたりすると、決済サービスは「届かなかったかもしれない」と判断して再送します。これは仕様であり、Stripe の公式ドキュメントにも明記されています。

通知を受け取るたびに残高を足す実装だと、そのまま**二重付与**になります。

### なぜAIは書き漏らすのか

「支払いが完了したら残高を増やす」と頼めば、AI はその通りに書きます。それは正しい実装です。ただし「同じ通知が2回来たらどうするか」は、頼まれていないので考慮されません。

そしてこの不具合は、テストでは絶対に出ません。手元では通知は1回しか来ないからです。本番で、しかも忙しい時間帯に初めて出ます。

### 対策 — 冪等性（べきとうせい）

対策には名前があります。**冪等性**——「何回やっても結果が同じになる性質」のことです。

具体的には、通知に含まれるイベントIDをデータベースに保存し、すでに処理済みのIDが来たら何もせず「受け取りました」と返すだけです。難しくはありません。忘れられるだけです。

私が担当した決済基盤では、この設計により**本番での二重課金を0件に保っています**。技術的に難しい話ではなく、「必ずやる」と決めているかどうかの差です。

### AIに貼る修正指示

```text
このアプリの決済 Webhook の処理を読んで、同じイベントが複数回届いた
ときに二重処理されないか確認してください。

イベントIDを一意キーとして保存し、処理済みなら早期リターンする
冪等性の実装を追加してください。記録と業務処理は同一トランザクションに
まとめ、失敗時に決済サービスが再送できるよう適切なステータスコードを
返す実装にしてください。

あわせて、(1) 金額がクライアントから送られた値を使っていないか、
(2) Webhook の署名検証が行われているか、も確認してください。
```

> [AIに決済を作らせたとき、二重課金が起きる理由と防ぎ方](/blog/ai-app-payment-double-charge-prevention) で、金額の改ざんと署名検証まで含めて扱っています。

---

## 7. 静かに間違うコード — 動くのに正しくない

ここからは「公開前に必ず」ではありませんが、放置すると効いてくる項目です。

AI 生成コードで最も多い不具合は、実は脆弱性ではありません。**エラー処理と業務ロジックの間違い**です。そしてこれらが厄介なのは、**エラーが出ないこと**です。

- 集計が微妙に合わない（境界の日付が含まれていない）
- 検索結果が一部欠ける（並び順が不定で、ページ送りで漏れる）
- 失敗したのに成功と表示される（例外を握りつぶしている）

どれも画面上は正常に動きます。数字が少し違うだけです。だから何ヶ月も気づかれません。

対策は「壊れてはいけない条件」を先に言語化することです。「合計は必ず明細の和と一致する」「同じ検索を2回したら同じ順序で返る」——こういう当たり前のことを書き出して、AIにテストとして書かせます。

> [「動いてるのに間違ってる」— AI生成コードで一番多い静かなバグ](/blog/ai-generated-code-silent-bugs) で扱っています。

---

## 8. お金と法律 — 見落としやすい2つ

### 従量課金

クラウドサービスやAIのAPIは、使った分だけ請求されます。想定外のアクセスや無限ループがあると、**請求は簡単に跳ねます**。個人開発で数十万円の請求が届く事故は珍しくありません。

公開直後にやるべきは3つです。各サービスの請求アラート設定、AI APIのキー単位の上限額設定、そしてアプリ側のレート制限（同じ人からの連続リクエストを制限する仕組み）。

> [公開したら請求が跳ねた — 従量課金の事故と上限設定](/blog/ai-app-cloud-bill-spike-prevention)

### 法務

メールアドレスを1つでも預かるなら、**プライバシーポリシーの掲載は個人情報保護法上の義務**です。「個人だから」「無料だから」は免除の理由になりません。

有料で提供するなら、**特定商取引法に基づく表記**も必要です。事業者名・連絡先・価格・支払時期・返品条件などの表示が法律で決まっています。Stripe の審査でも、ここが整っていないと通らないことがあります。

> [個人情報・特商法・プライバシーポリシー — AIアプリを商用公開する前の法務チェック](/blog/ai-app-legal-checklist-japan)

---

## 9. 壊れたときに戻れるか — 運用の最低ライン

最後は、公開後に効いてくる3点です。

**バックアップ。** AI に「このテーブルを整理して」と頼んだ結果、意図せずデータが消えることがあります。無料プランはバックアップの保持期間が短い、あるいは自動バックアップが無いこともあります。「気づいたときには復旧できる期間を過ぎていた」が起こります。

**ロールバック。** デプロイして壊れたとき、前のバージョンに戻す手順を知っていますか。深夜に本番が落ちたまま何時間も復旧できない、という事態は、手順を一度試しておくだけで防げます。なお、データベースの構造変更はコードと違って自動では戻りません。

**エラーの可視化。** 個人開発で最も多いのは「壊れていることに気づかない」です。フォームが送信できない状態が何ヶ月も続く——実際に起こります。ブラウザ側だけで失敗しているエラーは、サーバーのログには残りません。無料枠のあるエラー監視サービスを1つ入れておくだけで、状況は大きく変わります。

> 引き継ぎまで見据えるなら [エンジニアに相談・引き継ぐときの準備](/blog/handing-over-ai-app-to-engineer) もどうぞ。

---

## 10. 公開前チェックリスト（印刷用）

上から順に、1つずつ。

**公開前に必ず**

- [ ] 全テーブルで RLS が有効で、ポリシーが「本人の行だけ」に絞られている
- [ ] 書き込み（INSERT/UPDATE）にも制限がかかっている（`WITH CHECK`）
- [ ] `service_role` キーやDBパスワードが `NEXT_PUBLIC_` に入っていない
- [ ] `.env` が GitHub にコミットされていない
- [ ] 一度でも人目に触れた鍵を作り直した
- [ ] 別アカウントでURLの番号を書き換えて、他人のデータが見えないことを確認した
- [ ] 管理者機能がサーバー側で権限確認されている
- [ ] （決済がある場合）Webhook が二重に届いても二重処理されない
- [ ] （決済がある場合）金額をサーバー側で決めている
- [ ] （決済がある場合）Webhook の署名を検証している

**公開直後に**

- [ ] プライバシーポリシーを掲載した
- [ ] （有料の場合）特定商取引法に基づく表記を掲載した
- [ ] 各サービスの請求アラート・上限を設定した
- [ ] バックアップの有無と保持期間を確認した
- [ ] 前のバージョンに戻す手順を1回試した
- [ ] エラー監視を入れた、または重要処理の失敗が自分に届くようにした
- [ ] 本番URLで、登録とパスワード再設定が実際に動くことを確認した

> このチェックリストを20問の質問形式にして、危険な順に並べた結果とAIへの修正指示を出す[無料セルフ診断](/vibe-coding/checkup)を用意しています。登録不要・約5分です。

---

## まとめ — 作ったこと自体は、何も間違っていない

最後に一つだけ。

生成AIでアプリを作ったこと自体は、まったく正しい選択です。私自身、日々 Claude Code で本番のプロダクトを書いています。速くて安いのは事実で、それを使わない理由はありません。

ただ、**「速く作る」と「安全に出す」は別の作業**です。前者はAIが劇的に上手くなりました。後者は、まだ人間側に残っています。

そして後者は、思っているより短時間で終わります。この記事の4項目——RLS、鍵の露出、所有者チェック、決済の冪等性——を潰すだけで、致命的な事故のほとんどは防げます。全部作り直す必要はありません。

分からない項目があったら、まずは各章の修正指示をAIに貼ってみてください。それでも判断がつかないところ（特に認可の設計、決済、個人情報の扱い）だけ、人に聞けば十分です。
