サンプルレポート

リリース判定レポート

どれくらい多いか
公開アプリケーション 1,003 件のうち 593 件(59.1%)で、リリース前に確認すべき種類の信号が一つ以上出ました。二つが重なったときにだけ成り立つ結論は 146 件(14.6%)で出ています。以下の項目別の数字はより小さい標本を手で開いた値で、そのように明記します
この文書
お客さまの検査でお渡しするレポートと同じ形式です。対象アプリ一つではなく、公開アプリケーション 1,003 件の検査で実際に出た欠陥の種類を、直す順に並べています
根拠
広さは公開アプリケーション 1,003 件(測定日 2026-08-17)、深さは Next.js + Supabase アプリ 87 件と Firebase アプリ 60 件を手で開いた記録(測定日 2026-08-15)· 検査記録
標本
Supabase 側はスター 21 以上、中央値 66、最大 5,766。すべて 2025 年 6 月以降に更新されています。人が実際に参照して複製するコードであって、放置された個人の実験ではありません
検査範囲
ソースコード、依存関係、設定ファイル、DB スキーマ、アクセスルールのファイル、コンテナ・サーバー設定
検査ツール
シークレット検査 · 依存関係検査 · スキーマ・コードのパターン照合 · コードレビュー

判定

保留 — このままリリースしてはいけません。

すでに運用中でしたら、今日じゅうに A・B・C・D を処理し、その前にダッシュボードでの 3 件を先に行ってください。

ブロック項目
A · B · C · D — 四つとも今この瞬間に実行可能で、それぞれデータ・お金・権限に直接触れます
解除条件
四項目の「直ったかをどう確かめるか」がすべて通ること。E・F はリリースを止めません
判定の根拠
B は認証なしでデータベース全体に届き、A は個人情報の漏えいとして法定の届出義務にまでつながります
検討
スキャナ 2 種とスキーマ・コードのパターン照合を回したあと、コードを読んで判定しました

1 ページ要約

このアプリがすでに運用中なら、今この瞬間に三つのことが可能です。誰でもアクセスルールの抜けたテーブルの中身を見られ、お金を払わずに注文を完了でき、リポジトリに残った鍵でサービスを代わりに使えます。

発見
6
今日じゅうに
4
今週
2

コードを直す前に、ダッシュボードでの対応が 3 件あります。下の「いま 10 分で」。

同じ 87 件にシークレット検査と依存関係検査を回しました。この二つが出したのは D と F だけです。A・B・C・E はどちらの検出対象でもなく、スキーマとコードを照合して初めて出ます。

公開の静的解析ルールセット ルール 219 件とセキュリティルールセット 7 種(ルール 135 件)を同じ欠陥に回した、別の測定記録もあります。結果は同じで、ご要望があればそのままお送りします。二つ併せてお伝えします。ルールを自分で書けば捕まる項目もあります。捕まらないのはツールではなく既定の設定です。そしてその測定はファイル 11 個の小さなアプリで行ったため、規模が小さくツールに不利だったという反論が可能で、私たちもその反論は妥当だと考えています。

上の 87 件の数値はスキャナの出力ではなく、選り分けたあとの値です。シークレットは生の検出 23 件からドキュメント・例・フィクスチャを取り除いて 11 件になり、そのうち 4 件を直接開いて本物の漏えい 1 件を確認しました。アクセスルールは、パースの副産物と Supabase 管理スキーマ、表記だけ違う同じテーブルを取り除いたあと 6 件・50 個が残りました。取り除く作業のほうが、見つける作業より長くかかります。お客様の検査でも同じ順序を守ります。

いま 10 分で

コードに触れず、ダッシュボードで終わることです。コードだけ直しても、すでに外に出たものはそのまま生きているので、この三つが先です。

  1. 1 · 約 2 分

    露出した鍵をローテーションしてください

    Supabase ダッシュボード → Settings → API → service_role キーを Reset。リポジトリにコミットされた鍵が他にもあれば(下の D)、そのサービスのダッシュボードでも一緒にローテーションしてください。そのあとデプロイ先の環境変数を新しい値に置き換えます。

    このローテーションは、いまデプロイ中のアプリを即座に止めます。アプリがこの鍵でデータベースに接続しているからです。それでも先に行うことをお勧めします。鍵はすでに外に出ており、コード修正とデプロイにかかる時間だけデータベースが開いているより、少し止まるほうがましです。

    サイトを止められない状況なら順序を入れ替えてください。下の B の修正指示を先に適用して anon キーで動くコードをデプロイし、デプロイ完了の直後にローテーションします。この場合、デプロイが終わるまでは古い鍵が有効なままだとご承知おきください。どちらを選んでも、ローテーション自体を飛ばせばコードを直しても意味がありません。

  2. 2 · 約 3 分

    AI 提供元の月次予算上限と通知を有効にしてください

    ダッシュボード → Settings → Limits で月次の予算上限とメール通知を設定してください。現在このアプリは呼び出し上限がなく、自動の繰り返し呼び出しで料金が上がり続けます(下の E)。コード修正までは、これが唯一の防御線です。

  3. 3 · 約 3 分

    ホスティングの支出上限を有効にしてください

    デプロイ先のダッシュボード → Settings → Spend Management で上限と通知を有効にしてください。上の 2 番は AI の呼び出し料金しか止めません。ホスティングは送り出した転送量で別に課金し、攻撃トラフィックも転送量です。Vercel は DDoS トラフィックを含むすべての転送量を 1GB あたり $0.15 で課金し、支出上限は既定ではありません。出典

    上限に達するとサイトが止まることがあります。金額を決めるときは先月の実際の転送量を先にご覧になり、通知のしきい値を上限より低く置いてください。

今日じゅうに

コードを直す必要がある項目です。今この瞬間に誰でも実行できるので、一日を超えないでください。

A 緊急

一部のテーブルにしかアクセスルールがかかっていません

何が起きるか

データベースには「誰がどの行を見られるか」というルールがテーブルごとに個別にかかります。あるテーブルにはかかっていて、あるテーブルにはありません。ルールのないテーブルは、ブラウザに公開された鍵だけで全部読み出せます。その中に連絡先や注文が入っていれば個人情報の漏えいであり、法定の届出義務につながります。

誰が、どうやってできるか

必要なもの
ブラウザに公開された anon キー。アプリを開けば誰でも持っています
難易度
ブラウザの開発者ツールからのリクエスト一つ
影響範囲
ルールの抜けたテーブルの全行

どう確認したか

スキーマファイルの create table 文と enable row level security 文を、テーブル単位で照合します。表記の違う同じテーブルは一つとして数えます。Firebase アプリならルールファイルから、条件が true のものやログインの有無しか見ていないルールを探します。シークレットスキャナと依存関係スキャナの検出対象ではありません

Supabase のスキーマファイル

公開アプリ 87 件の検査で最も多く出た形です。SQL でテーブルを作る 60 件のうち 6 件がこれに該当し、その 6 件にルールのないテーブルが 50 個残っていました。最も鮮明だった一件はテーブル 82 個にルールをかけ、3 個を取りこぼしました。82 回きちんとやった人が 3 回逃したということです。「ダッシュボードで有効にしたのだろう」という説明はこの形には通りません — 同じファイルで別のテーブルには有効にしているからです。一社の問題ではありません。同じ日に公開 Firebase アプリ 60 件を別途検査したところ、ルールファイルをリポジトリに置いている 25 件のうち 8 件に条件が true のルールがあり、3 件は書き込みまで開いていました。4 件はアカウントさえあれば全体に届きます。同じ 60 件の中に、範囲がきちんと指定されたルールが 302 個あります。ただしリポジトリのルールファイルが実際に配備されたルールだという保証はないため、Firebase の数値はリポジトリに何が入っているかまでしか語りません。

create table public.orders (...);
alter table public.orders enable row level security;

create table public.contacts (...);
-- 三行上にはあるルール文が、ここにはありません

どう直すか

ルールを有効にする文だけを先に入れないでください。ポリシーなしにルールだけ有効になると、すべての参照が空の結果になり、アプリはその場で止まります。下の指示が両方を同じファイルに入れる理由です。

下を Claude Code・Cursor・Codex にそのまま貼りつけてください。

Supabase のスキーマでアクセスルールが抜けているテーブルを探し、
ルールとポリシーを一緒に入れてほしい。

重要: ルールを有効にする文とポリシーは、必ず同じマイグレーションファイルに
入れること。ポリシーなしにルールだけ有効になった瞬間、すべての参照が
空の結果になりアプリが止まる。

作業:
1. create table はあるが enable row level security がないテーブルを
   すべて列挙する。表記が混在していれば public.x と x は同じテーブルと見なす
2. 各テーブルに所有者条件のポリシーを追加 — ログインユーザーが自分の行だけ
   select/insert/update できるようにする
3. 公開で読まれるべきテーブルは、ルールを切るのではなく読み取り専用ポリシーで開く
4. anon ロールに残っている不要な GRANT を回収する
5. auth・storage など Supabase 管理スキーマには触れない

保つべき動作: ログインユーザーの現在の画面と API レスポンスは同一であること
変更禁止: アプリケーションのロジック、既存のカラム構造

確認: ログインしていないリクエストと、ユーザー A のトークンでユーザー B の行を
参照 → どちらも空の配列

直ったかをどう確かめるか

ログインせずに参照 → 空の結果。ユーザー A でユーザー B の行を参照 → 空の結果。そのうえでスキーマファイルの create table の数と enable row level security の数を数えて一致するか見てください。この検査で引っかかった 6 件は、すべて二つの数が違いました。

B 緊急

管理者キーをブラウザに出るファイルで使っています

何が起きるか

データベースのすべてのルールを無視するマスターキーがあります。その鍵を参照するコードが「ブラウザで実行」と印のついたファイルに入っています。このファイルはウェブページのソースに載って出ていきます。

誰が、どうやってできるか

必要なもの
変数名が NEXT_PUBLIC_ で始まっていれば何も要りません。ページのソースを開けば見えます
難易度
ブラウザでページのソースを見る
影響範囲
名前が公開形ならデータベース全体 — 読み取り・変更・削除。そうでなければ、その機能が権限なしに静かに失敗します

どう確認したか

コードを読んで発見。「ブラウザで実行」の印とマスターキーの参照が同じファイルにあるかを照合します

「use client」の付いたモジュール

どちらの場合も直す必要があります。変数名が NEXT_PUBLIC_ で始まっていると Next.js がその値をバンドルにそのまま埋め込むので、鍵が外に出ます。付いていなければブラウザでは値が空になり、管理者権限が必要な動作が権限なしで動くコードになります。前者は漏えい、後者は静かな故障です。

'use client'
import { createClient } from '@supabase/supabase-js'

export const admin = createClient(
  process.env.NEXT_PUBLIC_SUPABASE_URL!,
  process.env.SUPABASE_SERVICE_ROLE_KEY!   // ← ブラウザに出るファイルです
)

どう直すか

鍵のローテーションは上の「いま 10 分で」にあります。ローテーションはデプロイ中のアプリを即座に止めるので、その項目の順序の説明を先にお読みください。

下を Claude Code・Cursor・Codex にそのまま貼りつけてください。

service_role キーが 'use client' モジュールから参照されている。
サーバー専用に移してほしい。

作業:
1. 'use client' の付いたファイルから service_role の参照をすべて除去
2. ブラウザ用クライアントは anon キーだけを使うよう分離
   (lib/supabase-client.ts)
3. サーバー専用クライアントを作り、SUPABASE_SERVICE_ROLE_KEY
   (NEXT_PUBLIC_ なし) を使う
   → lib/supabase-server.ts、サーバーコンポーネントと API route からのみ import
4. 管理者権限が必要な動作は API route に移し、その中で呼び出し元をまず認証する

保つべき動作: 現在の画面と API レスポンスが同一であること
変更禁止: データベースのスキーマ、ビジネスロジック

確認: npm run build のあと grep -r "service_role" .next/static → 結果なし

直ったかをどう確かめるか

直す前に npm run build のあと grep -r "service_role" .next/static をまず回してみてください。いま検索に出るなら、鍵はすでに外に出ています。修正後は同じコマンドが何も見つけないはずです。そのうえで古い鍵で API を呼び、拒否されるか確認してください — これがローテーションが実際に効いた証拠です。

C 緊急

支払わずに注文を完了できます

何が起きるか

決済が終わったという信号を受け取るアドレスがあるのに、その信号が本物の決済事業者から来たのかを確認していません。アドレスさえ分かれば誰でも偽の信号を送り、お金を払わずに注文を完了させられますし、金額も好きなように書けます。

誰が、どうやってできるか

必要なもの
決済通知のアドレス一つ。よくある経路なので推測できます
難易度
コマンド一行
影響範囲
無料の注文、金額の改ざん、同じ注文の繰り返し処理

どう確認したか

コードを読んで発見。決済通知を受けるハンドラで、署名の検証・重複防止・金額の再確認の三つをそれぞれ探します。シークレットスキャナと依存関係スキャナの検出対象ではありません

決済 webhook のハンドラ

三つが揃って抜けています。署名の検証なし — 決済事業者から来たリクエストかを確認していません。重複防止なし — 同じ通知を 100 回送れば 100 回処理されます。金額の再確認なし — サーバーで実際の決済金額を照会し直していません。

export async function POST(req: Request) {
  const body = await req.json()
  await fulfillOrder(body.orderId, body.amount)
  return Response.json({ ok: true })
}

どう直すか

下を Claude Code・Cursor・Codex にそのまま貼りつけてください。

決済 webhook に署名の検証、重複防止、金額の再確認を追加してほしい。

作業:
1. 署名の検証: 決済事業者が送った署名ヘッダを raw body で検証する。失敗時は 401
   (Stripe なら stripe.webhooks.constructEvent、他社ならドキュメントの検証方式)
2. 冪等性: 処理した event id を保存するテーブルを作り、
   すでにあれば 200 だけ返して終了する
3. 金額の再確認: body の amount を使わず、決済事業者の API でその決済を
   照会し直し、実際の承認金額と注文金額が一致するか確認する。
   不一致なら中断してログを残す

保つべき動作: 正常な決済は今と同じく注文が完了すること
変更禁止: 注文完了後のビジネスロジック (fulfillOrder の内部)

確認:
1. 署名ヘッダなしで POST → 401
2. 有効なイベントを 2 回送信 → 注文は 1 回だけ処理
3. body の amount を改ざんして送信 → 処理を拒否

直ったかをどう確かめるか

署名ヘッダなしでリクエスト → 401 で拒否。同じイベントを 2 回送信 → 1 回だけ処理。金額を改ざんして送信 → 拒否。

D 緊急

コミットされたファイルに本物の鍵が入っています

何が起きるか

環境変数ファイルが値を含んだままリポジトリに入っています。リポジトリを見られる人は誰でも、その鍵で該当サービスを使えます。リポジトリが公開なら自動収集プログラムが常に走査しているので、鍵はすでに収集されたと考えるほうが安全です。

誰が、どうやってできるか

必要なもの
リポジトリの読み取り権限。公開リポジトリなら何も要りません
難易度
ファイルを開く
影響範囲
その鍵が開けるサービス全体

どう確認したか

シークレット検査で検出したあと、ファイルを一つずつ開いて本物の値かドキュメント・例・フィクスチャかを分けます

リポジトリにコミットされた環境変数ファイル

公開アプリ 87 件でのシークレット検査の生の検出は 23 件でした。README や API ドキュメント、テストのフィクスチャ、.env.example を取り除くと 11 件が残り、そのうち最も疑わしい 4 件を直接開いたところ、本物の漏えいは 1 件でした。コミットされた .env.save 一つに JWT の秘密鍵と Redis REST トークン、データベースの接続文字列が一緒に入っていました。残りの 7 件は開いていません。同じ 4 件から出た誤検出のほうが重要です。リリーススクリプトで「Supabase キーの漏えい」として報告された値を開き、JWT をデコードすると role が anon でした。ブラウザに載って出ていくために作られた公開鍵です。そのままレポートに移していたら、お客様は何ともない鍵をローテーションするためにアプリを止めていたはずです。

# .env.save — リポジトリにコミットされています
JWT_SECRET=eyJhbGciOiJIUzI1NiIs...
UPSTASH_REDIS_REST_TOKEN=...
DATABASE_URL=postgresql://postgres:...@db...supabase.co:5432/postgres

どう直すか

ローテーションの順序は上の「いま 10 分で」と同じです。ファイルを消すだけでは何も解決しません — 履歴にそのまま残り、すでに出た鍵は有効なままです。

下を Claude Code・Cursor・Codex にそのまま貼りつけてください。

コミットされたファイルに本物の鍵が入っている。
ローテーションして履歴から消してほしい。

作業:
1. 実際の値が入ったファイル(.env.save など)をリポジトリから削除し
   .gitignore に追加する
2. 各サービスのダッシュボードで露出した鍵をすべてローテーションする
3. 新しい鍵はデプロイ先の環境変数にだけ入れる。リポジトリに再びコミットしない
4. 過去のコミット履歴から除去する (git filter-repo または BFG)。履歴の
   書き換えなので、共同作業者に先に知らせ、強制プッシュの時点を合意する
5. 例が必要なら .env.example にプレースホルダだけを残す

保つべき動作: デプロイされたアプリが新しい鍵で動き続けること
変更禁止: アプリケーションのロジック

確認: リポジトリ全体で露出していた鍵の文字列を検索 → 結果なし。
古い鍵で API 呼び出し → 拒否

直ったかをどう確かめるか

リポジトリ全体で露出していた鍵の文字列を検索し、結果がないか確認してください。そのうえで古い鍵で各サービスの API を呼び、すべて拒否されるか確認してください。ファイルだけ消してローテーションを飛ばせば、何も変わりません。

今週

上の項目を処理したあと、続けて直してください。

E

AI 呼び出しに出力の上限がありません

何が起きるか

AI 機能を使うたびに料金が出ていくのに、応答の長さにもユーザーごとの呼び出し回数にも上限がありません。繰り返し呼び出しを自動化すれば料金は上がり続けます。セキュリティ事故ではありませんが、金銭的な損失は同じです。

誰が、どうやってできるか

必要なもの
ログイン。認証の確認がなければログインなしでも可能です
難易度
繰り返しリクエストのスクリプト
影響範囲
AI の利用料。gpt-4 の出力単価は 100 万トークンあたり $60 で、上限を 4,000 トークンに置くとリクエスト 1 件あたり最大 $0.24 です。毎分 60 回の自動呼び出しなら 1 時間で最大 $864 です。アカウント等級ごとの毎分トークン制限に当たれば実際の上限はこれより低くなります — ダッシュボードで現在の等級をご確認ください

どう確認したか

コードを読んで発見。LLM の呼び出し地点に出力の上限があるか、そして上限をかけるコードが別のファイルにあるかまで見ます。シークレットスキャナと依存関係スキャナの検出対象ではありません

LLM の呼び出し地点

ユーザーごとの呼び出し制限、日次の上限、予算の通知がすべてありません。認証の確認もありません。出力の上限を別のファイルでかけている場合があるので、この項目は呼び出し地点だけを見て判定せず、上限をかけるコードがどこにもないことを確認してから上げます。

どう直すか

予算上限の設定は上の「いま 10 分で」にあります。コードとは別に、それが最後の防御線です。

下を Claude Code・Cursor・Codex にそのまま貼りつけてください。

LLM を呼び出す API route に使用量の制限を追加してほしい。

作業:
1. ユーザーごとの rate limit: 毎分 5 回、日次 50 回 (ユーザー id + 日付で計数)
2. 超過時は 429 と案内メッセージ
3. 出力の上限(max_tokens)を 1000 と明示する
4. 認証されていないリクエストは 401 (現在は誰でも呼び出せる)
5. 日次の全体呼び出しがしきい値を超えたら機能を自動停止する kill switch

保つべき動作: 通常のユーザーの一般的な利用が妨げられないこと
変更禁止: プロンプトの内容、モデル選択のロジック

確認: 同じユーザーで毎分 6 回呼び出し → 6 回目が 429。認証なしで呼び出し → 401

直ったかをどう確かめるか

同じユーザーで毎分 6 回呼び出し → 6 回目が 429。認証なしで呼び出し → 401。そのうえで応答が上限で切れる場合がどれくらいあるかをログで確認してください — 頻繁に切れるなら上限が低いということで、それは別に調整する問題です。

F

既知の脆弱性がある依存関係が溜まっています

何が起きるか

インストールされているライブラリの中に、公開された脆弱性があるバージョンが残っています。公開された脆弱性は攻撃コードも一緒に出回っていることが多く、バージョンを見るだけで試せます。ほとんどはバージョンを上げれば終わります。

誰が、どうやってできるか

必要なもの
lockfile。公開リポジトリなら誰でも読めます
難易度
公開された脆弱性一覧との照合
影響範囲
パッケージによります。認証・リクエスト処理の系統なら迂回につながります

どう確認したか

依存関係検査

lockfile

公開アプリ 87 件のうち 83 件で lockfile を測定しました。脆弱性のある依存関係はリポジトリあたり中央値 81 件、最大 253 件でした。CVSS 9.0 以上を一つでも持つ所が 83 件中 46 件です。この項目を上の四つと同じ重さで数えない理由は、ほとんどが更新で解決し、判断が要らないからです。

どう直すか

下を Claude Code・Cursor・Codex にそのまま貼りつけてください。

既知の脆弱性がある依存関係を上げてほしい。

作業:
1. ロックファイルを基準に、既知の脆弱性の一覧を作る
2. CVSS 9.0 以上から上げる。メジャーアップグレードが必要なものは別に印を付ける
3. 一度に一つずつ上げ、ビルドと既存の動作を確認してから次に進む
4. 上位パッケージに縛られて上げられないものは、理由を書いて残す

保つべき動作: ビルドが通り、既存の画面・API が同一に動作すること
変更禁止: アプリケーションのロジック

確認: 同じ検査をもう一度回して CVSS 9.0 以上が 0 件

直ったかをどう確かめるか

同じ検査をもう一度回して CVSS 9.0 以上が 0 件か確認してください。残ったものがあれば、なぜ上げられなかったかを一行書き残してください — 次の検査で同じ項目を判断し直さずに済みます。

このままだと漏れ続けるお金

上の項目と違い、リリースを止めません。代わりに毎月出ていきます。機能を変えずに減らせるものだけを書きました。

何をいま 変えると
AI モデル
LLM の呼び出し地点
gpt-4
出力 100 万トークンあたり $60
gpt-4o
出力 100 万トークンあたり $10
6 分の 1
1 リクエスト $0.24 → $0.04
応答の長さ
同じ呼び出し
上限なし max_tokens 1000
チャットの応答に 4000 トークンはほとんど使われません
4 分の 1
4,000 トークン基準

二つを一緒に変えると AI リクエスト 1 件あたり $0.24 が $0.01 を下回ります。モデルと応答の長さを同時に下げるからです。上の E(呼び出し上限)とは別の問題です。E は暴走を止めるもので、こちらは平常時の単価を下げるものです。両方とも必要です。

アプリの中では減らせない費用

転送量の課金は上の二行とは違います。コードで単価を下げる話ではなくダッシュボードで上限を有効にする話で、その項目は上の「いま 10 分で」の 3 番にあります。このレポートは実際の請求内容と転送量の推移を見ていません。ダッシュボードの読み取り権限があって確認できます。

変える前に確認いただくこと

モデルを変えると応答が変わることがあります。お使いのプロンプトをいくつか両方のモデルに入れてみて、結果が使えるかをご自身で確かめてから変えてください。費用が 6 分の 1 でも答えが使えなければ節約ではありません。単価は 2026-08-15 に OpenAI の公開価格表で確認した値で、上の金額はその価格表で計算した値です。価格は変わるので、適用時点で改めてご確認ください。

どう直すか

下を Claude Code・Cursor・Codex にそのまま貼りつけてください。

LLM 呼び出しの単価を下げてほしい。

作業:
1. model を 'gpt-4' から 'gpt-4o' に置き換える
2. max_tokens を 1000 と明示する (現在は上限なし)
3. 応答が切れる場合に備えて finish_reason が 'length' なら
   ログを残すよう追加する (上限が実際に足りないかを確認するため)

保つべき動作: リクエスト・レスポンスの形式と、画面に見える結果の構造はそのまま
変更禁止: プロンプトの内容

確認: 同じプロンプト 5 個を変更の前後で呼び出し、応答を並べて比較。
      使えるなら維持、だめなら gpt-4o のまま max_tokens だけ上げて再比較

すでに悪用されたかを確かめる方法

運用中のアプリなら、直すだけでは終わりません。すでに起きたことがないか確認する必要があります。以下はダッシュボードで直接見られるものです。

何をどこで 何を探すか
ルールのないテーブルの大量参照Supabase → Logs → API同じテーブルを短時間に繰り返し参照したリクエスト、普段より大きいレスポンスサイズ
マスターキーの悪用Supabase → Logs → APIservice_role で入ってきたリクエストのうち、サーバーの IP でないもの
決済のない注文決済事業者のダッシュボード ↔ 注文テーブル期間を決めて承認件数と完了注文数を照合。注文のほうが多ければ、その差が疑わしい件
重複処理注文テーブル同じ決済 id で完了した注文が二つ以上ある場合
AI 料金の急増AI 提供元 → Usage日別の急上昇区間と、その時刻のサーバーログ
転送量の急増ホスティングのダッシュボード → Usage短時間に集中した帯域、同じ経路への繰り返しリクエスト

痕跡がないからといって、なかったと断定はできません。ログの保存期間が短ければ、それ以前は確認できません。確認した期間も一緒に記録しておいてください。

発見どうしがどう絡むか

それぞれを別々に見ると優先順位を取り違えます。このアプリでは三つの関係が重要です。

B を残したままでは A を直しても意味がありません

A(アクセスルール)は、データベースが要求者を見て弾く防御です。B のマスターキーはそのルールを丸ごと無視します。A だけ直して B を残せば、防御は成立しません。B の鍵のローテーションが先です。

B と D は同じローテーション一回では終わりません

B はバンドルに載った鍵で、D はリポジトリにコミットされた鍵です。出た経路が違うので、どちらを直しても他方の鍵は有効なままです。ローテーションは一度に行いますが、露出した鍵の一覧を先に二か所から集める必要があります。

A をポリシーなしで有効にすると、アプリはその場で止まります

ルールだけ有効にしてポリシーを入れなければ、すべての参照が空の結果になります。利用者にはデータが消えたように見えます。A の修正指示が両方を同じマイグレーションファイルに入れよと書いた理由がこれです。

一行だけ直すと何が残るか

上の修正指示がなぜ一行ではないのかの説明です。各項目で最も目につく修正を一つだけしたとき、何がそのまま残るかを書きました。コーディングエージェントで直されるときは、この表を横に置いてください。

目につく修正それでも残るもの
A ルールを有効にする ポリシーなしで有効にするとアプリが止まります。ポリシーを入れても anon ロールに残った GRANT はそのままです
B 鍵をサーバー専用に移す ローテーションしなければ、すでに出た鍵は有効なままです。コードはきれいなのにデータベースは開いたままです
C 署名の検証を入れる 同じ通知を何度も送れば何度も処理され、金額は依然としてリクエストが言った値を信じます
D ファイルを消す 履歴にそのまま残り、ローテーションしていない鍵は有効なままです。三つ全部やって終わりです
E 呼び出し制限をかける 認証の確認がなければ、ログインせずに呼び出す経路は開いたままです
F 最新版に一度に上げる メジャーアップグレードが混ざると、何が壊れたのか分かりません。一つずつ上げてこそ戻せます

お客様のエージェントが実際に何を取りこぼすかは、ケースごとに記録しています。数が溜まったら分母と一緒に公開します。

漏えいが確認されたらすべきこと

A でルールが抜けたテーブルに連絡先や注文が入っていれば、個人情報に当たります。実際の閲覧の形跡が確認されれば、説明は選択ではなく法定の義務になります。以下は韓国・個人情報保護法第 34 条と施行令第 40 条の要件です。

何をいつまでに どこへ
本人への通知 遅滞なく 影響を受けた利用者本人
届出 72 時間以内 個人情報保護委員会または韓国インターネット振興院(KISA) — 受付窓口は個人情報ポータル

届出の対象基準は、本人 1,000 人以上の漏えい、または機微情報・固有識別情報の漏えいです。未届出の場合、同法第 75 条により 3,000 万ウォン以下の過料が科されることがあります。基準線の近くなら判断を先送りせず、まず窓口にお問い合わせください。

誰かに聞かれたら

お客様に聞かれたら

「外部診断でアクセス権限の設定に問題が見つかり、直ちに修正しました。確認可能な期間の記録を精査し、現時点で確認された閲覧の形跡はありません。追加で判明した内容があれば、すぐにお知らせします。」

ログを実際に確認したあとにだけ使ってください。形跡が出たなら、この文の代わりに上の表の通知・届出の手続きに進みます。

投資家・パートナーに聞かれたら

「リリース前に外部診断を受け、深刻な項目を修正しました。診断の範囲と確認できなかった範囲が併記されたレポートをお渡しできます。」

「再検査で確認しました」は、再検査を実際に受けた場合にだけ使えます。このレポートは無料診断なので再検査は含まれていません。

言葉を選ぶとき

「現時点で確認された閲覧の形跡はありません」と「漏えいはありませんでした」は違う言葉です。前の文だけを使ってください。後ろの文は、ログが残っていない期間まで保証する言葉になります。

確認できなかった範囲

リリースを止めるのはセキュリティだけではありません。以下はレーンごとに、このレポートが何を見て何を見られなかったかです。見られなかった欄に問題がないという意味ではありません。見られなかったという意味です。

レーンこのレポートが見たもの 見られなかったもの — 見るために必要なもの有料
セキュリティ・データ アクセスルール、シークレット、決済の検証 — A〜D デプロイ環境の実際の権限動作(ステージング URL + テストアカウント)、Supabase・決済事業者の実際の設定(読み取り専用コネクタ)、過去のコミット履歴
費用 AI 呼び出しの上限 — E 実際の請求内容と使用の推移、転送量の課金と支出上限が実際に有効になっているか (ダッシュボードの読み取り権限)
インフラ・運用 依存関係 — F。そしてリポジトリに入っているコンテナ・サーバー設定 — Dockerfile の最後の USER、compose のポート表、設定モジュールのデバッグ・許可ホスト、nginx の設定 動いているインフラ — デプロイ設定、環境変数、バックアップ、ロールバック、可観測性。リポジトリの Dockerfile と compose が実際の配備構成だという保証もありません。クラウドへのアクセス権がありません 専門家レビューのみ
コード・ロジック 入力・検証の経路 — C テストの有無とカバレッジ、エラー経路の処理、重複・死んだコード、状態遷移の全数
設計・使いやすさ なし — 今回の検査範囲の外です モバイルのレイアウト、キーボード操作、中核となる導線の実際の画面 — アプリを動かさないと分かりません

セキュリティと費用の二つのレーンが厚いです。インフラ・運用はリポジトリに書かれているところまでで、残りの二つは薄いです。それが今のこの商品の形です。薄い欄を厚くするには、上に書いたアクセス権が必要です。

範囲の外で加わるものもあります。診断前の問答でどれが重要な機能かをまず決め、修正後の再検査をリリース判定は 1 回、専門家レビューは 2 回行います。専門家レビューには外部にお見せできる検証文書が含まれます。リリース後にこの判定を保ち続ける監視は、別の購読です。

このレポートが保証しないこと

  • 脆弱性がこれ以上ないという意味ではありません。上の範囲は検査していません。
  • 安全の認証ではありません。特定の時点、特定の範囲の観察結果です。
  • すでに悪用されたことがないという意味ではありません。確認可能なログの範囲だけを見ました。
  • 自動検査は、配備されたルールが知っているパターンしか見つけません。このアプリ固有の論理の穴は、コードを読んで初めて出ます。
  • 法律の助言ではありません。上の通知・届出の要件は条文どおりであり、適用の可否は窓口や代理人とご確認ください。
  • 修正後は必ず再検査してください。修正が別のものを壊すことがあります。

同じレポートをお受け取りください。

リポジトリのアドレスか URL をお送りいただければ、一日以内にまとめてお渡しします。無料です。