Fragments of verbose memory

冗長な記憶の断片 - Web技術のメモをほぼ毎日更新

Mar 3, 2026 - 日記

enject(旧enveil): AIに.envを覗かせないために『平文をディスクに置かない』という選択

enject(旧enveil): AIに.envを覗かせないために『平文をディスクに置かない』という選択

AI コーディングツールが当たり前になってきて、.env(環境変数ファイル)をそのまま置く怖さが目に見えるようになりました。

ファイルが読めるなら、秘密も読めます。ここはAI時代のセキュリティ として、無視しにくい問題だと思います。

この記事では、enject (旧enveil)を中心に、「AIが読める場所に秘密を置かない」という発想を整理します。

(補足)プロジェクトは以前 enveil という名前でしたが、現在は enject にリネームされています。

その上で、dotenvx を比較対象として出し、チーム/CIまで含めた運用で何が変わるかも見ます。

まず脅威モデルを分解する(どこで漏れる?)

.env を安全にする」と言っても、漏れる経路は複数あります。ここでは3つに分けて考えます。

  1. ファイル漏洩: .env をAIや別プロセスが読める
  2. プロセス漏洩: プロセス環境変数(os.environ)のダンプやログ出力で漏れる
  3. ネットワーク漏洩: 悪意あるコードが外部送信する

今回フォーカスするのは主に(1)です。(2)(3)まで本気でやるなら、権限分離やサンドボックス(隔離実行)など別の対策が必要になります。

2つのアプローチの狙い

enject(旧enveil)の狙い

enject は「.env参照テンプレにする」設計です。.env に値は書かず、en:// 参照だけを置き、実体は暗号化ローカルストアに保存します。

結果として、「AIがディレクトリを覗いても .env に秘密がない」状態を作ります。反面、プロセス上の秘密ネットワーク送信は別の対策が必要です。

enject のデータ(秘密)はどこに保存される?

ここが一番気になるところなので、先に書きます。

  • プロジェクト側(リポジトリ内)
    • .env:秘密の値は書かず、en:// 参照だけを書く(テンプレ)
    • .enject/:プロジェクトごとの設定と暗号化ストア
      • .enject/config.toml:設定(salt等)
      • .enject/store暗号化された秘密の実体(バイナリ)
  • あなたの頭(=マスターパスワード)
    • store を復号するためのパスワードは、基本的にファイルには残しません(対話入力)

運用上は、.enject/必ず .gitignore に入れて「ローカル専用」にするのが前提になります。

# enject
.enject/

(補足)READMEによると、内部では Argon2id で鍵を導出して AES-256-GCM でストアを暗号化、という構成です。ここは“暗号が強い”より「平文がディスクに無い」という性質のほうが効きます。

dotenvx の狙い

dotenvx は .env を中心に、暗号化・複数環境・配布まで扱うツール群です。GitHub リポジトリ と公式のドキュメント では .env.keys(復号鍵ファイル)を使った暗号化フローや、CI連携のガイドが整理されています。

ここでは「チーム/CIで回す」前提のよくある使い方を想定します。

  • .env を使ったローカル開発の互換性をなるべく壊さない
  • CIやチーム配布を含めて回す

比較

観点dotenvxenject
狙っている主問題.env運用全般(配布/互換/暗号化など)平文の秘密をディスクに置かない
ファイル漏洩への強さ運用次第で強くも弱くもなる参照テンプレなので強い設計
プロセス漏洩への対応別途対策が必要別途対策が必要
ネットワーク漏洩への対応別途対策が必要別途対策が必要
チーム/CI適性ガイドがあり導入しやすい追加設計が必要になりやすい
運用負荷鍵配布や環境分離の運用が必要パスフレーズ(長めのパスワード)管理が必要
既存 .env 互換高め参照テンプレに書き換える必要

この表を見て「どっちが勝ち」というより、 どのレイヤをどのツールに任せるかが設計になります。

最小手順で触ってみる

dotenvx を試す

前提: Node.js が使える環境です。ここでは npx(パッケージ実行ツール)経由で実行します。ダミー値を使うので、実際の秘密は入れないでください。

dotenvx は「値をファイルに書く」以外に、dotenvx set値を設定しつつ暗号化できるのが便利です(Quickstart にも出てきます)。

  1. 作業ディレクトリを作る

    1
    2
    
    mkdir dotenvx-demo
    cd dotenvx-demo
    
  2. set で値を入れる(暗号化)

    これで .env(または .env.production 等)と .env.keys が作られます。

    1
    
    npx @dotenvx/dotenvx@latest set API_KEY dummy-123
    
  3. 実行して読み込めているか確認する

    loaded true が出れば読み込み確認は完了です。

    1
    
    npx @dotenvx/dotenvx@latest run -- node -e "console.log('loaded', !!process.env.API_KEY)"
    

.env.keys には復号鍵が入るので、コミットしない運用が前提になります。

# dotenvx
.env.keys

enject を試す

前提: Rust(システムプログラミング言語)が必要です。rustup が使える環境で試してください。.enject/ はローカルストアなので、.gitignore に入れる前提です。

  1. インストール

    これで enject の CLI(コマンドラインツール)が使えるようになります。

    1
    
    cargo install enject --version 0.2.0-alpha
    
  2. プロジェクトで初期化

    .enject/ が作られ、ローカル暗号化ストアが初期化されます。

    1
    2
    3
    
    mkdir enject-demo
    cd enject-demo
    enject init
    
  3. 秘密を登録(対話入力なので、シェル履歴に残りにくい)

    ここではダミー値を入力します。

    1
    2
    
    enject set database_url
    # Value for 'database_url': (hidden)
    
  4. .env は参照テンプレにする

    en:// 参照を置き、実体はローカルストアに任せます。

    1
    
    DATABASE_URL=en://database_url
    
  5. 実行は enject run 経由で確認

    loaded true が出れば参照が解決できています。

    1
    
    enject run -- node -e "console.log('loaded', !!process.env.DATABASE_URL)"
    

この時点で「AIが .env を読んでも値が出ない」状態にはできます。

インフラ間で共有・移動するならどっちが現実的?

ここが enject を触って一番「思想の差」を感じるところです。

dotenvx: 暗号文を共有して、鍵だけを別経路で渡す

dotenvx は “共有” の作法が比較的分かりやすいです。

  • 暗号化された .env(=暗号文)は、リポジトリやアーティファクトとして配れる
  • 復号に必要な .env.keys(鍵)は、別経路で渡す(=ここを厳重に)

暗号文と鍵を分離できるので、「インフラ間で同じ設定を回す」用途に向きます。

もちろん、鍵が漏れたら終わりです。 ただ、鍵の取り扱いを “ここだけ注意” に寄せられるのが運用上ありがたいです。

enject: .enject/ を束ねて“持っていく”になりがち

enject はローカルに閉じた設計が主役なので、「複数マシンで同じ秘密を回す」をやろうとすると、結局こうなります。

  • .enject/config.toml.enject/store(暗号化ストア)を含む .enject/ を、何らかの安全な経路で移動

例えば、プロジェクトルートで

1
tar czf enject.tgz .enject

を作って、別マシンで展開する、のような運用です。

これは “できる” けど、dotenvxの「暗号文と鍵を分離する」感じとは違います。

  • .enject/ は暗号化されていても 機密ファイル です(漏れたらオフライン攻撃の対象)
  • 共有した後は enject rotate でパスフレーズを更新して、被害範囲を区切りたくなります

制作者へのリスペクトは前提として、 enject は共有(チーム/CI)より、ローカルの覗き見耐性に賭けたツール、という評価になります。

一方でこの割り切りは、個人的にはかなり好感があります。 「秘密を取り出せるコマンド(get/export)を提供しない」という方針は不便でも、 うっかりを減らすには効きます。

  • ターミナルのコピペ
  • ログへの出力
  • AIが読めるファイルへの書き出し

この辺の“人間のミス”を、ツール側が最初から潰しに来ている感じです。

落とし穴(ここを外すと事故る)

  • enject は(1)を強くする代わりに、(2)(3)は止めません
  • dotenvx は汎用性が高い分、運用設計が甘いと(1)が残ります

つまり、

  • 守る対象が(1)中心なら enject の思想は刺さる
  • チーム/CI含めた運用が主戦場なら dotenvx のほうが自然

という整理になります。

選び方

次の問いに答えると、選びやすいです。

  • 「AIが読む場所から秘密を消したい」か
  • 「CIまで含めて回す」ことが主目的か
  • 「ローカル開発の互換性」をどこまで保ちたいか
  • 「鍵やパスフレーズの配布」を運用で回せるか

まとめ: まず“守りたい経路”を決めてから選ぶ

  • 「AIが読む場所から秘密を消す」が主目的なら、enject は分かりやすい
  • 「配布/互換/CIまで含めた運用」が主目的なら、dotenvx は強い

自分はこの2つを “代替” というより、守るレイヤが違う道具として見ています。

参考リンク