最初の演習は、いちばん単純で、いちばん被害が大きい穴から始めます。APIキーやデータベースの接続情報といった「鍵」が、コードや公開リポジトリやブラウザに紛れ込んでいないか。見つけ方は検索だけです。そして見つかったときにやるべきことは、消すことより先に、鍵を取り替えることです。
この章の学習目標
- 自分のアプリにある秘密情報の種類を列挙し、どこに置かれているべきかを説明できるようになる
- リポジトリ、Gitの履歴、Vercelの設定、ブラウザに届くコードの4か所を自分で確認できるようになる
- 漏れていた鍵を「無効化して再発行する」手順を、慌てずに実行できるようになる
秘密情報とは何か
秘密情報(シークレット)とは、それを知っているだけで、あなたのアプリやデータを自由に操作できてしまう文字列のことです。代表的なものは次の4つです。
- APIキー
- 外部サービスを呼び出すための合鍵。AIのAPI、メール送信、決済など。漏れると、あなたの名義と課金で他人が使えます。
- 接続文字列
- データベースに直接つなぐための住所と鍵がひとまとまりになったもの。漏れると、全テーブルの読み書きができます。
- 管理者パスワード
- 管理画面や管理用アカウントのパスワード。初期設定の値のまま、コードに書いてあることがあります。
- 署名用の鍵
- ログイン状態の証明書(トークン)を発行するための鍵。漏れると、誰にでもなりすませます。
経理の言葉で言えば、金庫の鍵、銀行印、ネットバンキングのワンタイムパスワード機です。鍵のコピーがどこに何本あるか分からない状態が、いちばん危険です。
秘密情報の置き場所は、第8章の図で言う「サーバー側」の、しかもコードの外です。具体的には、Vercelの環境変数の設定画面です。コードは人に見せるもの、鍵は見せないもの。この2つを分けておくのが基本で、OWASPのSecrets Management Cheat Sheetもそれを前提に書かれています。
よくある漏れ方
鍵が漏れる経路は、だいたい次の5つに集約されます。どれも「うっかり」で起きます。
- コードに直書き。AIが「とりあえず動くように」キーをコードに埋め込み、環境変数に移すのを忘れたまま公開されます。
- .envファイルをGitに含めてしまう。
.envは鍵をまとめて書いておくファイルです。Gitの管理対象から外し忘れると、リポジトリごと鍵が配られます。 - 公開リポジトリの履歴に残る。あとで消しても、Gitは過去の状態を全部覚えています。履歴をたどれば、消したはずの鍵が読めます。
- フロントエンドに埋め込む。ブラウザに届くコードに鍵が入っていれば、誰でも「ページのソースを表示」で読めます。Next.jsでは、
NEXT_PUBLIC_で始まる環境変数は、ビルド時にブラウザ向けのコードに埋め込まれる仕様です。公開してよい値だけに使います。 - スクリーンショットやチャットに貼る。設定画面の画像を同僚に送ったり、AIへの相談でファイルごと貼り付けたりして漏れます。
Supabaseの鍵は2種類ある
Supabaseを使っている方は、ここがいちばん間違えやすいところです。Supabaseの鍵には、ブラウザに出してよいものと、絶対に出してはいけないものがあります。
| 鍵の種類 | 従来の名称 | 新しい名称 | ブラウザに出してよいか | 行単位のアクセス制御(RLS) |
|---|---|---|---|---|
| 公開用 | anon キー | publishable キー(sb_publishable_ で始まる) | よい。公開前提の鍵 | 適用される |
| 管理用 | service_role キー | secret キー(sb_secret_ で始まる) | 絶対に出さない | 素通りする |
Supabaseの公式ドキュメントによれば、公開用の鍵は行単位のアクセス制御(RLS、Row Level Security。第10章で扱います)が効くことを前提に、ブラウザに置いてよいものとされています。一方、管理用の鍵はデータベース上で service_role という役割に対応し、RLSを素通りします。つまり管理用の鍵がブラウザに出ると、RLSで守っていたはずの全データが読み書きできる状態になります。新旧どちらの名称の鍵も、管理画面で無効化するまでは有効です。
経理で言えば、公開用の鍵は「窓口で誰でも受け取れる振込依頼書の用紙」、管理用の鍵は「金庫の鍵そのもの」です。用紙は配ってよい。鍵は配らない。
確認手順は「読むだけ」
確認は、検索と目視だけで済みます。何かを実行する必要はありません。見るべき場所は4つです。
- リポジトリの中身を検索する。VS Codeの検索(全ファイル対象)で、
key、secret、password、token、sb_secret、service_roleを順に探します。長い英数字の羅列が引っかかったら、それが何かをAIに聞きます。 .gitignoreを開く。.env、.env.local、.env*.localのような行があるかを見ます。なければ、.envファイルがGitに入っている可能性があります。- GitHubで履歴を見る。公開リポジトリなら、GitHubの画面でファイルの「History」を開き、過去に
.envやキーらしき文字列が含まれていたコミットがないかを見ます。GitHubには公開リポジトリの鍵を自動検知する「secret scanning」という仕組みもあり、検知されると通知が届きます。 - Vercelの環境変数画面を見る。プロジェクトの Settings から Environment Variables を開き、鍵がここに揃っているかを確かめます。Vercelには値を画面上でも隠す「Sensitive」という区分があり、鍵はこれにしておきます。あわせて、
NEXT_PUBLIC_で始まる変数の中に、秘密であるべき値が入っていないかを見ます。
新しい形式の管理用の鍵は
sb_secret_ で始まるので、この文字列が見つかれば露出しています。旧形式の鍵(anon/service_role)は eyJ で始まる長い文字列(JWT)で、役割の名前は英数字に変換された中身に埋め込まれているため、service_role と検索しても鍵そのものは見つかりません。eyJ で始まる長い文字列が見つかったら、Supabaseの管理画面の API Keys に表示される値と見比べ、公開用(anon)か管理用(service_role)かを確かめます。逆に、service_role という言葉がコードに出てきても、変数名や説明文のこともあり、それだけでは鍵の漏えいとは限りません。自分のアプリに対してだけ行ってください。
漏れていたら:削除ではなく、再発行
鍵がコードや履歴に入っていたと分かったとき、最初にやりたくなるのは「消す」ことです。それでは足りません。GitHubの公式ドキュメントは、一度リポジトリに入った秘密情報は漏えいしたものとみなし、履歴から取り除くのと別に、その鍵を無効化して新しいものに替えるよう求めています。履歴を書き換えても、誰かが既にクローンしていたり、フォークやキャッシュに残っていたりする可能性を消せないからです。
この「無効化して再発行する」ことを、ローテーションと呼びます。印鑑を紛失したときに、探すより先に改印届を出すのと同じ発想です。手順は次の通りです。順番が大切です。
- 外部サービス(Supabase、AIのAPIなど)の管理画面で、新しい鍵を発行します。古い鍵はまだ消しません。
- Vercelの環境変数を新しい鍵に差し替え、再デプロイします。
- アプリが正常に動くことを確認します。
- 外部サービス側で古い鍵を無効化します。Supabaseなら Settings の API Keys 画面で行います。
- コードから鍵を取り除き、環境変数から読むように直します。履歴の掃除はその後で、やりたければやります。古い鍵が死んでいれば、履歴に残っていても害はありません。
先に古い鍵を消すと、アプリが止まります。新口座への入金を確認してから旧口座を解約するのと同じ順番です。
このリポジトリに秘密情報(APIキー、データベース接続文字列、パスワード、トークン、署名用の鍵)が含まれていないかを調べて、報告してください。 調べる範囲: - すべてのソースコード、設定ファイル、.env系ファイル - .gitignore に .env 系のファイルが含まれているか - Gitの履歴(過去のコミットに含まれていたもの) - NEXT_PUBLIC_ など、ブラウザに届く変数名で秘密の値を扱っていないか - Supabase の secret キー(sb_secret_ で始まるもの、または service_role キー)がブラウザ側のコードから参照されていないか 報告の形式: - ファイル名と行番号、秘密情報の種類、「現在のコード」か「履歴のみ」か - 値そのものは絶対に出力しないでください。先頭4文字と文字数だけで示してください - 見つからなかった場合も、調べた範囲を列挙してください 修正はまだしないでください。報告だけお願いします。
演習1:秘密情報の棚卸し
- 第8章の全体図で、鍵マークを付けた場所を点検ノートに書き出します。
- 上の定型文でAIに捜索させ、報告を点検ノートに貼ります。
- AIの報告とは別に、自分でもVS Codeの検索で
secret、password、token、sb_secret、service_roleを探します。AIの見落としを人が補います。 .gitignoreを開き、.env系の行があるかを確認します。- GitHubの画面でリポジトリの公開・非公開を確認し、公開なら履歴に
.envが含まれた形跡がないかを見ます。 - Vercelの Environment Variables を開き、鍵が揃っているか、Sensitiveになっているか、
NEXT_PUBLIC_に秘密が混ざっていないかを見ます。 - 公開中のアプリで「ページのソースを表示」し、
sb_secret_とservice_roleを検索します。 - 1つでも漏れがあれば、「削除ではなく再発行」の手順で鍵を取り替え、点検ノートの対応内容と再確認日を埋めます。
まとめ
- 秘密情報はAPIキー、接続文字列、管理者パスワード、署名用の鍵。置き場所はコードの外、Vercelの環境変数。Supabaseの管理用の鍵(secret/service_role)は行単位のアクセス制御を素通りするので、ブラウザには絶対に出さない。
- 確認は検索と目視だけ。リポジトリ内、
.gitignore、Gitの履歴、Vercelの設定画面、ブラウザに届くコードの順に見る。 - 漏れていたら、消すのではなく取り替える。新しい鍵を入れて動作を確かめてから、古い鍵を無効化する。履歴の掃除は最後。
出典
- OWASP Cheat Sheet Series「Secrets Management Cheat Sheet」(OWASP)
- GitHub Docs「Removing sensitive data from a repository」(GitHub)
- GitHub Docs「Secret scanning」(GitHub)
- Supabase Docs「Understanding API keys」(Supabase)
- Vercel Docs「Sensitive environment variables」(Vercel)
- Next.js Docs「How to use environment variables in Next.js」(Vercel)