はじめに
プロンプトインジェクション防御でも、SAST静的解析でも、OAuth認証設計でもない。「APIキーをどこに保管するか」——.gitにpushした.envファイルがリポジトリに残り続ける、Claude Code自体がプロジェクトの.envを読み込む可能性があるという報告がある、という2つの現実的なリスクから始まる話だ。
「.envファイルをgitignoreに入れたから大丈夫」——この思い込みが出発点になっているチームは多い。しかしgitignoreは.envのgit追跡を防ぐだけで、ローカルに存在する.envファイルへのアクセスまでは制御しない。AIコーディングツールが普及した現在、シークレット管理のリスクモデルを更新する必要がある。
本記事では、CLAUDE.mdによるポリシー定義・settings.jsonでの明示的なアクセス禁止・1PasswordとAWS Secrets Managerによる本番品質の注入設計を組み合わせたアプローチを紹介する。
CLAUDE.mdとsettings.jsonで二重のガード——.envへのアクセスをデフォルト禁止にする
Claude Code自体が.envを読み込むリスク
セキュリティ研究者は、Claude Codeがプロジェクトの.envファイルを明示的な許可なく読み込む可能性があると指摘している(knostic.ai)。この挙動の正確な条件についてはさらなる確認が必要だが、Claude Code公式ドキュメント(code.claude.com/docs/en/settings)がpermissions.denyで.envへのアクセスを明示的に禁止する設定を推奨している事実は、このリスクが実在することを示唆している。
AIコーディングツールがシークレットをコンテキストに取り込んだ場合、会話ログや処理の流れでシークレット値が予期しない場所に記録される可能性がある。この問題に対する現実的な対策は二重のガードだ。CLAUDE.mdでポリシーを明文化し、settings.jsonでアクセス禁止を技術的に強制する。
CLAUDE.mdによるシークレット管理規約
# CLAUDE.md(シークレット管理ポリシー)
## Secrets Management Policy
### 禁止事項(絶対守ること)
- .env, .env.local, .env.* ファイルへのシークレットのハードコード禁止
- ソースコードへのAPIキー・パスワード・証明書の直接埋め込み禁止
- シークレットを含むファイルのgit commit禁止(.gitignoreで必ず除外)
- Claude Codeへの会話でシークレット値を直接貼り付けない
### 保管場所の規約
- 本番シークレット: AWS Secrets Manager(本番・ステージング)
- 開発シークレット: 1Password(op:// reference)
- ローカル開発: direnv + .envrc(.gitignore済み)で注入
### コードでのシークレット参照
- Lambda/コンテナ: AWS Secrets Manager SDKで実行時取得
- 環境変数にはシークレット値ではなく「シークレットのARN/パス」を保存
例: DB_SECRET_ARN=arn:aws:secretsmanager:...(値ではなく参照先)
### MCPサーバーの設定
- .mcp.json の env にシークレット値を直書き禁止
- op:// reference または環境変数参照を使うこと
例: "GITHUB_TOKEN": "op://vault/github/token"(1Password)
### Claude Code設定
- .claude/settings.json で .env* へのReadアクセスを禁止:
permissions.deny: ["Read(.env)", "Read(.env.*)"]settings.jsonでの技術的なアクセス制限
CLAUDE.mdのポリシーは「Claude Codeへの指示」だが、settings.jsonのpermissions.denyは「技術的な制限」だ。この二重のガードが重要になる。
// .claude/settings.json(プロジェクトルートに配置)
{
"permissions": {
"deny": [
"Read(.env)",
"Read(.env.*)",
"Read(**/.env)",
"Read(**/secrets/**)"
]
}
} この設定をプロジェクトルートの.claude/settings.jsonに配置することで、Claude Codeは.envファイルの読み取りを要求された場合でも実行できない状態になる。CLAUDE.mdのポリシー定義と合わせることで、「意図的にも、意図せずにも」.envへのアクセスが発生しない環境を作れる。
さらに補完的な対策として、実際の値を持たない.env.exampleのみをgitで管理し、開発者がローカルで.envを作成する運用にすることで、.envファイル自体を作らない設計に近づけられる。1PasswordやAWS Secrets Managerから実行時に直接注入する設計であれば、ローカルの.envファイルすら不要になる。
1Password × MCP設定——op:// referenceでクレデンシャルを安全に注入
.mcp.jsonへのハードコードというリスク
MCP(Model Context Protocol)サーバーが普及するにつれて、新しいシークレット管理の問題が表面化している。MCPサーバーの設定ファイル(.mcp.json)のenvセクションにAPIキーをハードコードしているケースだ。
// 危険な設定(ハードコード)
{
"mcpServers": {
"github": {
"env": {
"GITHUB_PERSONAL_ACCESS_TOKEN": "ghp_xxxxxxxxxxxx"
}
}
}
}.mcp.jsonがgitリポジトリに含まれる場合はもちろん、チームメンバー間でファイルを共有する際にも、シークレット値ごと流出するリスクがある。
op:// referenceによる安全な設定
1Passwordが公式ブログで推奨しているアプローチは、シークレット値の代わりに1Passwordへの参照(op:// reference)を.mcp.jsonに記述する方法だ。
// 正しい設定(1Password op:// reference使用)
{
"mcpServers": {
"github": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-github"],
"env": {
"GITHUB_PERSONAL_ACCESS_TOKEN": "op://Personal/GitHub PAT/token"
}
}
}
} op://Personal/GitHub PAT/tokenという文字列はシークレット値ではなく、1Password内の保管場所へのポインターだ。1Passwordが実行時にシークレット値を解決してメモリに注入するため、.mcp.jsonにシークレット値がハードコードされることはない。このファイルはgitにコミットしても安全になる。
op runラッパーでClaude Codeを起動する
Claude Code起動時に1Passwordからシークレットを解決してセッション全体に注入する方法もある。
# .env.op(gitにコミット可能:値ではなくop://参照のみ)
ANTHROPIC_API_KEY=op://Personal/Anthropic/api-key
GITHUB_TOKEN=op://Work/GitHub/token
DATABASE_URL=op://Work/Production DB/connection-string# op runでClaude Codeを起動(1Passwordからシークレットを注入)
op run --env-file .env.op -- claude.env.opには参照先のパスのみが書かれており、シークレット値は含まれない。これによって、シークレットの管理を1Passwordに集約しながら、ローカル開発環境でもClaude Codeへのシークレット注入が安全に行える。
なお、settings.jsonのenvセクションでop://参照を直接サポートするFeature Requestが公式GitHubに提出されている(Issue #23642)。現状はop runのラッパースクリプト経由での対応が現実的な選択肢だ。
AWS Secrets Manager × Lambda——本番シークレットの実行時取得パターン
環境変数に値を入れない設計
本番のLambda関数でシークレットを扱う際、環境変数に実際の値を入れてしまうのが最もよくある誤りだ。環境変数はAWS Consoleやaws lambda get-function-configurationコマンドで参照できてしまうため、IAMで制御されているとはいえ漏洩リスクが残る。
環境変数にはシークレットのARN(参照先)のみを保存し、実行時にSecrets Managerから値を取得するパターンが推奨される設計だ。Claude Codeへの指示はこうなる。
このLambda関数がDB接続情報を環境変数から直接読んでいる(DB_PASSWORD)。
これをAWS Secrets Manager経由に変換して。
要件:
- DB認証情報をSecrets Managerに保存する手順(CDKコード)
- Lambda実行時にSecrets Managerから取得するコード
- 環境変数にはシークレットのARNのみを保存
- Lambda cold start対策でキャッシュ付き
- Lambdaに必要なIAMポリシー(secretsmanager:GetSecretValue)も生成してClaude Codeが生成する実装はこうなる。
import { SecretsManagerClient, GetSecretValueCommand } from "@aws-sdk/client-secrets-manager";
// モジュールレベルでキャッシュ(Lambda cold start対策)
let secretCache: Record<string, string> = {};
async function getSecret(secretArn: string): Promise<string> {
if (secretCache[secretArn]) return secretCache[secretArn];
const client = new SecretsManagerClient({ region: process.env.AWS_REGION });
const response = await client.send(
new GetSecretValueCommand({ SecretId: secretArn })
);
const value = response.SecretString!;
secretCache[secretArn] = value; // Lambdaインスタンス内でキャッシュ
return value;
}
// 環境変数にはARNのみ(値は入れない)
const dbSecret = await getSecret(process.env.DB_SECRET_ARN!);cold startとキャッシュ設計のトレードオフ
モジュールレベルでsecretCacheを宣言することで、同一Lambdaインスタンス内では最初の呼び出し以降Secrets Managerへのアクセスが発生しない。ただし、Lambdaの再起動(cold start)のたびにキャッシュが消えるため、コールドスタートのたびにSecrets Managerへのアクセスが発生することは理解しておく必要がある。
Secrets Managerの料金はAPIコール数に比例するため、呼び出し頻度が高いLambdaでは接続コストが積み上がる場合がある。コールドスタートの頻度・Lambdaの同時実行数・Secrets Managerのコストを考慮した上で、キャッシュ戦略を調整するとよい。
既存コードの一括変換
本番環境で既にDB_PASSWORDやAPP_SECRETなどの環境変数を直接使っているLambda関数が複数ある場合、Claude Codeに一括で変換させることができる。「このリポジトリ内のLambda関数で、環境変数から直接シークレット値を読み込んでいる箇所を全て特定して、Secrets Manager ARN参照パターンに変換して」という指示で、CLAUDE.mdのポリシーと照合しながら問題のある実装を洗い出せる。
まとめ
シークレット管理の問題が「見えにくい」のは、動いているうちは問題が顕在化しないからだ。.envファイルをgitignoreに入れ、環境変数にパスワードを入れ、MCP設定にトークンをハードコードしていても、アプリケーションは正常に動き続ける。リスクは「漏洩したとき」に初めて可視化される。
CLAUDE.mdでシークレット管理ポリシーを定義し、settings.jsonで.envへのアクセスを禁止することは、Claude Codeが生成するコードに安全なデフォルトを組み込む最も手軽な方法だ。1PasswordのMCP設定への導入はop://参照に変更するだけで済む。AWS Secrets Managerへの移行は既存コードの書き換えが必要だが、Claude Codeに「Secrets Manager経由に変換して」と依頼することで実装の大部分を自動生成できる。
まずやるとしたら、既存プロジェクトの.claude/settings.jsonにpermissions.denyを追加し、CLAUDE.mdにシークレット管理ポリシーのセクションを1つ書き加えることから始めてみてほしい。それだけで、Claude Codeが.envを読もうとする操作が技術的に防がれ、シークレットの扱いに関するポリシーが明文化される。

コメント