.env の代わりに lkr で LLM API キーを安全に管理する — セットアップから Claude Code 連携まで
AI エージェントがローカルファイルを読み書きする時代、.env に平文で置いた API キーが LLM のコンテキストに載るリスクが現実のものになっています。前回の記事ではこの問題の全体像を、aws-vault の記事では AWS 認証情報の保護を解説しました。
本記事では、LLM Key Ring(lkr)を使って LLM API キーを安全に管理する具体的な手順を解説します。aws-vault が AWS 認証情報に特化しているのに対し、lkr は OpenAI・Anthropic・Google など LLM API キーの管理に特化したツールです。
lkr が解決する問題
.env に LLM API キーを置くリスク
多くの開発者は .env ファイルに API キーを平文で保存しています。
| |
このファイルには4つの攻撃ベクトルがあります。
| 攻撃ベクトル | 説明 |
|---|---|
| Git への混入 | .gitignore に頼るヒューマンエラー。うっかりコミットは後を絶たない |
| シェル履歴への漏洩 | export OPENAI_API_KEY=sk-... が ~/.bash_history に残る |
| プロセス情報への露出 | ps コマンドで環境変数が見える |
| AI エージェントによる抽出 | Claude Code がファイルを読み取り、LLM の API リクエストに含まれる |
4番目が AI 時代に特有の脅威です。Claude Code は.env ファイルを自動的に読み込むことが確認されており、API キーが意図せず Anthropic のサーバーに送信されるリスクがあります。
lkr のアプローチ
lkr は2つの原則で問題を解決します。
- 平文を置かない: API キーを macOS Keychain に暗号化保存する
- 必要な瞬間だけ注入:
lkr execで子プロセスの環境変数にのみ注入し、ファイルにも標準出力にも書き出さない
[従来]
.env(平文)→ アプリが読み取り → Claude Code も読める
[lkr]
macOS Keychain(暗号化)→ lkr exec → 子プロセスの環境変数にのみ注入
→ プロセス終了で消える
→ Claude Code からは見えない
セットアップ
前提条件
- macOS が必要です(Keychain に依存するため)
- Rust 1.85 以上 が必要です
Rust が未インストールの場合:
| |
インストール
| |
インストール確認:
| |
現時点では Homebrew やバイナリリリースは提供されていません。Cargo でのビルドが唯一のインストール方法です。
最初のキーを登録する
| |
キーは対話的プロンプトで入力します。CLI 引数としては受け付けないため、シェル履歴や ps への漏洩を防ぎます。
クリップボードからの貼り付けも可能です:
| |
複数のプロバイダを登録
| |
登録できたことの確認
| |
.env ファイルを削除
| |
対応プロバイダと環境変数のマッピング
lkr は provider:label 形式のキー名から、環境変数名を自動的に解決します。
| Keychain ラベル | 注入される環境変数 |
|---|---|
openai:prod | OPENAI_API_KEY |
anthropic:main | ANTHROPIC_API_KEY |
google:dev | GOOGLE_API_KEY |
mistral:api | MISTRAL_API_KEY |
cohere:prod | COHERE_API_KEY |
groq:prod | GROQ_API_KEY |
deepseek:api | DEEPSEEK_API_KEY |
xai:prod | XAI_API_KEY |
ラベル部分(: の後)は自由に名前をつけられます。同じプロバイダで複数のキーを管理する場合に便利です。
| |
lkr の管理対象外のシークレット
lkr が管理できるのは上記8プロバイダの LLM API キーのみです。.env に含まれることが多い以下のようなシークレットは lkr の管理対象外です。
| シークレットの種類 | 例 | 代替ツール |
|---|---|---|
| DB 接続情報 | DATABASE_URL, REDIS_URL | Doppler, AWS Secrets Manager |
| アプリケーション秘密鍵 | SECRET_KEY, JWT_SECRET | Doppler, AWS Secrets Manager |
| AWS 認証情報 | AWS_ACCESS_KEY_ID | aws-vault |
| 外部 SaaS の API キー | STRIPE_SECRET_KEY, SENDGRID_API_KEY | Doppler, HashiCorp Vault |
lkr gen でテンプレートから .env を生成する際も、対応プロバイダに一致しない変数は解決されずそのまま残ります(後述の lkr gen の例で DATABASE_URL が「Kept as-is」となる動作を参照)。
つまり、.env を完全に廃止するには、lkr 単体では不十分であり、シークレットの種類に応じたツールの組み合わせが必要です。
| シークレット | ツール |
|---|---|
| LLM API キー | lkr |
| AWS 認証情報 | aws-vault |
| DB・SaaS 等その他 | Doppler / AWS Secrets Manager 等 |
詳しくは Claude Code 時代の .env 管理 を参照してください。
基本的な使い方
lkr exec — 推奨ワークフロー
最も安全な使い方です。キーは子プロセスの環境変数にのみ注入され、ファイルにも標準出力にも一切書き出されません。
| |
lkr get — 手動取得
環境変数が使えない場面での手動取得手段です。
| |
デフォルトの動作:
- 出力はマスク表示(末尾4文字のみ)
- クリップボードに自動コピー
- 30秒後にクリップボードを自動消去(SHA-256 でハッシュ照合し、別の内容がコピーされていた場合はスキップ)
オプション:
| |
lkr gen — テンプレートからの設定ファイル生成
.env 形式や JSON 形式のテンプレートから、Keychain の値を解決して設定ファイルを生成します。exec が使えない場合のフォールバックです。
.env テンプレートから生成
| |
| |
生成されたファイルは 0600(所有者のみ読み書き可能)のパーミッションが設定されます。.gitignore に含まれていない場合は警告が表示されます。
JSON テンプレートから生成(MCP サーバー設定)
MCP サーバーの設定ファイルなど、JSON 形式が必要な場合に使います。
| |
| |
{{lkr:provider:label}} プレースホルダが Keychain の値に解決されます。
lkr rm — キーの削除
| |
lkr usage — API 使用量の確認
| |
使用量の確認には admin キーが必要です。
runtime キーと admin キーの分離
lkr のキー管理の中核にある設計です。推論用キーと管理用キーでは、漏洩時の影響が大きく異なります。
| 分類 | 用途 | 漏洩時の影響 |
|---|---|---|
| runtime | API 推論呼び出し | 不正利用による課金増加 |
| admin | 課金・使用量管理 | アカウント全体の制御を奪われる |
| |
| 操作 | runtime | admin |
|---|---|---|
lkr exec | 注入される | 注入されない |
lkr gen | 解決される | 解決されない |
lkr list(デフォルト) | 表示 | 非表示 |
lkr list --all | 表示 | 表示 |
lkr usage | 不可 | 必要 |
「全キーを一括注入」という安易な運用を防ぎ、権限の最小化を実現しています。
TTY ガード — AI エージェント対策
lkr の最も特徴的な防御機構です。Claude Code のような AI エージェントがキーを抽出するのを防ぐ3つのレイヤーがあります。
レイヤー 1: 生値出力のブロック
| |
検出には isatty(fd レベルの TTY チェック)を使用しています。CI や TERM 環境変数は容易に偽装できるため、意図的に無視しています。
レイヤー 2: クリップボードコピーのブロック
| |
lkr get key && pbpaste のような抽出チェーンを防ぎます。
レイヤー 3: exec ワークフローの優先
そもそもシークレットを出力しない exec を推奨ワークフローとすることで、出力ブロックに依存しない設計を実現しています。
既知の制限
IDE の統合ターミナル(VS Code のターミナル等)は isatty(true) を返すため、TTY ガードはバイパスされます。完全な防御ではなく、攻撃コストを上げる設計です。
メモリ保護 — Zeroizing
全てのシークレット値は String ではなく Zeroizing<String> で保持されます。スコープを抜けてドロップされる際に、メモリ上の値がゼロ埋めされてから解放されます。
対象:
- API キーの取得・保存時の一時バッファ
execでの子プロセス環境変数の構築時- テンプレート解決時の中間値
ヒープダンプやコアファイルからのシークレット復元を防ぐ仕組みです。
Claude Code との連携
apiKeyHelper で Anthropic API キーを動的取得
Claude Code の settings.json に apiKeyHelper を設定すると、API キー自体も .env から排除できます。
| |
Claude Code は起動時にこのコマンドを実行し、出力を API キーとして使用します。デフォルトでは5分ごとに再取得されます。TTL のカスタマイズも可能です。
| |
deny 設定との併用
Claude Code が .env を読み取らないよう、プロジェクトの .claude/settings.json に deny ルールを追加します。
| |
Claude Code を lkr exec で起動する
Claude Code 自体が LLM API キーを使うスクリプトを実行する場合、lkr exec の子プロセスとして起動できます。
| |
ただし、Claude Code に API キーが環境変数として渡されるため、Claude Code がそれを読み取る可能性はあります。通常は apiKeyHelper 経由の方が安全です。
実践的なワークフロー
Python(OpenAI / Anthropic SDK)
| |
OpenAI SDK や Anthropic SDK は、デフォルトで OPENAI_API_KEY / ANTHROPIC_API_KEY 環境変数を参照するため、コード変更は不要です。
Node.js
| |
Jupyter Notebook
| |
Notebook 内のセルで os.environ["OPENAI_API_KEY"] が使えます。
Docker コンテナへの受け渡し
| |
Makefile での統合
| |
| |
aws-vault との併用
AWS リソースにもアクセスする LLM アプリケーションでは、aws-vault と lkr を組み合わせます。
| |
| 認証情報 | ツール | 保存場所 |
|---|---|---|
| AWS アクセスキー | aws-vault | macOS Keychain |
| AWS 一時認証 | aws-vault (STS) | メモリ上のみ |
| OpenAI API キー | lkr | macOS Keychain |
| Anthropic API キー | lkr | macOS Keychain |
Makefile に組み込む場合:
| |
根本的な限界 — 復号されたシークレットとエージェント
lkr や aws-vault を導入しても、復号されたシークレットが子プロセスの環境変数に注入されるという構造は変わりません。AI エージェントがその子プロセス内で動作していれば、環境変数を読み取ることができます。
lkr exec -- claude
↓
子プロセス(Claude Code)の環境変数に API キーが注入される
↓
Claude Code は os.environ を読める
↓
キーが LLM の API リクエストに含まれる可能性がある
aws-vault でも同じです。aws-vault exec dev -- claude とすれば、AWS_ACCESS_KEY_ID と AWS_SECRET_ACCESS_KEY が Claude Code のプロセスから読めます。
防げるもの・防げないもの
| 攻撃ベクトル | .env(平文) | lkr / aws-vault |
|---|---|---|
ファイルの読み取り(.env) | 防げない | 防げる(ファイルが存在しない) |
| シェル履歴への漏洩 | 防げない | 防げる |
| Git への混入 | 防げない | 防げる |
| エージェントが環境変数を読む | 防げない | 防げない |
つまり、これらのツールは「平文ファイルの排除」には有効ですが、「実行中のプロセスからの読み取り」には無力です。
リスクを下げるための多層防御
完全な解決は難しいですが、複数のレイヤーを重ねてリスクを下げるアプローチがあります。
1. apiKeyHelper を使う(Claude Code 自身の API キー)
| |
Claude Code の API キーは環境変数に載らず、Claude Code の内部機構が直接取得します。少なくとも「自分自身の API キー」はコンテキストに載りにくくなります。
2. -k で必要最小限のキーだけ注入する
| |
3. エージェントとシークレットを使うプロセスを分離する
最も根本的な対策は、エージェントのプロセスにシークレットを渡さないことです。
| |
Claude Code にはコードの編集とレビューだけを任せ、シークレットが必要な実行は別プロセスで行う運用です。
4. deny ルールで補強する
| |
環境変数の読み取りは防げませんが、ファイル経由のアクセスをブロックする追加の防御層になります。
現実的な結論
lkr や aws-vault は「攻撃コストを上げる」ためのツールであり、「完全な防御」ではありません。lkr 自身も TTY ガードについて同様の位置づけを明示しています。セキュリティは単一のツールで完結するものではなく、複数の防御層を重ねて総合的にリスクを下げるのが現実的なアプローチです。
トラブルシューティング
Keychain のパスワード入力が頻繁に求められる
macOS の「キーチェーンアクセス.app」で、lkr が使用するキーチェーンのロック時間を調整します。
cargo install でビルドエラー
Rust のバージョンが 1.85 未満の場合:
| |
環境変数が注入されない
lkr list でキーが登録されていることを確認してください。admin キーは exec では注入されません。
| |
gen で生成したファイルの .gitignore 漏れ
lkr は .gitignore に出力ファイルが含まれていない場合に警告を表示します。必ず .gitignore に追加してください。
| |
まとめ
- 平文を排除:
lkr setで API キーを macOS Keychain に暗号化保存し、.envファイルを削除する - 実行時注入:
lkr execでキーを子プロセスの環境変数にのみ注入。ファイルにも標準出力にも書き出さない - AI エージェント対策: TTY ガードが非対話環境でのキー抽出をブロックする
- 権限の最小化: runtime / admin の分離により、推論キーと管理キーを使い分ける
- コード変更不要: OpenAI SDK / Anthropic SDK はデフォルトで環境変数を参照するため、アプリケーション側の変更は不要
- Claude Code 連携:
apiKeyHelperで API キー自体も動的取得に移行できる - aws-vault と併用可能:
aws-vault exec dev -- lkr exec -- commandで AWS + LLM の認証情報を同時に管理 - 根本的な限界を理解する: 復号されたシークレットが環境変数に載る以上、エージェントからの読み取りは防げない。プロセス分離と多層防御で対処する