{"componentChunkName":"component---src-templates-blog-template-js","path":"/2026-05-15-weekly-knowledge","result":{"data":{"site":{"siteMetadata":{"title":"KenRp Blog"}},"markdownRemark":{"html":"<p><code class=\"language-text\">.env</code> を <code class=\"language-text\">.gitignore</code> に追加しても、すでに追跡されたファイルはGit管理から外れない。さらに、現在のファイルを消しても過去のコミットに値が残る。</p>\n<p>Go製APIサンプルで <code class=\"language-text\">.env</code> がコミットされていたため、追跡対象、設定例、Docker Compose、README、回帰テストを一つの契約として修正した。</p>\n<h2>この記事で解決すること</h2>\n<p>対象は、<code class=\"language-text\">.env</code> を誤ってコミットし、今後の再混入も防ぎたい人だ。次の順序で対応する。</p>\n<ol>\n<li>漏えいした可能性のある値を無効化する</li>\n<li><code class=\"language-text\">.env</code> を追跡対象から外す</li>\n<li>実値を持たない <code class=\"language-text\">.env.example</code> を作る</li>\n<li>アプリとDocker Composeの参照方法を揃える</li>\n<li>テストで再混入を検出する</li>\n</ol>\n<h2>最初に値をローテーションする</h2>\n<p>公開履歴へ入った秘密情報は、削除後も取得済みと考える。JWT署名鍵、DBパスワード、APIキーなどは、Git操作より先に利用先で無効化・再発行する。</p>\n<p>履歴修正は露出を減らす処置であり、値を再び安全にする処置ではない。この二つを同じ作業として扱わないことが重要だ。</p>\n<h2>実装手順</h2>\n<p>追跡状態を確認してから、安全な設定例へ置き換える。</p>\n<p>まず、Gitが現在どの設定ファイルを追跡しているか確認する。</p>\n<div class=\"gatsby-highlight\" data-language=\"bash\"><pre class=\"language-bash\"><code class=\"language-bash\"><span class=\"token function\">git</span> ls-files .env .env.example</code></pre></div>\n<p><code class=\"language-text\">.env</code> が表示される場合、<code class=\"language-text\">.gitignore</code> の追加だけでは不十分だ。作業用ファイルは手元に残し、インデックスから外す。</p>\n<div class=\"gatsby-highlight\" data-language=\"bash\"><pre class=\"language-bash\"><code class=\"language-bash\"><span class=\"token function\">git</span> <span class=\"token function\">rm</span> --cached .env</code></pre></div>\n<p>そのうえで <code class=\"language-text\">.gitignore</code> に <code class=\"language-text\">.env</code> を追加する。リポジトリにはキー名だけを示す <code class=\"language-text\">.env.example</code> を置き、値は明確なプレースホルダーにする。</p>\n<div class=\"gatsby-highlight\" data-language=\"env\"><pre class=\"language-env\"><code class=\"language-env\">POSTGRES_USER=change-me\nPOSTGRES_PASSWORD=change-me\nSECRET=replace-with-a-long-random-value\nGO_ENV=dev</code></pre></div>\n<p><code class=\"language-text\">your-secret-key</code> のようなありがちな固定値もサンプルに残さない。コピー後に変更されず、本番へ流用される可能性があるためだ。</p>\n<h2>設定の参照元を揃える</h2>\n<p>アプリだけを直しても、<code class=\"language-text\">docker-compose.yml</code> に値が直書きされていれば目的を達成できない。PostgreSQLのユーザー名、パスワード、DB名も環境変数を参照する形へ揃えた。</p>\n<p>利用者向けのREADMEには、次の初期化手順と、変更が必須な値を明記する。</p>\n<div class=\"gatsby-highlight\" data-language=\"bash\"><pre class=\"language-bash\"><code class=\"language-bash\"><span class=\"token function\">cp</span> .env.example .env\ndocker compose up -d\ngo <span class=\"token builtin class-name\">test</span> ./<span class=\"token punctuation\">..</span>.</code></pre></div>\n<p>設定例、実行環境、説明が同じキー名を使うことで、秘密情報を除去した結果として開発環境が起動不能になる事故を防げる。</p>\n<h2>検証結果</h2>\n<p>人の注意だけでは再発するため、GoのテストからGitの追跡状態を検査するようにした。</p>\n<div class=\"gatsby-highlight\" data-language=\"go\"><pre class=\"language-go\"><code class=\"language-go\"><span class=\"token keyword\">func</span> <span class=\"token function\">TestEnvironmentSecretsAreNotTracked</span><span class=\"token punctuation\">(</span>t <span class=\"token operator\">*</span>testing<span class=\"token punctuation\">.</span>T<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n\tcmd <span class=\"token operator\">:=</span> exec<span class=\"token punctuation\">.</span><span class=\"token function\">Command</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"git\"</span><span class=\"token punctuation\">,</span> <span class=\"token string\">\"ls-files\"</span><span class=\"token punctuation\">,</span> <span class=\"token string\">\".env\"</span><span class=\"token punctuation\">,</span> <span class=\"token string\">\".env.example\"</span><span class=\"token punctuation\">)</span>\n\tout<span class=\"token punctuation\">,</span> err <span class=\"token operator\">:=</span> cmd<span class=\"token punctuation\">.</span><span class=\"token function\">Output</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n\t<span class=\"token keyword\">if</span> err <span class=\"token operator\">!=</span> <span class=\"token boolean\">nil</span> <span class=\"token punctuation\">{</span>\n\t\tt<span class=\"token punctuation\">.</span><span class=\"token function\">Fatalf</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"git ls-files failed: %v\"</span><span class=\"token punctuation\">,</span> err<span class=\"token punctuation\">)</span>\n\t<span class=\"token punctuation\">}</span>\n\n\t<span class=\"token keyword\">for</span> <span class=\"token boolean\">_</span><span class=\"token punctuation\">,</span> path <span class=\"token operator\">:=</span> <span class=\"token keyword\">range</span> strings<span class=\"token punctuation\">.</span><span class=\"token function\">Fields</span><span class=\"token punctuation\">(</span><span class=\"token function\">string</span><span class=\"token punctuation\">(</span>out<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n\t\t<span class=\"token keyword\">if</span> path <span class=\"token operator\">==</span> <span class=\"token string\">\".env\"</span> <span class=\"token punctuation\">{</span>\n\t\t\tt<span class=\"token punctuation\">.</span><span class=\"token function\">Fatal</span><span class=\"token punctuation\">(</span><span class=\"token string\">\".env must not be tracked; commit .env.example instead\"</span><span class=\"token punctuation\">)</span>\n\t\t<span class=\"token punctuation\">}</span>\n\t<span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span></code></pre></div>\n<p>別のテストでは <code class=\"language-text\">.env.example</code> に必須キーがあることと、既知の危険なサンプル値がないことを確認する。今回の再検証では全パッケージが成功した。</p>\n<div class=\"gatsby-highlight\" data-language=\"bash\"><pre class=\"language-bash\"><code class=\"language-bash\">go <span class=\"token builtin class-name\">test</span> ./<span class=\"token punctuation\">..</span>.\n\n<span class=\"token comment\"># ok   go-sample-api</span>\n<span class=\"token comment\"># controller、db、modelなども失敗なし</span></code></pre></div>\n<p>このテストは「Gitに追跡された <code class=\"language-text\">.env</code>」を防ぐ。未追跡ファイルの中身や、別名で保存された鍵までは検出しないため、秘密情報スキャナとの併用が必要だ。</p>\n<h2>履歴を調査する</h2>\n<p>現在のツリーを直した後、過去のコミットに <code class=\"language-text\">.env</code> が存在した範囲を確認する。</p>\n<div class=\"gatsby-highlight\" data-language=\"bash\"><pre class=\"language-bash\"><code class=\"language-bash\"><span class=\"token function\">git</span> log --all -- .env</code></pre></div>\n<p>公開リポジトリから履歴を除去する場合は、共同作業者への影響を確認してから <code class=\"language-text\">git filter-repo</code> などを使う。履歴を書き換えると既存cloneとの整合が崩れるため、無断でforce pushしてはいけない。</p>\n<h2>採用理由と代替案</h2>\n<p><code class=\"language-text\">.env.example</code> を採用したのは、必要なキーをコード外で一覧化しつつ、実値を配布しないためだ。ただし本番の秘密情報管理にはならない。本番ではホスティング基盤のSecret機能や専用のSecret Managerを使う。</p>\n<p>回帰テストには既存の <code class=\"language-text\">go test</code> を使った。追加ツールなしで必ず実行できる利点がある。一方、値のパターン検出や履歴全体の走査は得意ではないため、CIではgitleaksなどを追加する余地がある。</p>\n<h2>完了条件</h2>\n<ul>\n<li>漏えいした可能性のある値をローテーションした</li>\n<li><code class=\"language-text\">git ls-files .env</code> が何も返さない</li>\n<li><code class=\"language-text\">.env.example</code> はキー名と安全なプレースホルダーだけを含む</li>\n<li>アプリ、Docker Compose、READMEでキー名が一致する</li>\n<li><code class=\"language-text\">go test ./...</code> と秘密情報スキャンが成功する</li>\n<li>履歴修正の要否と共同作業者への影響を確認した</li>\n</ul>\n<p>ファイルを消すだけでは、秘密情報対応は完了しない。値の失効、追跡解除、開発手順の再構築、機械的な再発防止までを一つの修正として扱う必要がある。</p>","excerpt":"を  に追加しても、すでに追跡されたファイルはGit管理から外れない。さらに、現在のファイルを消しても過去のコミットに値が残る。 Go製APIサンプルで  がコミットされていたため、追跡対象、設定例、Docker Compose、README…","frontmatter":{"date":"2026年05月15日","path":"/2026-05-15-weekly-knowledge","title":"Git管理された.envを安全な構成へ戻す手順","thumbnail":"/kenRp-blog/assets/codex-worklog-notebook.jpg","metaDescription":"誤ってGit管理した.envを除外し、.env.example、Docker Compose、回帰テストまで整える手順と、履歴削除だけでは終わらない注意点を解説します。","category":"Knowledge","tags":["git","security","go"]}}},"pageContext":{"slug":"/2026-05-15-weekly-knowledge"}}}