.envファイル — GitHubに秘密情報を漏らす6つの誤り
.envファイルの漏洩は、ハッカーによるものではなく、.gitignoreが設定される前に入力された開発者や、ライブデータを含む.env.exampleファイルを配布した、またはフレームワークがクライアントJSにサーバーの秘密情報を静かにバンドルした開発者が原因です。ここに実際に起こる6つの誤りを紹介します。
GitGuardianの2023年「セクレットの広がり」報告書によると、公開GitHubリポジトリに1200万件以上のセクレットがコミットされていることが判明した。多くは盗まれたものではなく、開発者が実際にそれを適切に扱ったと思い込んでアップロードしたものである。これらが原因となるパターンは以下の通りである。
1. 1回目のコミット後に.gitignoreを追加する
.gitignore は、未トラッキングファイルのみを追加するのを防ぎます。ファイルがすでにGitの履歴に含まれている場合、 に追加しても何も起こりません。Gitはそのファイルをまだトラッキングし、変更をコミットします。 ステージングされないファイル。一度ファイルが追跡されたら——たとえ一時的にでも——Gitの歴史に記録される。もしファイルを作成し .env、実行し git add . && git commit、その後に .env に .gitignore を追加した場合、その変更以前のすべてのコミットにファイルが含まれている。
すでに歴史に存在するか確認する:
git log --all -- .env
それがコミットを返す場合、セクレットは歴史に存在している。まず認証情報を回転させ、その後、 git-filter-repo ( git filter-branch):
pip install git-filter-repo
git filter-repo --path .env --invert-paths
の推奨代替品)を使用して歴史からファイルを削除する。すべてのリモートに強制プッシュし、チームメンバーに再クローンを通知する。この処理により、その削除前のすべてのクローン(自動CIシステムを含む)にコミットが存在する。
2. .envを.env.exampleにコピーするが、値をクリーンアップしない
標準的なワークフロー:実際の値を持つ .env を作成し、それを .env.example にコピーして、チームメンバーにプロジェクトが必要なキーを示す。コピーが問題の所在である。
cp .env .env.example はすべてをコピーする——キー と 値。そして .env.example はコミットされるべきである。それがその目的である。実際の値は .env.example に意図的に入っている。
❌ gitに残る内容:
DATABASE_URL=postgres://admin:supersecretpassword@prod-db.example.com/appdb
STRIPE_SECRET_KEY=sk_live_51AbcDefGhiJklMnopQrstUvwx...
JWT_SECRET=my-actual-production-jwt-secret
✅ 本来の内容: .env.example はプレースホルダー値で作成し、コミットした後、それを
DATABASE_URL=postgres://user:password@localhost:5432/appdb
STRIPE_SECRET_KEY=sk_live_YOUR_KEY_HERE
JWT_SECRET=generate-a-random-secret-min-32-chars
作成する .env.example にコピーし、実際の認証情報を埋め込む——逆の順序ではいけない。 .env 3. エラーハンドラーでプロセス.envをログ出力する
これはインシデント中に「一時的なデバッグ」として始まり、削除されない。あるいは、一般的なエラーメディエートで無害に見える。
実行時には、dotenvが読み込んだすべての変数およびシステム変数を含む。ログを出力する全オブジェクトを渡すことで、ログアグレゲーター、エラートレーキングサービス(Sentry、Datadog、Rollbar)、およびエラーナレッジ通知メールやWebhookに記録される。多くのサービスは、自社のアクセス制御を持つ第三者ストレージにデータを転送する。
// Classic debug line that makes it to production
console.log('Starting with config:', process.env);
// Generic error handler that dumps everything
app.use((err, req, res, next) => {
logger.error({ config: process.env, error: err.message });
res.status(500).json({ error: 'Internal server error' });
});
process.env 診断に必要な特定の値のみをログ出力する:
4. Dockerイメージレイヤーにセクレットを組み込む
logger.error({
nodeEnv: process.env.NODE_ENV,
appVersion: process.env.APP_VERSION,
error: err.message,
stack: err.stack
});
Dockerイメージの歴史に永続的にセクレットを埋め込む2つのパターン:
ファイルを後続のレイヤーで削除(
# Pattern 1: COPY bakes the entire .env into a layer
COPY .env .
# Pattern 2: ARG/ENV burns values into build metadata
ARG DATABASE_URL
ENV DATABASE_URL=$DATABASE_URL
)しても、値はイメージの歴史に読み取れる。画像のプルアクセスを持つ誰もが実行できる:RUN rm .envと、ビルド時のARG値を回収できる。Docker BuildKitのセクレットは、ビルド中にセクレットをマウントし、レイヤーに書かれない正しいツールである:
docker history --no-trunc your-image:tag
実行時の設定は、コンテナ開始時に
# syntax=docker/dockerfile:1
RUN --mount=type=secret,id=db_url DATABASE_URL=$(cat /run/secrets/db_url) ./setup.sh
を介して環境変数を注入する。Docker Composeでホスト環境変数を参照する——決してハードコードされた値、決して docker run -e または environment: ‘d secret files。 COPY5. 生産環境に送られる弱いプレースホルダーのセクレットを使用する
は開発用プレースホルダーから始まり、場合によっては置き換えされない。攻撃者がJWT署名をブートフォースする際に、これらの文字列を積極的に試している——GitHub検索で出現するため、特別なワードリストに含まれている。
JWT_SECRET=secret, SESSION_SECRET=keyboard cat, APP_KEY=changeme, ENCRYPTION_KEY=1234567890abcdefを署名し、弱いシークレットを使用したJWTは、c-jwt-crackerなどのツールでオフラインで破られることができる。
1つの検拾された有効なトークンがあれば、シークレットをブートフォースし、任意のトークンを発行できる。 HS256 適切なシークレットは暗号的にランダムで、最小32バイト以上であるべきである。必要になる前に生成する——IO Toolsは、JWTキー、セッションシークレット、APIキーなどの一般的な.envシークレットに対して、設定不要で適切なランダム値を生成する。最初から設定し、プレースホルダーを使用して「プロダクション前に修正する」という計画を立てない。 6. フレームワークの環境変数規則により、セクレットがクライアントに漏れるいくつかのポピュラーなフレームワークは、変数名のプレフィックスを使ってクライアントとサーバーの可視性を決定する。これが間違っていると、アプリを読み込むすべてのブラウザにJavaScriptバンドルにセクレットが明文で送られる。
Next.js: 環境シークレットジェネレーター -prefixed変数はクライアントサイドにバンドルされる。しかし、サーバー側のセクレットは
propsを通じて漏れ、
が返されたすべての値はページHTMLにシリアル化され、ソースコードで読み取れる。
- Vite: のみ
NEXT_PUBLIC_変数がgetServerSidePropsにプレフィックスされ、クライアントJSにバンドルされる。実際の開発者が「利便性のため」使用するのは誤りである。propsCreate React App: - 変数はクライアントバンドルに含まれ、例外がない。サーバー側のCRAランタイムは存在しない——すべてがブラウザに送られる。 ビルド後に出力ディレクトリを検索して、既知のセクレット値を確認する:
VITE_もしマッチが返された場合、そのセクレットはすべてのブラウザタブに存在している。すぐに回転し、バンドルされた他の内容を調査する。VITE_DATABASE_URL最初に構築すべき習慣 - セクレットを保持するファイルを作成する前に、まず すべて
REACT_APP_を設定する——後回しにしない。新しいリポジトリの最初のコミットは、プレースホルダー値で
を設定するべきである。これにより、
grep -r "sk_live_" ./dist
grep -r "sk_live_" ./.next/static
は数分以内にフレームワークごとの完全な無視ファイルを生成する。
上記の6つの誤りは、コードを書く前にすべて防げられる。セクレットが暴露された後は、回転が唯一の解決策であり、それはすべての場所で回転する必要がある——サービスプロバイダー、コピーを保持したすべての環境、ログにキャッシュされた値を持つすべてのシステムである。
.envファイル — GitHubにセクレットが漏れる6つの誤り 2 .gitignore .envファイル — GitHubにセクレットが漏れる6つの誤り 1 .gitignore と .env.example 多くの.envの漏洩はハッカーから来ていない——開発者が.gitignoreが設定されていない状態でファイルをコミットした、.env.exampleに実際の値を含めて配布した、またはフレームワークがサーバーのセクレットをクライアントJSに静かにバンドルしたことが原因である。以下が実際に起こる6つの誤りである。 .gitignore ジェネレーター 1分以内に、フレームワークごとの無視ファイルを完全に生成します。
上記の6つの誤りは、コードを書く前にすべて回避可能です。セレクトが暴露された後、唯一の解決策は回転することですが、それはすべての場所で行われなければなりません:サービスプロバイダー、コピーを保持していたすべての環境、ログに値がキャッシュされた可能性のあるすべてのシステムです。
