dotenvx で暗号化、1Password CLI で注入 — .env 平文ゼロのローカル開発環境を構築する

@higa_toshiki 氏のポストが、ローカル開発で .env の平文を排除する実践的な手法を紹介しています(いいね 217、ブックマーク 255)。

ローカルに.envの平文を置きたくないけど、ローカルで開発したいこともあるので、

  1. dotenvxで.envを暗号化
  2. 1 password cli で key を注入する を使ってます。 (元木さんの言うように「秘密情報の平文はクラウドに置こう」に則る形)

引用元の @swarm_ai_cloud 氏のポストでは、AI CLI の .env 読み込み防止機能への疑問が呈されています。

AI のCLIには.env読まない仕様があるって?そんなん信用できるか?AI CLIはバージョンが上がればバグが混入し弾くファイル設定していても普通に読んだりするし

Claude Code が .env ファイルを自動的に読み込むことが確認されている今、「deny ルールで防ぐ」だけでは不十分という指摘は的を射ています。本記事では、higa 氏が紹介する2つのツール — dotenvx と 1Password CLI — の仕組みと実践的なセットアップ手順を解説します。

2つのアプローチの組み合わせ

higa 氏のワークフローは、2つの異なるアプローチを組み合わせています。

ツールアプローチ何を守るか
dotenvx.env ファイル自体を暗号化ファイルを読まれても平文が漏れない
1Password CLIクラウド Vault からランタイム注入そもそもファイルにシークレットを置かない
[dotenvx のアプローチ]
.env(暗号化済み)→ dotenvx run → 復号してプロセスに注入
                   → .env.keys(秘密鍵)が必要
                   → Git にコミット可能

[1Password CLI のアプローチ]
1Password Vault(クラウド)→ op run → プロセスに注入
                            → Touch ID / マスターパスワードで認証
                            → ディスクに平文が一切残らない

両者は排他的ではなく、用途に応じて使い分けるのが現実的です。

dotenvx — .env ファイルを暗号化して Git にコミットする

dotenvx とは

dotenvx は、.env ファイルの暗号化に対応した次世代の dotenv ツールです。オリジナルの dotenv の作者である Mot 氏が開発しており、週間100万回以上インストールされています。

従来の dotenv は「.env を読み込んで環境変数にセットする」だけのツールでしたが、dotenvx は暗号化・複数環境対応・クロスプラットフォームサポートを追加しています。

暗号化の仕組み

dotenvx は ECIES(Elliptic Curve Integrated Encryption Scheme) を採用しています。

暗号化フロー:
1. 各シークレットを AES-256 で暗号化(個別の一時鍵を使用)
2. AES 鍵を Secp256k1 楕円曲線暗号の公開鍵で暗号化
3. 暗号化されたシークレットを .env に書き戻し

復号フロー:
1. .env.keys から DOTENV_PRIVATE_KEY を読み込み
2. 秘密鍵で AES 鍵を復号
3. AES 鍵でシークレットを復号
4. 環境変数に注入
ファイル内容Git にコミット
.env暗号化されたシークレット + DOTENV_PUBLIC_KEY可能(推奨)
.env.keysDOTENV_PRIVATE_KEY(復号鍵)絶対に不可

セットアップ

1
2
3
4
5
6
7
8
# インストール(npm)
$ npm install @dotenvx/dotenvx --save

# インストール(Homebrew)
$ brew install dotenvx/brew/dotenvx

# インストール(curl)
$ curl -sfS https://dotenvx.sh | sh

暗号化の実行

1
2
# 既存の .env を暗号化
$ dotenvx encrypt

暗号化後の .env ファイル:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# .env(暗号化後)
#/-------------------[DOTENV_PUBLIC_KEY]--------------------/
#/            public-key encryption for .env files          /
#/       [how it works](https://dotenvx.com/encryption)     /
#/----------------------------------------------------------/
DOTENV_PUBLIC_KEY="0354d5b..."

# 暗号化されたシークレット
DATABASE_URL="encrypted:BDzX9..."
OPENAI_API_KEY="encrypted:AEf2k..."
SECRET_KEY="encrypted:C8mNp..."

.env.keys ファイル(Git に含めない):

1
2
# .env.keys
DOTENV_PRIVATE_KEY="ec4b..."

暗号化された .env を使って実行

1
2
3
4
5
6
7
8
# 開発環境(.env.keys が同じディレクトリにある場合)
$ dotenvx run -- python manage.py runserver

# 本番環境(環境変数で秘密鍵を渡す)
$ DOTENV_PRIVATE_KEY="ec4b..." dotenvx run -- node index.js

# 特定の .env ファイルを指定
$ dotenvx run -f .env.production -- python app.py

個別のシークレットを追加・更新

1
2
3
4
5
# 暗号化した状態でシークレットを追加
$ dotenvx set DATABASE_URL "postgres://user:pass@localhost/mydb"

# 既存のシークレットを更新
$ dotenvx set OPENAI_API_KEY "sk-proj-new-key..."

AI エージェントに対する防御効果

dotenvx で暗号化された .env ファイルは、Claude Code が読み取っても暗号文しか見えません。

# Claude Code が .env を読んだ場合
DATABASE_URL="encrypted:BDzX9aK3..."  ← 平文ではない

ただし、dotenvx run の子プロセスとして Claude Code を起動した場合は、復号されたシークレットが環境変数として渡されるため、この防御は無効です。

1Password CLI — クラウド Vault からランタイム注入

1Password CLI とは

1Password CLI(op) は、1Password の Vault に保存されたシークレットをコマンドラインから操作するツールです。op run コマンドで、シークレットを子プロセスの環境変数に注入できます。

lkr / aws-vault との違い

特徴1Password CLIlkraws-vault
保存先1Password クラウドmacOS KeychainmacOS Keychain
対象全種類のシークレットLLM API キーのみAWS 認証のみ
チーム共有可能(Vault 共有)不可(ローカルのみ)不可
認証Touch ID / マスターPWKeychainKeychain
料金有料($2.99/月〜)無料無料

最大の違いは対象範囲です。lkr は LLM API キー、aws-vault は AWS 認証情報に特化していますが、1Password CLI は DATABASE_URLSECRET_KEYSTRIPE_API_KEY などあらゆるシークレットを管理できます。

セットアップ

1
2
3
4
5
6
7
8
# macOS
$ brew install 1password-cli

# インストール確認
$ op --version

# サインイン
$ op signin

シークレットの保存

1Password アプリまたは CLI でシークレットを Vault に保存します。

1
2
3
4
5
6
7
# CLI でシークレットを作成
$ op item create --category=login \
    --title="My App Secrets" \
    --vault="Development" \
    'DATABASE_URL=postgres://user:pass@localhost/mydb' \
    'OPENAI_API_KEY=sk-proj-xxx' \
    'SECRET_KEY=django-insecure-xxx'

op run — シークレット参照の解決

.env ファイルにシークレット参照(op:// URI)を記述し、op run で解決します。

1
2
3
4
# .env(シークレット参照を含む)
DATABASE_URL=op://Development/My App Secrets/DATABASE_URL
OPENAI_API_KEY=op://Development/My App Secrets/OPENAI_API_KEY
SECRET_KEY=op://Development/My App Secrets/SECRET_KEY
1
2
# op run で参照を解決してプロセスを起動
$ op run --env-file=.env -- python manage.py runserver

このとき:

  1. op.env 内の op:// 参照を検出
  2. 1Password Vault からシークレットを取得(Touch ID で認証)
  3. 復号した値を環境変数として子プロセスに注入
  4. プロセス終了で環境変数は消える
  5. ディスクに平文は一切書き出されない

環境変数として直接設定する方法

1
2
3
4
5
6
# 環境変数に op:// 参照を設定
$ export DATABASE_URL="op://Development/My App Secrets/DATABASE_URL"
$ export OPENAI_API_KEY="op://Development/My App Secrets/OPENAI_API_KEY"

# op run で解決して実行
$ op run -- python manage.py runserver

1Password Environment の活用

1Password の Environment 機能を使うと、.env ファイルすら不要になります。

1
2
# Environment ID を指定して実行
$ op run --env <environment-id> -- python manage.py runserver

Environment は 1Password アプリの Developer セクションで作成・管理します。UNIX named pipe(FIFO)を使ってマウントされるため、ディスク上に平文ファイルが存在しません。

dotenvx + 1Password CLI の実践ワークフロー

higa 氏のワークフローを具体的に構築します。

使い分けの指針

シナリオ推奨ツール理由
チーム共有の開発環境変数dotenvx暗号化した .env を Git で共有できる
個人の API キー・シークレット1Password CLIチーム共有不要、クラウド保管で安全
CI/CD 環境dotenvxDOTENV_PRIVATE_KEY を CI の Secret に設定するだけ
ローカル開発1Password CLITouch ID で認証、ディスクに平文なし

パターン1: dotenvx でチーム共有 + 1Password で個人キー

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
# .env(dotenvx で暗号化、Git にコミット)
DOTENV_PUBLIC_KEY="0354d5b..."
DATABASE_URL="encrypted:BDzX9..."
REDIS_URL="encrypted:AEf2k..."

# 個人の API キーは 1Password から注入
$ export OPENAI_API_KEY="op://Personal/OpenAI/api_key"

# 両方を組み合わせて実行
$ op run -- dotenvx run -- python manage.py runserver

パターン2: 全て 1Password に統一

1
2
3
4
5
6
7
# .env(シークレット参照のみ、Git にコミット可能)
DATABASE_URL=op://Development/MyApp/DATABASE_URL
REDIS_URL=op://Development/MyApp/REDIS_URL
OPENAI_API_KEY=op://Personal/OpenAI/api_key

# op run で全て解決
$ op run --env-file=.env -- python manage.py runserver

Makefile での統合

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
# Makefile
.PHONY: server test jupyter

# dotenvx + 1Password の組み合わせ
server:
	op run -- dotenvx run -- python manage.py runserver

# 1Password のみ
test:
	op run --env-file=.env -- pytest tests/

# Jupyter
jupyter:
	op run --env-file=.env -- jupyter notebook

AI エージェントからの防御 — 限界と多層防御

dotenvx の防御効果

攻撃ベクトル防御効果
.env ファイルの直接読み取り有効 — 暗号文しか見えない
.env.keys の読み取り無効 — 秘密鍵が取れれば復号可能
子プロセスの環境変数読み取り無効dotenvx run の子プロセスには平文が渡る

1Password CLI の防御効果

攻撃ベクトル防御効果
ファイルの読み取り有効 — ディスクに平文がない
op run の子プロセスの環境変数無効 — 注入された環境変数は読める
/proc/<pid>/environ の読み取り無効 — 同一ユーザーのプロセスなら読める

Michael Hannecke 氏の指摘にある通り、op run -- pytest のようにテストを実行すると、テストプロセスの環境変数に Vault のシークレットが注入されます。Claude Code が同じユーザーで動作していれば、/proc/<pid>/environ 経由でアクセス可能です。

根本的な限界

swarm_ai_cloud 氏の指摘の核心はここにあります。どのツールを使っても、復号されたシークレットが実行プロセスに渡る限り、同一プロセスまたは同一ユーザーの他プロセスからの読み取りは防げないということです。

多層防御の構成

完全な防御は不可能でも、複数の層を重ねてリスクを下げることは可能です。

レイヤー 1: ファイル暗号化(dotenvx)
  → .env を読まれても暗号文しか見えない

レイヤー 2: クラウド保管(1Password)
  → ディスクに平文ファイルが存在しない

レイヤー 3: deny ルール(Claude Code settings)
  → .env / .env.keys の読み取りをブロック

レイヤー 4: プロセス分離
  → Claude Code とシークレットを使うプロセスを分離

レイヤー 5: 別ユーザー実行(上級)
  → Claude Code を別ユーザーで実行し、/proc 経由のアクセスを遮断

他のツールとの比較

ツール対象保存先チーム共有料金
dotenvx全種類暗号化ファイル(Git)可能無料
1Password CLI全種類1Password クラウド可能有料
lkrLLM API キーmacOS Keychain不可無料
aws-vaultAWS 認証macOS Keychain不可無料
Doppler全種類Doppler クラウド可能有料
AWS Secrets Manager全種類AWS可能従量課金

dotenvx と 1Password CLI の組み合わせは、「チーム共有の設定は dotenvx で Git 管理、個人のシークレットは 1Password で安全に保管」という使い分けができる点が強みです。

まとめ

  • dotenvx は .env を暗号化する: ECIES + AES-256 で暗号化し、Git にコミット可能にする。Claude Code が .env を読んでも暗号文しか見えない
  • 1Password CLI はクラウドからランタイム注入する: op run でシークレットを子プロセスに注入し、ディスクに平文を一切残さない
  • 両者は補完的: dotenvx はチーム共有の設定管理に、1Password CLI は個人のシークレット管理に向いている
  • lkr / aws-vault との違いは対象範囲: 1Password CLI は DB 接続情報や SaaS キーなど全種類のシークレットを扱える
  • 根本的な限界は残る: 復号されたシークレットが環境変数に載る以上、同一プロセスからの読み取りは防げない
  • 多層防御が現実解: ファイル暗号化 + クラウド保管 + deny ルール + プロセス分離を組み合わせる
  • 「秘密情報の平文はクラウドに置こう」: higa 氏の実践は、ローカルに平文を置かない原則を具体化している

参考