昨年、YCのスタートアップで働く開発者が、.envファイルを公開GitHubリポジトリにプッシュしてしまいました。そこにはStripeの本番鍵、OpenAIのAPIキー、本番データベースのURLが含まれていました。自動スキャナは30秒以内にそれを見つけました。GitHubのシークレットスキャンが認識したトークンを無効化するまでに、誰かがすでにOpenAI APIの請求で14,000ドル分を使い切っていました。
これは珍しいことではありません。GitGuardianの2024年の「State of Secrets Sprawl(シークレットの拡散実態)」レポートでは、1年間で公開GitHubリポジトリに露出した新しいシークレットは1,280万件でした。前年から28%の増加です。そしてこれらは公開リポジトリだけです。プライベートはもっと悪いです。誰もスキャンしていないからです。
1,280万
公開リポジトリで露出したシークレット(2024年)
28%
前年からの増加
あなたの.envファイルは負債です。理由と、どうするべきかを説明します。
The dotenvパターンは、そもそもセキュリティ機構ではなかった
dotenvパターンは2012年にRubyコミュニティから出てきました。解決したのは現実的な問題です。開発者がデータベースのパスワードやAPIキーをソースファイルに直書きしてしまっていたことです。それらを別ファイルに移し、.gitignoreで追跡しないようにするのは、本当に改善でした。
しかし、いま開発者のマシンにある典型的な.envはこうなっています:
OPENAI_API_KEY=sk-proj-abc123...
STRIPE_SECRET_KEY=sk_live_...
DATABASE_URL=postgresql://admin:password@prod-db.example.com:5432/main
CLOUDFLARE_API_TOKEN=v4x...
AWS_SECRET_ACCESS_KEY=wJalrXUtnFEMI...
本番クレデンシャルが5つ。平文です。読み取るための認証がありません。保存時暗号化もありません。監査ログの痕跡もありません。あなたのユーザーとして動いているあらゆるプロセス――不正なnpmのpostinstallスクリプト、侵害されたVS Code拡張機能、うっかりしたdocker build――が、あなたに知られることなくそのファイルを読めてしまいます。
それはセキュリティではありません。監視モニタに「金庫の暗証番号を書いた付箋」を貼っているのと同じです。
なぜ .envファイルのセキュリティは大規模になると破綻するのか
単一の.envファイルなら管理できます。誰も1つの.envファイルだけを持っていません。
こちらで数えてみました。47です。アクティブなプロジェクトにあるものもあれば、アーカイブされたリポジトリにもあります。コピーされてイメージになってしまったDockerコンテキストもありました。まったく同じCloudflare APIトークンが6つの異なるファイルに出ていました。ローテーションしたとき、3つは更新したものの、残り3つを数週間見逃しました。(この監査の全文はこちら。)
有効期限の追跡がありません。どのシークレットがまだ有効かを知る方法がありません。どのプロセスがどのキーを読んだかの監査ログもありません。侵害が起きると、インシデント対応は「待って、そもそもこのキーはどこにあるんだ?」から始まり、ホームディレクトリ全体に対するgrep -rで終わります。
警告
GitHubのプッシュ保護は、GitHub、AWS、Google Cloud、および他にもいくつかのプロバイダからのトークンを自動で無効化します。ですが、多くのサービスにはその連携がありません。あなたのStripeキー、Postmarkのトークン、独自APIクレデンシャル――それらが公開リポジトリに到達してしまっても、自動で無効化してくれるところは多くありません。請求が来たとき、あるいはデータが消えたときに気づくことになります。
AIエージェントが .envファイルを致命的な脆弱性に変えた
.envのパターンは、「あなたのアプリケーションだけがそのファイルを読む」という前提に立っていました。その前提は2025年に崩れました。
AIコーディングエージェント(Claude Code、Cursor、GitHub Copilot、Codex)は、文脈を作るためにプロジェクトファイルを日常的に読み取ります。あなたの.envはプロジェクトファイルです。エージェントがそれを読むと、そのファイル内のすべてのシークレットが、モデルのコンテキストウィンドウに入ります。そこから、生成されたコード、デバッグ出力、エラーメッセージ、会話ログに値が現れる可能性があります。
エージェントが悪意を持っているわけではありません。助けようとしているのです。「こちらがエラーです――あなたのDATABASE_URLは接続が拒否されます」——そしてその結果、あなたの本番データベースのホスト名とクレデンシャルがチャット履歴に載ってしまいます。チームと共有されます。プロバイダに記録されます。さらに、学習データとして使われる可能性もあります。
6 Ways AI Agents Leak Your Secrets(AIエージェントがあなたのシークレットを漏えいさせる6つの方法)では、特定の漏えい経路についてさらに詳しく書きました。要点はこうです。エージェントが読めるファイル内にシークレットがあるなら、「読まれる」と仮定してください。
.envファイルの代替:Touch ID付きのmacOS Keychain
macOS Keychainは、20年以上前からすべてのMacに搭載されている暗号化クレデンシャルストアです。Secure Enclaveに支えられており、Touch IDで保護され、Safari、SSH、そしてAppleが署名したすべてのアプリケーションで使われています。スタートアップが作った独自の金庫ではありません。Appleが維持・監査しているインフラです。
NoxKeyはKeychainの上に薄く重ねたレイヤーで、開発者のシークレットのために作られています:
変更前:平文の .env
# 平文ファイル。誰でも read できる
$ cat .env
STRIPE_SECRET_KEY=sk_live_51Hx...
変更後:Keychain + Touch ID
# Keychain上で暗号化、アクセスのたびにTouch ID
$ noxkey set myorg/project/STRIPE_KEY --clipboard
✓ Stored myorg/project/STRIPE_KEY
$ eval "$(noxkey get myorg/project/STRIPE_KEY)"
# → Touch IDのプロンプト → シークレットがシェルに読み込まれる
# → 生の値はディスクに書き込まれない
$ echo $STRIPE_KEY
sk_live_51Hx...
シークレットは1つ、場所も1つ。どのプロジェクトのディレクトリからでもアクセス可能です。ディスク上にファイルはありません。毎回Touch IDです。AIエージェントがnoxkey getを呼び出すと、エージェントのプロセスツリーを検出し、生の値ではなく暗号化した引き渡しを返します――シークレットは環境変数には到達しますが、会話コンテキストには入りません。
エージェントがnoxkey getを呼び出す → プロセスツリーを検出 → AES-256-CBCで暗号化 → シークレットはenvには入るが、コンテキストには入らない
数分で .env から Keychain へ移行する
# 既存の .env ファイルをインポート
$ noxkey import myorg/project .env
✓ 5つのシークレットをインポートしました
返却形式: {"translated": "翻訳されたHTML"}
$ noxkey ls myorg/project/ myorg/project/STRIPE_SECRET_KEY myorg/project/OPENAI_API_KEY myorg/project/DATABASE_URL myorg/project/CLOUDFLARE_API_TOKEN myorg/project/AWS_SECRET_ACCESS_KEY # 値をのぞいて確認する (先頭8文字を表示) $ noxkey peek myorg/project/STRIPE_SECRET_KEY sk_live_... # 今すぐ負債を削除する $ rm .env
これをすべてのプロジェクトで行ってください。私たちは午後1回で47件やりました。最初のきっかけになったfindコマンド:
$ find ~/dev -name ".env" -not -path "*/node_modules/*" -not -path "*/.git/*" | wc -l
47
CI/CDはどうするの?
このアプローチはローカル開発向けです——つまり、あなたのマシン、あなたのシークレット、あなたのTouch IDです。CI/CDシステムには独自のシークレット管理があります:GitHub Actionsのsecrets、Cloudflareの環境変数、AWS Parameter Store、Vault。これらはその環境のために作られていて、ちゃんと動きます。
問題は「CIで管理されたシークレット」と「あなたのラップトップ上のシークレット」の間にあるギャップです。現在そのギャップを埋めているのは、認証も暗号化もアクセス制御もない平文ファイルです。そこは、あなたのオペレーティングシステムの資格情報ストア(credential store)で埋めるべきです。
dotenvを捨てたままにすることの正直なトレードオフ
これはmacOS専用です。チームがLinuxやWindowsの場合、NoxKeyはその答えではありません(ただし原則は同じです——平文ファイルではなく、あなたのOSの資格情報ストアを使う)。
摩擦があります。アクセスのたびにTouch IDが必要になるので、以前よりも多くの認証が発生します。セッションのロック解除(noxkey unlock myorg/project)によって、これを1つのバッチにつき1回の認証に減らせますが、それでも.envが提供する「認証ゼロ」よりは多いままです。この摩擦こそがポイントです——つまり、実際にアクセスをガードしているということを意味します。
ワークフローを更新する必要があります。.env.exampleをコピーして値を埋める代わりに、noxkey importを一度実行し、evalコマンドを使います。私たちが本能的に.envに手を伸ばさなくなるまで、だいたい1日かかりました。
あなたのAPIキーは、平文ファイルよりも良い扱いを受けるべき
昨年は、公的なリポジトリで1,280万件のシークレットが露出しました。AIエージェントがあなたのプロジェクトディレクトリ内のすべてのファイルを読み取ります。サプライチェーン攻撃が平文の認証情報を狙うこともあります。.envのパターンは2012年には良いアイデアでした。しかし2026年では負債です。
あなたのオペレーティングシステムには、生体認証付きの、暗号化され、ハードウェアに裏付けられた資格情報ストアがあります。それを使ってください。
Key Takeaway
あなたの.envファイルには、暗号化も認証もアクセス制御もありません。すべてのAIエージェント、すべての不正なスクリプト、そしてうっかりしたgitへのプッシュで読めてしまいます。シークレットをmacOSのKeychainに移してください——場所は1つだけで、アクセスのたびにTouch IDを有効にし、AIエージェントは生の値を決して見られないようにします。移行はプロジェクトごとに数分で完了します。
brew install no-box-dev/noxkey/noxkey
無料。アカウント不要。クラウド不要。シークレットはあなたのマシンにとどまります。
よくある質問
*.envファイルはなぜ不安全なのですか?*
.envファイルは、暗号化も認証もアクセス制御もなしに、ディスク上にシークレットを平文として保存します。ファイルシステムへのアクセスがあるあらゆるプロセス、スクリプト、AIエージェントが読み取れます。さらに、それらがgitにコミットされがちです——2024年にはGitHubで1,280万件のシークレットが露出しました。
*dotenvの代わりに何を使うべきですか?*
オペレーティングシステムの資格情報ストアを使ってください。macOSでは、KeychainがTouch ID認証付きのハードウェア暗号化ストレージを提供します。NoxKeyはKeychainを開発者向けのCLIでラップします:eval "$(noxkey get myorg/KEY)"がprocess.env.KEYの代わりになります。
*AIエージェントは .envファイルを読み取れますか?*
はい。Claude Code、Cursor、CopilotのようなAIコーディングツールはファイルシステムへのアクセスがあり、プロジェクトディレクトリ内の任意の .envファイルを読み取れます。NoxKeyは、シークレットをKeychainに保存し、AIエージェントが検知されたときに暗号化された引き渡しを通じて提供することで、これを防ぎます。
*.envからmacOSのKeychainへはどう移行しますか?*
NoxKeyをインストール(brew install no-box-dev/noxkey/noxkey)し、次にnoxkey import myorg .envを実行してすべてのシークレットをKeychainに移動します。その後、.envファイルを削除してください。移行はプロジェクトごとに約1分です。
NoxKeyは無料でオープンソースです。brew install no-box-dev/noxkey/noxkey — GitHub | Website