Claude Codeで始めるシークレット管理: .envから本番ローテーションまで
Claude Codeで.env、CI/CD、クラウド鍵、ログ秘匿を安全に扱う実践ガイド。
APIキー、データベースURL、OAuthのclient secret、クラウドのデプロイ権限。これらは「動くアプリ」に必ず必要ですが、扱いを一度間違えると、GitHubの履歴、CIログ、スクリーンショット、記事のサンプルコードに残り続けます。Claude Codeに調査や実装を任せるほど、シークレットを見せない、保存しない、出力しない境界線を先に決める必要があります。
ここでいうシークレット管理は、秘密の値を暗記する話ではありません。コードから設定を分離し、環境ごとに権限を分け、必要最小限の人とプロセスだけが読めるようにし、漏れたらすぐ無効化して入れ替える運用です。The Twelve-Factor AppのConfigがいう通り、設定はコードに埋め込まず環境から注入します。ただし、.envを置くだけでは足りません。.gitignore、.env.example、バリデーション、ログのマスク、CI/CD secrets、クラウドのSecret Manager、ローテーション手順までを1本の線でつなぐ必要があります。
flowchart LR
Dev["Local .env"] --> Loader["Validated config loader"]
CI["CI/CD secrets"] --> Deploy["Deployment runtime"]
Store["AWS / GCP / Azure secret store"] --> Deploy
Deploy --> App["Application process"]
App --> Logs["Redacted logs"]
1. 何をどこに置くかを先に決める
初心者が最初に混乱するのは「どれがシークレットで、どこに置くべきか」です。目安は単純で、漏れたときに第三者が課金、送信、閲覧、書き込み、ログインをできる値はシークレットです。逆に公開URL、機能フラグの名前、OAuthのclient IDのように単体では権限を持たない値は、機密度を下げて扱えます。
| ユースケース | 例 | ローカル | 本番 |
|---|---|---|---|
| メール/API連携 | SendGrid API key、Stripe secret key、GitHub token | テスト用キーを.envに入れる | CI/CD secretsかSecret Managerから注入 |
| データベース | DATABASE_URL、接続ユーザー、パスワード | 開発DB専用ユーザー | 本番DBの読み書き権限を分ける |
| デプロイ | AWS/GCP/Azureのクラウド認証情報 | 原則ローカルに長期鍵を置かない | OIDCやデプロイ専用ロールを使う |
| OAuth | OAUTH_CLIENT_SECRET、Webhook signing secret | ローカル用アプリを別に作る | prod専用client secretを保管 |
この表で大事なのは「本番キーをローカルで再利用しない」ことです。SendGridやStripeはテストキーを用意できます。GitHub tokenはリポジトリ単位、期限付き、必要スコープだけにします。クラウドは個人の長期アクセスキーより、CIから短時間だけ引き受けるデプロイロールのほうが安全です。これが最小権限、つまり必要な作業に必要な権限だけを渡す考え方です。
2. リポジトリには値ではなく型を置く
.envはローカルの秘密箱ですが、リポジトリに入れてはいけません。代わりに.env.exampleを置き、「どの名前が必要か」「どんな形式か」「どこで取得するか」を共有します。新メンバーは環境変数管理の記事と合わせて読むと、オンボーディング時の質問が大きく減ります。
# Local secrets
.env
.env.*
!.env.example
!.env.test.example
# Logs and generated output that may contain tokens
npm-debug.log*
yarn-debug.log*
coverage/
dist/
# .env.example - placeholders only, never real values
NODE_ENV=development
APP_BASE_URL=http://localhost:3000
# Use a local database user, not production credentials.
DATABASE_URL=postgres://app_user:replace-me@localhost:5432/app_dev
# Use test/sandbox keys for local development.
SENDGRID_API_KEY=SG.xxxxxx
STRIPE_SECRET_KEY=sk_test_xxxxxx
GITHUB_TOKEN=ghp_xxxxxx
# OAuth secrets must be separated by environment.
OAUTH_CLIENT_ID=local-client-id
OAUTH_CLIENT_SECRET=replace-me
# Deployment reads these from CI or a cloud secret store.
AWS_REGION=ap-northeast-1
DEPLOY_ROLE_ARN=arn:aws:iam::123456789012:role/app-deploy-dev
Claude Codeに頼むときは「.env.exampleは読んでよいが、.envの値は開かない」と指示します。もし実装に実値が必要なら、チャットに貼るのではなく、あなたがローカルやCIの設定画面で入力し、Claude Codeにはキー名だけを伝えます。
3. Node.jsでは起動時に検証し、ログでは必ずマスクする
秘密の値は「存在するか」「URLとして正しいか」「今の環境で使ってよいか」を起動時に落とすほうが安全です。以下はdotenvとenvalidを使ったコピペ可能な設定ローダーです。ポイントは、アプリ内部では型付きの設定として扱い、ログ出力時はredactConfigを通すことです。
import { config as loadDotenv } from "dotenv";
import { cleanEnv, str, url } from "envalid";
const envFile = process.env.NODE_ENV === "test" ? ".env.test" : ".env";
loadDotenv({ path: envFile });
const secretKeyPattern = /(KEY|TOKEN|SECRET|PASSWORD|DATABASE_URL|PRIVATE)/i;
const env = cleanEnv(process.env, {
NODE_ENV: str({
choices: ["development", "test", "staging", "production"],
default: "development",
}),
APP_BASE_URL: url({ default: "http://localhost:3000" }),
DATABASE_URL: url(),
SENDGRID_API_KEY: str(),
STRIPE_SECRET_KEY: str(),
GITHUB_TOKEN: str(),
OAUTH_CLIENT_ID: str(),
OAUTH_CLIENT_SECRET: str(),
AWS_REGION: str({ default: "ap-northeast-1" }),
DEPLOY_ROLE_ARN: str(),
});
export function appConfig() {
return Object.freeze({
nodeEnv: env.NODE_ENV,
appBaseUrl: env.APP_BASE_URL,
databaseUrl: env.DATABASE_URL,
sendgridApiKey: env.SENDGRID_API_KEY,
stripeSecretKey: env.STRIPE_SECRET_KEY,
githubToken: env.GITHUB_TOKEN,
oauthClientId: env.OAUTH_CLIENT_ID,
oauthClientSecret: env.OAUTH_CLIENT_SECRET,
awsRegion: env.AWS_REGION,
deployRoleArn: env.DEPLOY_ROLE_ARN,
});
}
export function redactValue(key, value) {
if (!secretKeyPattern.test(key)) return value;
if (!value) return "<empty>";
const text = String(value);
if (text.length <= 8) return "<redacted>";
return `${text.slice(0, 4)}...${text.slice(-4)}`;
}
export function redactConfig(config) {
return Object.fromEntries(
Object.entries(config).map(([key, value]) => [key, redactValue(key, value)]),
);
}
if (process.argv[1] === new URL(import.meta.url).pathname) {
console.log(redactConfig(appConfig()));
}
失敗例としてよくあるのは、デバッグ中にconsole.log(process.env)を入れ、そのままCIログやNotion、ブログ記事に貼ってしまうことです。Claude Codeにも「ログを見せるから原因を探して」と雑に頼むと、貼り付けたログをもとにコードやドキュメントへ秘密値を再出力する危険があります。ログは渡す前にマスクし、Claude Codeが生成するテストや記事にも実値を含めないルールにします。
4. Claude Codeには権限境界をプロンプトで渡す
Claude Codeは強力ですが、秘密値を読ませる必要はほとんどありません。読ませるべきなのは、変数名、設定ファイルの構造、権限の目的、失敗しているエラーメッセージのマスク済み部分です。次の境界プロンプトを、セキュリティ関連タスクの最初に貼ってください。
You may inspect .env.example, package.json, deployment manifests, and secret names.
Do not open, print, summarize, store, or copy .env, CI/CD secret values, cloud credentials, production dumps, or screenshots containing tokens.
When you need a value, ask me to set it outside chat and confirm only the variable name.
If a secret appears in command output, stop, redact it, and report which file or command exposed it.
Before changing permissions, explain the least-privilege scope and ask for approval.
Do not paste real secrets into prompts, logs, documentation, code comments, tests, tickets, or articles.
加えて、Claude Codeの実行許可も絞ります。.envを読むコマンド、printenv、クラウド認証情報を表示するCLI、スクリーンショット取得、CIログの丸ごと貼り付けは、事前確認を必須にします。秘密値を知らない状態でも、.env.example、型定義、エラーメッセージ、公式ドキュメントがあれば、ほとんどの修正は進められます。
5. CI/CDとクラウドのSecret Managerを使い分ける
ローカルは.env、CIはGitHub Actionsやホスティング基盤のSecrets、本番はクラウドのSecret Managerという分担が現実的です。AWSならAWS Secrets Manager、Google CloudならSecret Manager、AzureならKey Vaultを使います。GitHubにはsecret scanningもあるので、公開リポジトリだけでなく組織リポジトリでも有効化しておきます。
name: deploy
on:
workflow_dispatch:
jobs:
deploy:
runs-on: ubuntu-latest
permissions:
contents: read
id-token: write
env:
NODE_ENV: production
AWS_REGION: ap-northeast-1
steps:
- uses: actions/checkout@v4
- name: Validate required secret names
run: test -n "${{ secrets.DEPLOY_ROLE_ARN }}" && test -n "${{ secrets.DATABASE_URL }}"
- name: Deploy without echoing secrets
env:
DATABASE_URL: ${{ secrets.DATABASE_URL }}
DEPLOY_ROLE_ARN: ${{ secrets.DEPLOY_ROLE_ARN }}
run: npm run deploy
CIで注意すべき落とし穴は、echo $DATABASE_URLのように確認のために表示してしまうことです。GitHub Actionsは一部のsecretsを自動でマスクしますが、加工した値、JSONに埋めた値、URLエンコードした値までは常に守れるとは限りません。Claude CodeにCI修正を頼むときも「secret値をechoしない」「失敗時はキー名だけ出す」「権限はcontents: readから増やす理由を説明する」と明記します。
6. ローテーションは事故後ではなく平時の手順にする
シークレットは「漏れてから慌てて変える」ものではなく、定期的に入れ替えられる状態にしておきます。SendGridやStripeのようなAPIキー、GitHub token、DBパスワード、OAuth client secret、クラウドロールの信頼ポリシーは、所有者、期限、影響範囲を台帳化します。
## Secret rotation checklist
- [ ] Identify owner, environment, consumers, and business impact.
- [ ] Create a new secret with the smallest required scope.
- [ ] Store it in CI/CD secrets or the cloud secret store.
- [ ] Deploy one service or job with the new value.
- [ ] Confirm logs and metrics without printing the secret.
- [ ] Revoke the old secret.
- [ ] Scan Git history, tickets, docs, screenshots, and chat snippets.
- [ ] Record the rotation date and next review date.
Claude Codeには、このチェックリストをIssueやRunbookに落とし込ませるのが向いています。ただし「古いキーをここに貼るので置換して」と依頼してはいけません。値は人間が管理画面で差し替え、Claude Codeには「SENDGRID_API_KEYをv2へ切り替えるため、参照箇所と検証手順を洗い出して」と頼みます。
7. よくある失敗を先に潰す
公開前レビューでは、次の失敗例を批判的に確認します。1つでも該当するなら公開やデプロイを止めます。
.envや.env.productionをコミットした。履歴から消しただけで、キーの無効化をしていない。- スクリーンショット、CIログ、エラーレポート、記事のコード例にtokenやDB URLが写っている。
- クラウドのキーに
AdministratorAccessのような広すぎる権限を付けている。 - 本番のStripe key、SendGrid key、DB URLをローカル開発や検証環境で使い回している。
- OAuth client secretやWebhook signing secretのローテーション手順がなく、担当者の記憶に依存している。
- Claude Codeや別のAIに実値を貼り、その内容をREADME、テスト、チケット、ブログ下書きに再利用してしまった。
セキュリティ事故は技術だけではなく運用の穴から起きます。セキュリティ監査チェックリストで権限を棚卸しし、セキュリティ失敗事例で典型的な事故パターンをチームに共有しておくと、レビューの目が揃います。API実装側はAPI開発ガイド、メール送信はメール自動化の記事も合わせて確認してください。
8. チーム導入時の現実的な進め方
最初から全サービスをSecret Manager化しようとすると止まります。まずは1つのアプリで.gitignore、.env.example、設定ローダー、ログマスク、CI secrets、ローテーション台帳を通しで整備します。その後、SendGrid、Stripe、GitHub token、DATABASE_URL、OAuth client secret、クラウドデプロイ権限の順に移すと、影響範囲を確認しやすくなります。
ClaudeCodeLabの研修・コンサルティングでは、既存リポジトリを題材に、Claude Codeへ渡してよい情報と渡してはいけない情報、CI/CD secretsの設計、GitHub secret scanning、Secret Manager移行、ローテーションRunbookまで一緒に作ります。単なる講義ではなく、チームの実リポジトリに合わせたテンプレートを残す形にすると、翌日からレビューで使えます。
まとめると、シークレット管理は「隠す」だけでは不十分です。値をコードから分離し、環境ごとに分け、起動時に検証し、ログではマスクし、権限を絞り、漏えい前提でローテーションできる状態にする。Claude Codeは、この仕組みを作るには強力ですが、秘密値そのものを渡す相手ではありません。
この記事で紹介した内容を実際に試した結果、Masaの検証用Node.jsアプリでは、.env.exampleから不足値を見つけ、redactConfigでCIログのDB URLとAPIキーを伏せ、GitHub Actionsのdeploy jobから不要なwrite権限を外せました。一方で、古いスクリーンショットにStripeのテストキーが写っていたため、記事化前にキーを再発行しました。コードだけでなく、ログ、画像、下書きまで見る運用が必要だと確認できました。
無料PDF: Claude Code はじめてのチートシート
まずは無料PDFで基本コマンドと最初の使い方をまとめて確認してください。登録後はそのままテンプレート集や導入相談にも進めます。
スパムは送りません。登録情報は厳重に管理します。
Claude Codeを仕事で使える形にしませんか?
無料PDFで基礎を固めたあと、すぐ使えるテンプレート集で試し、必要なら業務自動化や導入相談まで進められます。
この記事を書いた人
Masa
Claude Codeの実務活用、導入設計、収益導線改善を検証しているエンジニア。10言語の技術メディアを運営中。
関連書籍・参考図書
この記事のテーマに関連する書籍を楽天ブックスで探せます。
※ 当サイトは楽天市場のアフィリエイトプログラムに参加しています。上記リンクから商品をご購入いただくと、運営者に紹介料が支払われる場合があります。
関連記事
Claude Code権限セーフティラダー: 初心者がallowを広げる順番
Claude Codeの権限をread-onlyからbuild、限定編集、deploy確認まで段階的に広げる安全な運用手順。
Claude Code Small PR Proof Pack: 小さなPRをレビュー可能にする証拠セット
Claude Codeの小さなPRに、差分・検証・公開URL・CTA・rollbackを添える実務チェックリスト。
Claude Codeのコミット前レビューゲート: 差分、テスト、CTAをまとめて止める型
Claude Codeでcommit前に差分をレビューする実践手順。build、公開URL、CTA、Gumroadリンク、未翻訳本文を検知します。