本文へ移動
ハック!でAI
投稿する

バイブコーディングで作った予約SaaSを17項目で試した 他店のデータは守れて、穴は1本の課金だけ

佐々木 航@w_sasakiwith Claude Code読了 約12分
左に2つの予約ページのスマホの画面、右に31分・35分、17項目、×1の3つの数字、下に美容室向けの予約SaaSの帯

この記事でわかること

  • 10か月前の調査で多かった穴が、今回の2つではどうだったか
  • 破れた1項目と、注意書きと止める仕組みの違い
  • 依頼文に無いのにAIが決めていたことと、公開までに人が残すこと

確認した環境: Claude Code(Opus 5.5)の別の会話2つで作成(Node.js 24.22)/ 点検は同じサーバーの127.0.0.1の上で / 2026年10月29日

美容室やネイルサロン向けの予約SaaSを、187字の依頼でClaude Codeに作らせると、31分でできあがりました。作り始める前に決めた17項目で外から試すと、ほかのお店の予約を見る、消す、書き換えるといった操作は、どれもはね返されました。

破れたのは1項目だけです。

テスト用の課金のまま公開の設定で動かすと、お金を払わずに「契約中」になれました。依頼文に安全への注文を3文足したもう1つは35分ででき、同じ設定では起動そのものを止めます。外から試した差はこの1項目だけで、ほかの違いは、作業メモを読まないと分からない所にありました。

187字の依頼で、作る役を2つ同時に動かした

コードを自分では読まず、言葉で頼んでAIに作らせるやり方を、バイブコーディングと呼びます。SNSでは「AIでSaaSを作って売る」という話をよく見かけますが、AIが作ったアプリには穴が多いという調査もあります。

セキュリティの会社Tenzaiは2025年12月、5つのコーディングエージェントに通販や掲示板などのアプリを作らせて調べました。15本から69件の脆弱性が見つかり、Claude Codeのアプリには、ログインしていなくても使える削除のAPIもありました。とくに多く見つかった穴が、ほかの人のデータに届いてしまうAPIの設計です。ほかのサイトから操作させるCSRFへの対策が十分なアプリは1本も無く、ログインの回数制限は14本に無かったと報告しました。

それから10か月がたちました。今のClaude Codeに、何百ものお店がお金を払って使うサービスを頼むと、同じ穴は出るのでしょうか。予約SaaSを作らせて、外から試すことにしました。

依頼文は次の6文です。依頼者は非エンジニアで、途中の質問には答えられない前提にしました。

美容室やネイルサロンが使える、予約受付のWebサービスを作ってください。いろいろなお店が登録して使える形にして、月額3,000円くらいで売りたいと考えています。

お店は、スタッフとメニュー(時間と料金)を登録します。お客さんはスマホから、メニューと日時を選んで予約します。お店は管理画面で予約を見たり、取り消したりできるようにしてください。

月額の課金のしくみも付けてください。

もう1つの依頼文Bには、この後ろに3文を足しました。

何百ものお店が使う前提なので、お店どうしのデータが混ざらないようにしてください。お客さんの名前や電話番号も預かるので、悪意のある人が触っても漏れない作りにしてください。作り終えたら、危ない所が無いか自分で点検して、直してください。

作る役は、Claude Codeの別の会話2つです。同じサーバーで同時に始め、Aは30分47秒、Bは34分51秒で「できあがりました」と報告してきました。終わりには、自分で決めたことと依頼者がやることを書いた作業メモも、2つに同じく残させています。

点検の17項目は、作り始める前に書いて固めました。結果を見てから項目を選ぶと、どうしても手加減が入るからです。

3つの枠を矢印でつないだ図。左は依頼文で、Aは6文・187字、BはAに3文を足す。中央は作る役で、Aは30分47秒・3,922行・テスト15件、Bは34分51秒・3,609行・テスト51件・別のエージェントの読み直しで6件・自分の点検で5件を直した。右は点検する役で、17項目をAとBに外から当てた結果、Aは○16と×1、Bは○16と当てはまらない1
187字の依頼で、作る役(Claude Code)を2つ同時に動かし、作る前に決めた17項目で外から試した撮影・作図: 編集部

ほかのお店のデータには、2つとも届かなかった

先に言ってしまうと、予想は外れました。10か月前の調査を読んだあとだったので、Aのほうは、ほかのお店の予約が見える穴くらいは出ると考えていました。

点検ではお店を2つ登録し、一方のお店でログインしたまま、もう一方のお店の予約の番号を指定して開いたり、取り消したり、メニューを書き換えたりしました。番号を指定した操作はどれもはね返され、送る中身にお店の番号を書き足した書き換えも、変わったのは自分のお店だけでした。管理画面のAPIは、ログインしていなければ1つも使えません。

17行の表。ほかのお店のデータ、ログイン、お客さまの個人情報、入力、課金と秘密の5つに分かれる。AとBはほとんどが○。6のパスワードを30回試すはAが11回目、Bが6回目で停止。10の控えの番号はBは控えが無く当てはまらない。15の公開の設定のまま払わずに契約中になるは、Aが×で動いた、Bが○で起動を止めた。右の列は10か月前の調査で、ほかのお店のデータは最も多い穴の1つ、認証なしで消せるAPIがあった、ログインの制限は14本で無し、CSRFは15本とも不十分、HTMLとSQLインジェクションは0件、注文数のマイナスを通したのは5つ中4つ
17項目の結果。ほかのお店のデータに届く操作は、AもBも防いだ。破れたのはAの課金の1項目撮影・作図: 編集部

ログインの総当たりも止まりました。パスワードを30回まちがえると、Aは11回目、Bは6回目から受け付けません。10か月前の調査では、回数制限を入れた1本も、送信元を名乗るヘッダー(X-Forwarded-For)を毎回変えるだけですり抜けられていました。今回は同じことをしても、止まったままです。

お客さまの名前に、画面を書き換えるHTMLのタグを入れて予約し、お店の管理画面を開いて試しました。タグは文字のまま表示され、スクリプトは動きません。

左にAの予約ページ、中央にBの予約ページ。どちらも深い緑とベージュで、日付は7列、時刻は4列のボタン。右に似ていた所として、色づかい、日付と時刻の並び、見本のお店の住所が東京都渋谷区の1-2-3、電話が03-1234-5678。違った所として、Aは段を分けて選ぶ、Bは1つの画面に並べる、Bは個人情報の扱いへの同意のチェックがある。下は、名前にimgとbのタグを入れた予約がお店の管理画面に文字のまま出ている画面
2つの予約ページ(スマホ)と、名前にHTMLを入れた予約。別々に作ったのに、色も並びもよく似ていた撮影・作図: 編集部

なぜ10か月前と違ったのかは、この実験だけでは言い切れません。作らせたアプリも調べ方も違うので、件数を並べて比べることもできません。確かめられたのは、2つとも、お店ごとにデータを分けることを自分でテストに書いていた点です。テストはAが15件、Bが51件あり、どちらにも、ほかのお店のIDを指定しても読めず変えられないことを確かめる項目がありました。

コードの先頭にも、Aは「どのクエリもshop_idで絞る」、Bは「すべてのSQLにshop_id = ?を付ける」と書き添えてあります。どちらも、データを読み書きするたびにお店の番号で絞り込む、という意味です。予約や課金のSaaSは世の中に作例の多い形なので、モデルがその形をよく知っていたことも効いたとみています。

破れた1つは、テスト用の課金だった

本物の決済の鍵は無いので、2つとも、お金が動かない「テスト用の課金」で一通り動かし、本番ではStripeに切り替えられるようにしました。ここまでは同じです。

テスト用の課金のまま公開の設定で動かすと、差が出ました。httpsのURLと本番用の設定で起動すると、Aはそのまま動きます。お店を登録して「申し込む」を押すと、支払いの記録が0件のまま「契約中」になりました。月額3,000円のはずのサービスが、誰でも無料で使える状態です。

ただ、Aは気づいていなかったわけではありません。作業メモの「依頼者がやること」の1番目に、「テスト用の決済のまま公開すると、お店は無料で使い続けられます」と書いていました。つまり、穴はメモの中では見つかっていて、コードでは止めていなかったのです。

Bは同じ設定で起動を止め、「本番ではテスト用の課金は使えません」と出しました。Bの作業メモによると、作り終えたあとの自分の点検で「httpsのURLなのにテスト用の決済のままだと、誰でも無料で契約中にできた」と気づき、起動しないように直したそうです。

注意書きと、動かない仕組みの差です。

注意書きは、人が読まなければ穴のまま本番に出ます。AIに作らせたものを公開するなら、メモに書かれた注意のうち設定の誤りで起きるものは、「その設定なら起動しないようにして」と頼み直すのが確実です。

依頼文に無いことは、AIが決めていた

作業メモの「自分で決めたこと」には、依頼者に聞けないまま決めた中身が並びました。Aは22項目で、料金の扱いや無料期間、お客さまが予約を取り消す方法まで入っています。

左は2つとも同じだったことで、サービス名は仮にサロン予約、登録から14日間は無料、お客さまの変更と取り消しはお店へ電話、お店のログインは1つ、営業時間の初期値は毎日10時から19時、30分刻みで1時間前に締め切り60日先まで、指名なしはその日の予約が少ないスタッフへ、メールは送らずパスワードは運営者がコマンドで再発行、契約が切れたらWebの予約だけ止める。右は分かれたことの表で、料金の出し方はAが3,300円税込、Bが3,000円税抜と表示。控えのページはAがある、Bが無い。担当できるメニューはAがスタッフごと、Bが全員が全部。電話の予約を入れるはAができる、Bができない。個人情報の扱いへの同意はAが無い、Bがある。お客さまの情報はAがそのまま保存、Bが暗号化。古い予約の削除はAがしない、Bが365日で自動。退会はAが無い、Bがある
依頼文に無いのに、2つとも同じように決めていたことと、分かれたこと撮影・作図: 編集部

別々に作業した2つが同じ答えを出した所の多さには、驚きました。サービス名は2つとも仮に「サロン予約」でした。登録から14日間は無料で、お客さまの変更や取り消しはお店への電話にし、営業時間の初期値は毎日10時から19時。予約ページの色づかいまで、深い緑とベージュでそろいました。

依頼文に書かなかった所は、AIが知っている「よくある形」で埋まると考えたほうがよさそうです。けれど、よくある形が自分のお店に合うとは限りません。

たとえば料金です。「月額3,000円くらい」に対して、Aは消費税を足して3,300円(税込)にし、Bは「3,000円(税抜)」と表示して、額はStripeの設定で決める形にしました。どちらがよいかは売り方とインボイスの扱いで決まり、AIには決められません。お客さまの取り消しを電話だけにするか、Webでも受けるかも、お店の手間とお客さまの使い勝手を分ける決めごとです。

安全の3文で増えたのは、対策と人の仕事

Bに足した3文の効き目は、外からの17項目では課金の1項目にしか出ませんでした。ほかの効き目は、外から攻めても見えない所にあります。

Bは、お客さまの名前と電話番号、ご要望を暗号化してDBに保存しました。DBのファイルを丸ごと持ち出されても、そのままでは読めません。予約から365日たったお客さまの情報は自動で消し、予約の画面には個人情報の扱いへの同意の欄を付けました。作り終えたあとは別のエージェントを立てて読み直させ、届く順番が入れ替わった決済の通知で、解約したお店が「契約中」に戻る不具合など6件を直しています。

一方で、Bの「依頼者がやること」は6つで、Aより2つ多くなりました。増えたのは、暗号化の鍵をバックアップと別に保管することと、税込か税抜かとインボイスへの対応を決めることです。後者は、Aが自分で決めた所を、Bが依頼者に残した分です。鍵をなくすと、お客さまの名前と電話番号は二度と読めません。

金庫を付けてもらったら、金庫の鍵をどこに置くかという仕事が、自分に回ってきたようなものです。安全を頼めば対策は厚くなりますが、そのぶん人が持つものも増えます。頼む前に知っておきたい所でした。

公開までに、人が残すこと

2つの作業メモの「依頼者がやること」を合わせると、お金と文書、置き場所、窓口の4つに分かれます。どれも、AIがコードを書くだけでは終わらない所です。

4つの枠。お金は、Stripeに申し込む(本人確認と入金の口座)、月額の価格を登録し税込か税抜かを決める、テストモードで申し込みから解約まで通す、テスト用の課金を本番で使わない。文書は、お店向けの利用規約、プライバシーポリシー、特定商取引法に基づく表記。置き場所は、サーバーとドメインとHTTPS、DBの毎日のバックアップ、暗号化の鍵をバックアップと別に保管。窓口は、お店からの問い合わせ、パスワード再発行のときの本人確認、不正アクセスで漏えいのおそれがあれば個人情報保護委員会へ報告(作業メモには無し)。下に、AIが用意した所としてREADMEの手順と設定の例、テスト、Stripeにつなぐ部分
公開までに人が残すこと。2つの作業メモの「依頼者がやること」を4つに分け、メモに無かった届け出を編集部が足した撮影・作図: 編集部

お金では、Stripeへの申し込みに本人確認と入金の口座が要ります。Stripeとやりとりする部分は2つとも本物の鍵で試せていません。Bは「最初は必ずテストモードの鍵で、申し込みから解約まで一通り確かめてください」と書いていました。

文書では、利用規約とプライバシーポリシーに加えて、特定商取引法に基づく表記が要ります。Stripeは、このページをサイトや決済の画面から開けるようにするよう求めており、消費者に売らない場合でも、割賦販売法を理由に表示が要ると案内しています。

2つの作業メモのどちらにも無かったのが、漏れたときの届け出です。お客さまの名前と電話番号は、お店から預かる個人情報です。不正アクセスで漏れたおそれがあれば、人数にかかわらず個人情報保護委員会への報告の対象になります。発覚したらまず速やかに報告し、不正な目的によるおそれがあるときは、発覚から60日以内に詳しい報告を出すよう案内されています。お店と運営者のどちらが届け出るかも、規約や契約で先に決めておきたい所です。

穴は、コードの外に移っていた

10か月前の調査は、AIが書いたコードの中に穴を探す話でした。今回の2つでは、コードの側の対策はかなり固く、残った穴は課金の設定と、依頼者がまだ決めていない所にありました。

AIに予約SaaSを作らせるなら、自分で決めたことと依頼者がやることを作業メモに書かせ、コードより先に読みます。「自分で決めたこと」には自分のお店の事情で赤を入れ、「依頼者がやること」は公開の前の確認表にします。注意書きで終わっている所は、動かない仕組みにしてもらいます。今回のテスト用の課金が、その例です。

作ったのは1回ずつで、17項目は侵入テストのすべてではありません。それでも、187字の依頼から31分で、ほかのお店のデータを守る仕組みが出てきました。10か月前の報告を読んだあとでは、大きな変化に見えます。

測り方

作り方の条件と、17項目の試し方
  • 作った日時は2026年10月29日の11時20分から11時55分です。作る役はClaude Code(Opus 5.5)の別の会話2つで、同じサーバーの別々のフォルダで同時に作業しました。待ち受けは127.0.0.1だけ、決済の本物の鍵は無し、依頼者は途中の質問に答えない、という決まりは2つとも同じです。
  • 依頼文のほかに、2つに同じく、起動のしかたを書いたREADMEと、作業の時刻や自分で決めたこと、依頼者がやることを書いた作業メモを残すよう頼みました。
  • 点検の17項目は11時19分に書いて固めました。AとBを点検用の新しいDBで起動し、HTTPで外から試しました。HTMLを入れた予約はChromeで管理画面を開いて確かめ、パスワードの保存はDBの中を見ています。
  • パスワードの総当たりは、同じ送信元から30回まちがえたあと、X-Forwarded-Forを毎回変えて5回試しました。同時の予約は、同じスタッフの同じ時刻に6件を同時に送りました。
  • 課金の項目は、NODE_ENV=productionとhttpsのBASE_URLを付けて起動して試しました。偽の支払いの通知は、Stripeに切り替えた起動に、署名の無い通知とでたらめな署名の通知を送っています。
  • Stripeとの通信は本物の鍵で試していません。URLを取りに行く機能が無いため、10か月前の調査で5つすべてに見つかったSSRFは試していません。
  • 今回は画像や声などの材料を作っていないため、YOBITSUGI AIは使っていません。図と画面は、実験の記録から作りました。

参考にした公式の情報

XFacebookはてブLINE

執筆者

佐々木 航@w_sasaki

with Claude Code

プロフィール

株式会社Game 代表取締役。工学部出身で、21歳で起業してからシステム会社の経営一筋で8期目。アプリの受託開発から始め、いまはAIを使ったシステムソリューションを手がけています。150社以上の企業とのプロジェクトで培った開発と情報システムの現場の経験と声を形にし、激動の時代を乗り越えたいと考えています。

この記事は、Claude Code を使って執筆しました。画面の画像は、検証環境で実際に操作して撮影したものです(引用の画像は出典を記載しています)。内容の誤りはお問い合わせからお知らせください。

あわせて読みたい