Claude CodeやCodexを使っていると、AIからこんな指示を受けたことはないでしょうか。

OPENAI_API_KEY=sk-...
DATABASE_URL=postgres://...
STRIPE_SECRET_KEY=sk_live_...

.env にAPIキーを書いてください」
「この値を .env.local に追加してください」
「このパスワードを環境変数に入れてください」

最近は、非エンジニアの方でもClaude CodeやCodexを使ってWebアプリや業務ツールを作ることが増えています。その一方で、Xなどでは「.env にパスワードやAPIキーを書くのは危ない」「AIエージェントに .env を読ませるのは怖い」という話を見かけたこともあると思います。

もちろん、秘密情報の守り方にはいろいろな方法があります。
クラウドのSecret Managerを使う方法もありますし、direnv、Doppler、Infisical、Vault、Cloudflare Workers Secrets、GitHub Actions Secretsなどを使う方法もあります。

この記事では、その中でも 1Passwordを普段から使っている人にとって導入しやすい方法 として、1Password CLIを使って、Claude CodeやCodexに .env の実キーを直接見せない運用をまとめます。

100%防げる方法ではありません。
ただ、通常の「.env に実キーを書いて、AIエージェントが読める状態にしておく」運用と比べると、体感として80%くらいは事故の導線を減らせると思っています。


3分でわかるまとめ

  • .env にAPIキーやパスワードを直接書くと、Claude CodeやCodexが読めてしまう可能性がある
  • .env が読める状態は、机の上に鍵付き金庫の暗証番号を書いた紙を置いて作業を頼むようなもの
  • 1Password CLIを使うと、.env に実キーを書かず、op://... という秘密参照だけを書ける
  • Claude Code / Codex本体は、原則として1Password経由で起動しない
  • 秘密情報が必要なコマンドだけ、op run --env-file=.env.1password -- <command> で実行する
  • 例:./scripts/run-safe.sh pnpm dev
  • .env / .env.* はAIエージェントに読ませない
  • printenvenvop readop item get なども基本denyまたは慎重に扱う
  • コマンド許可時は「秘密を表示しないか」「外部送信しないか」「本番に影響しないか」を最低限見る
  • 本番キーはローカルのAIエージェント運用に渡さない
  • 1PasswordのVaultは、個人用・会社用・開発用・本番用を分ける

この記事のおすすめ方針の全体像:.env に実キーを書かず、op run で包んだコマンドにだけ秘密情報を流す構造図


なぜ .env をAIエージェントに読ませると危ないのか

.env は便利です。
ローカル開発では、APIキーやDB接続情報をまとめて置けます。

ただし、Claude CodeやCodexのようなAI開発エージェントにプロジェクトを触らせる場合、.env に実キーが入っていると危険です。

たとえば、.env に次のような値があるとします。

OPENAI_API_KEY=sk-...
STRIPE_SECRET_KEY=sk_live_...
DATABASE_URL=postgres://user:[email protected]:5432/db

AIエージェントが .env を読める場合、以下のようなことが起こり得ます。

  • 実キーを会話に表示してしまう
  • ログやエラーメッセージに出してしまう
  • printenvenv で環境変数を一覧表示してしまう
  • テストやデバッグ用コードにキーを埋め込んでしまう
  • 誤ってGit管理対象に入れてしまう
  • 外部APIやCLIに渡してしまう
  • 本番DBや本番Stripeに影響する操作をしてしまう

わかりやすく言うと、.env に実キーを書いたままAIエージェントに作業させるのは、家の鍵、銀行カードの暗証番号、会社の金庫の鍵を机の上に並べた状態で、外部の優秀な作業代行者に「この部屋で自由に作業して」と頼むようなものです。

机の上に鍵・暗証番号・金庫のカードを広げたまま、AIエージェントに作業を任せている比喩イラスト

その人が悪意を持っていなくても、うっかり写真に写るかもしれません。
メモに書き写すかもしれません。
別の人に見える場所へ置いてしまうかもしれません。

AIエージェントも同じです。
悪意がなくても、指示の解釈ミス、ツール実行、ログ出力、ファイル編集の中で秘密情報が漏れる可能性があります。

だから、目標はこうです。

AIエージェントを信用しないのではなく、
AIエージェントがミスしても秘密情報が見えにくい構造にする。

今回のおすすめ方針

今回のおすすめ方針はシンプルです。

Claude Code / Codex本体には、原則として秘密情報を渡さない。

秘密情報が必要な開発コマンドだけ、
1Password CLIの op run 経由で実行する。

つまり、Claude CodeやCodex自体は普通に起動します。

claude
codex

または、普段使っているaliasがあるならそれを使います。

cc # claude --permission-mode autoのalias

そのうえで、APIキーやDB接続情報が必要な開発コマンドだけ、以下のように実行します。

op run --env-file=.env.1password -- pnpm dev

毎回長いので、後ほど scripts/run-safe.sh にまとめます。

./scripts/run-safe.sh pnpm dev

この方針のメリットは、Claude Code / Codex本体に秘密情報を持たせないことです。
秘密情報が必要なのは、実際にはAIエージェント本体ではなく、開発サーバー、ビルド、テスト、DBマイグレーション、デプロイなどのコマンドであることが多いからです。


1Password CLIとは何か

パスワード管理ツールとして1Passwordを使っている人も多いと思います。

普段の1Passwordは、ブラウザ拡張やデスクトップアプリでログイン情報、クレジットカード、APIキー、メモなどを安全に管理するためのものです。

1Password CLI は、それをターミナルや開発ワークフローから使えるようにするツールです。

1Password CLIを使うと、たとえば以下ができます。

  • 1Password内の秘密情報をターミナルから参照する
  • APIキーやパスワードを平文でファイルに書かずに扱う
  • op://... 形式の秘密参照を使う
  • op run で、指定したコマンドにだけ秘密情報を環境変数として渡す
  • op inject で、テンプレートファイルに秘密情報を注入する

この記事で中心になるのは op run です。

1Password公式ドキュメントでは、op run は環境変数内のsecret referenceを探し、対応する値を1Passwordから読み込み、その値を環境変数として利用できる状態で指定コマンドを実行すると説明されています。

1Password CLI と op run の仕組み図:.env.1password の op:// 参照を解決し、子プロセスにだけ環境変数として秘密情報を渡す流れ


1PasswordのVaultおすすめ運用

1Passwordを普段のパスワード管理にも使っている場合、開発用のAPIキーと、個人のログイン情報や本番キーを同じ場所に入れない方が安全です。

おすすめはVaultを分けることです。

例:

Private
  個人のログイン情報
  個人のクレジットカード
  個人用メモ

<会社名> Dev
  開発用APIキー
  開発用DB接続情報
  テスト用Stripeキー
  ローカル開発用Cloudflare Token

<会社名> Staging
  ステージング用APIキー
  ステージング用DB接続情報

<会社名> Prod
  本番APIキー
  本番DB接続情報
  本番Stripeキー

Private / Dev / Staging / Prod に分けた1Password Vaultの構成図とアクセス権イメージ

ポイントは以下です。

  • 個人用Vaultと仕事用Vaultを分ける
  • 開発用と本番用を分ける
  • Claude Code / Codexのローカル運用では原則Dev Vaultだけ使う
  • 本番キーはローカルAIエージェントに渡さない
  • 必要なキーだけ入れたVaultを作る
  • 退職者、外部委託、共同開発者がいる場合はVault単位で権限管理する
  • 使わなくなったキーはローテーションする

株式会社KOIYALのような会社で試す場合でも、この記事の例では実運用名ではなく、たとえば KOIYAL Demo Dev のようなテスト用のVault名・Item名を使うと安全です。


secret referenceとは何か

1Password CLIでは、秘密情報そのものではなく、秘密情報の場所を表す文字列を使えます。
これを secret reference(秘密参照) と呼びます。

形式は以下です。

op://Vault名/Item名/Field名

例:

op://<会社名> Dev/OpenAI/api_key
op://<会社名> Dev/Stripe/test_secret_key
op://<会社名> Dev/Database/database_url

.env.1password には、実キーではなくこの参照だけを書きます。

OPENAI_API_KEY=op://<会社名> Dev/OpenAI/api_key
STRIPE_SECRET_KEY=op://<会社名> Dev/Stripe/test_secret_key
DATABASE_URL=op://<会社名> Dev/Database/database_url

このファイルには実キーが入っていません。

ただし、Vault名やItem名は分かるため、.env.1password もGit管理しない方が無難です。


秘密参照パスの取得方法

秘密参照は、1Passwordアプリからコピーする方法が一番簡単です。

1Passwordアプリから取得する

  1. 1Passwordアプリを開く
  2. 対象のVaultを開く
  3. APIキーやパスワードが入っているItemを開く
  4. 対象Fieldを右クリック
  5. Copy Secret Reference を選ぶ
  6. .env.1password に貼り付ける

例:

OPENAI_API_KEY=op://<Vaulty名>/<Item名>/<Field名>
# 例えば、OPENAI_API_KEY=op://KOIYAL Dev/OpenAI/API Key

1Password公式ドキュメントでも、secret referenceはデスクトップアプリ、1Password for VS Code、CLI、手書きなどで作成できると説明されています。

CLIで確認する

Vault一覧:

op vault list

Vaultが存在するか確認:

op vault get "<Vault名>" > /dev/null && echo "Vault OK"

Vault内のItem一覧:

op item list --vault "<Vault名>"

Itemが存在するか確認:

op item get "OpenAI" --vault "<Vault名>" > /dev/null && echo "Item OK"

Field名を確認:

op item get "OpenAI" --vault "<Vault名>"

jq が使える場合、Field名だけ確認できます。

op item get "OpenAI" --vault "<Vault名>" --format json | jq -r '.fields[]?.label'

参照全体が正しいか確認:

op read "op://<Vault名>/OpenAI/api_key" > /dev/null && echo "Reference OK"

ここで重要なのは、値を表示しないことです。

op read "op://<Vault名>/OpenAI/api_key"

とすると、秘密情報がそのまま表示されます。
確認だけなら > /dev/null を付けます。


プロジェクトのファイル構成

おすすめは以下です。

project-root/
  .env.example
  .env.1password
  .gitignore
  .claude/
    settings.local.json
  scripts/
    run-safe.sh
    check-1password-env.sh

Codexだけで使う場合も、.env.example.env.1passwordscripts/run-safe.sh の考え方は同じです。


.env.example の作り方

.env.example には、必要な環境変数名だけを書きます。
実値は書きません。

OPENAI_API_KEY=
ANTHROPIC_API_KEY=
GEMINI_API_KEY=
STRIPE_SECRET_KEY=
CLERK_SECRET_KEY=
CLOUDFLARE_API_TOKEN=
DATABASE_URL=

これはGit管理してOKです。

目的は、「このプロジェクトで必要な環境変数は何か」を人間とAIエージェントに伝えることです。


.env.1password の作り方

.env.1password には、実値ではなく1Passwordのsecret referenceを書きます。

OPENAI_API_KEY=op://KOIYAL Dev/OpenAI/api_key
ANTHROPIC_API_KEY=op://KOIYAL Dev/Anthropic/api_key
GEMINI_API_KEY=op://KOIYAL Dev/Gemini/api_key
STRIPE_SECRET_KEY=op://KOIYAL Dev/Stripe/test_secret_key
CLERK_SECRET_KEY=op://KOIYAL Dev/Clerk/secret_key
CLOUDFLARE_API_TOKEN=op://KOIYAL Dev/Cloudflare/api_token
DATABASE_URL=op://KOIYAL Dev/Database/database_url

.gitignore には以下を入れます。

.env
.env.*
!.env.example

.env.1password は実値ではありませんが、Vault名やItem名などの内部構造が分かるため、原則Git管理しない方がよいです。

チームで共有する場合は、.env.1password.example のようなテンプレートにします。

OPENAI_API_KEY=TODO_1PASSWORD_REFERENCE
STRIPE_SECRET_KEY=TODO_1PASSWORD_REFERENCE
DATABASE_URL=TODO_1PASSWORD_REFERENCE

run-safe.sh を作る

毎回以下を打つのは面倒です。

op run --env-file=.env.1password -- pnpm dev

そこで、scripts/run-safe.sh を作ります。

#!/usr/bin/env bash
set -euo pipefail

cd "$(dirname "$0")/.."

ENV_FILE=".env.1password"

if ! command -v op >/dev/null 2>&1; then
  echo "1Password CLI 'op' が見つかりません。"
  exit 1
fi

if [ ! -f "$ENV_FILE" ]; then
  echo "$ENV_FILE が見つかりません。"
  exit 1
fi

op run --env-file="$ENV_FILE" -- "$@"

実行権限を付けます。

chmod +x scripts/run-safe.sh

使い方:

./scripts/run-safe.sh pnpm dev

Claude Code内から使うなら:

!./scripts/run-safe.sh pnpm dev

Codexでコマンド実行を許可している場合も考え方は同じです。


Claude Code / Codex本体を op run で包まない理由

最初はこうしたくなります。

op run --env-file=.env.1password -- claude

または、

op run --env-file=.env.1password -- codex

しかし、この記事ではこの方針をおすすめしません。

理由は2つあります。

1. AIエージェント本体に秘密情報を渡してしまう

op run -- claude のように起動すると、Claude Code本体のプロセスに秘密情報が環境変数として渡ります。

つまり、Claude Codeが実行するBashや子プロセスから、原理上はその環境変数にアクセスできます。

この記事の目的は、そもそもAIエージェント本体に秘密情報を持たせないことです。

2. Claude Codeでは --print エラーに遭遇するケースがある

環境によっては、以下のようなエラーになることがあります。

Error: Input must be provided either through stdin or as a prompt argument when using --print

また、cc というaliasでClaude Codeを起動している場合、

op run --env-file=.env.1password -- cc

とすると、シェルaliasが展開されず、OS上のCコンパイラ cc が起動してしまうことがあります。

その場合、以下のようなエラーになります。

cc: fatal error: no input files
compilation terminated.

そのため、方針はこうします。

Claude Code / Codex本体は普通に起動する。
秘密情報が必要なコマンドだけ op run で実行する。

Claude Codeのdeny / ask設定例

Claude Codeには権限設定があります。
公式ドキュメントでは、allowaskdeny のルールがあり、評価順は deny -> ask -> allow と説明されています。つまり、denyが最も強く優先されます。

Claude Code の deny / ask / allow の評価順と、それぞれに割り当てるコマンド分類のイメージ図

.claude/settings.local.json に以下のような設定を置きます。

{
  "permissions": {
    "deny": [
      "Read(./.env)",
      "Read(./.env.*)",
      "Read(./secrets/**)",

      "Bash(printenv *)",
      "Bash(env *)",
      "Bash(set *)",

      "Bash(op read *)",
      "Bash(op item get *)",
      "Bash(op vault *)",
      "Bash(op document get *)",

      "Bash(cat .env*)",
      "Bash(grep * .env*)",
      "Bash(rg * .env*)",

      "Bash(node -e*)",
      "Bash(python -c*)",
      "Bash(ruby -e*)",
      "Bash(perl -e*)"
    ],
    "ask": [
      "Bash(op run *)",
      "Bash(./scripts/run-safe.sh *)",
      "Bash(pnpm *)",
      "Bash(npm *)",
      "Bash(git *)"
    ]
  }
}

この設定の狙いは、Claude Codeを不便にすることではありません。

狙いは以下です。

秘密情報を読む操作はdeny。
秘密情報を使う可能性がある操作はask。
通常の開発操作は内容を見て許可。

許可するときに見るべきポイント

ここまで設定しても、実際にClaude CodeやCodexからコマンド実行の許可を求められたときに、何も考えずに全部許可していたら、せっかくの安全運用が無駄になります。

AIエージェントは便利ですが、許可ボタンを押すのは人間です。
最低限、以下だけは見てから許可を出してください。

コマンド許可時のチェックポイント6項目:秘密表示/外部送信/本番影響/破壊的操作/権限変更/op run の中身、を一覧化した図

1. 秘密情報を表示しようとしていないか

注意するコマンド:

printenv
env
set
cat .env
cat .env.local
grep KEY .env
rg SECRET .env
node -e ...
python -c ...

秘密情報を「使う」のと「表示する」のは違います。

たとえば開発サーバーがAPIキーを使うのは必要です。

./scripts/run-safe.sh pnpm dev

しかし、APIキーを画面に表示する必要は基本ありません。

printenv OPENAI_API_KEY

このようなコマンドは許可しない方が安全です。


2. 外部に送信しないか

注意するコマンド:

curl ...
wget ...
gh ...
git push ...
npm publish
wrangler deploy
vercel deploy
firebase deploy

ネットワーク送信を伴うコマンドは、コード、ログ、秘密情報、ビルド成果物が外部へ送られる可能性があります。

特に、以下は内容を確認してから許可します。

  • curl
  • wget
  • gh
  • git push
  • npm publish
  • wrangler deploy
  • vercel deploy --prod
  • firebase deploy

3. 本番に影響しないか

ローカル開発と本番反映はまったく違います。

比較:

wrangler dev

これはローカル開発寄りです。

wrangler deploy

これは本番・ステージングに影響する可能性があります。

他にも注意すべきコマンドがあります。

prisma migrate deploy
supabase db push
stripe ...
vercel deploy --prod
firebase deploy

本番に影響しそうなコマンドは、原則としてask、人間確認です。


4. ファイルを大量削除・上書きしないか

注意するコマンド:

rm -rf
git clean
git reset --hard
find . -delete
mv
cp -r

AIエージェントは、リファクタリングや生成物整理のために削除コマンドを提案することがあります。

しかし、rm -rfgit clean は取り返しがつかないことがあります。


5. 権限や認証を変更しないか

注意するコマンド:

sudo
chmod
chown
gh auth login
op signin
npm config set
git config --global

認証や権限変更は、人間がやる作業として分けた方が安全です。

Claude CodeやCodexにログイン操作、トークン発行、権限変更を任せると、意図せず強い権限を渡す可能性があります。


6. op run の中で何を実行しているか

op run 自体は悪いものではありません。
この記事の中心です。

ただし、以下のように何でも許可してはいけません。

op run --env-file=.env.1password -- printenv

これは秘密情報を表示する可能性があります。

許可してよい例:

op run --env-file=.env.1password -- pnpm dev

慎重に見る例:

op run --env-file=.env.1password -- wrangler deploy

拒否寄りの例:

op run --env-file=.env.1password -- printenv

見るべきなのは、op run ではなく、その後ろのコマンドです。


Codexで考えるべきこと

Codexでも考え方は同じです。

Codexの公式ドキュメントでは、安全運用のためにサンドボックス、承認、ネットワーク制御が説明されています。Codexのサンドボックスは、Codex自身の操作だけでなく、Codexが実行するコマンドにも適用されると説明されています。

また、Codex CLIには承認やサンドボックスをバイパスする危険なオプションもあります。

--dangerously-bypass-approvals-and-sandbox
--yolo

公式ドキュメントでは、これは承認とサンドボックスなしで全コマンドを実行するもので、外部で十分に隔離された環境でのみ使うべきものとして説明されています。

つまり、Codexでも以下が基本です。

Codex本体には秘密情報を常時渡さない。
秘密情報が必要なコマンドだけ op run で実行する。
サンドボックスや承認を安易に無効化しない。

よく使うコマンド例

Vite / Next.jsなどの開発サーバー:

./scripts/run-safe.sh pnpm dev

npmプロジェクト:

./scripts/run-safe.sh npm run dev

Cloudflare Workers:

./scripts/run-safe.sh pnpm wrangler dev

ビルド:

./scripts/run-safe.sh pnpm build

テスト:

./scripts/run-safe.sh pnpm test

Drizzle:

./scripts/run-safe.sh pnpm drizzle-kit push

Prisma:

./scripts/run-safe.sh pnpm prisma migrate dev

ただし、秘密情報が不要なコマンドまで何でも op run で包む必要はありません。

型チェックだけなら、通常はそのままでよいことが多いです。

pnpm typecheck

.env.1password の参照チェック

.env.1password の参照が正しいか、値を表示せずに確認するスクリプトを置いておくと便利です。

scripts/check-1password-env.sh

#!/usr/bin/env bash
set -euo pipefail

ENV_FILE=".env.1password"

if [ ! -f "$ENV_FILE" ]; then
  echo "$ENV_FILE が見つかりません。"
  exit 1
fi

while IFS='=' read -r key ref; do
  [[ -z "${key:-}" ]] && continue
  [[ "$key" =~ ^# ]] && continue

  if [[ "${ref:-}" != op://* ]]; then
    echo "SKIP: $key は op:// 参照ではありません"
    continue
  fi

  if op read "$ref" > /dev/null 2>&1; then
    echo "OK:   $key"
  else
    echo "NG:   $key -> $ref"
  fi
done < "$ENV_FILE"

実行権限:

chmod +x scripts/check-1password-env.sh

実行:

./scripts/check-1password-env.sh

このスクリプトは値を表示せず、参照が解決できるかだけ確認します。


よくある躓きポイント

op run --env-file=.env.1password -- claude でエラーになる

以下のようなエラーになる場合があります。

Error: Input must be provided either through stdin or as a prompt argument when using --print

この記事の方針では、Claude Code本体を op run で起動しません。

Claude Codeは普通に起動します。

claude

秘密情報が必要なコマンドだけ以下で実行します。

./scripts/run-safe.sh pnpm dev

op run --env-file=.env.1password -- cc でCコンパイラが動く

cc をClaude Code起動用のaliasにしている場合でも、op run -- ... の後ろではaliasが展開されないことがあります。

その結果、Cコンパイラの cc が起動し、以下のようなエラーになります。

cc: fatal error: no input files
compilation terminated.

対処は、op run の後ろで cc aliasを使わないことです。

pnpm dev が存在しない

pnpm dev は、そのプロジェクトの package.jsonscripts.dev を実行するコマンドです。

確認:

pnpm run

または、

cat package.json

dev がなければ、そのプロジェクトに合ったコマンドを使います。

Vault名・Item名・Field名が違う

secret referenceが間違っていると、op run で解決できません。

確認:

op vault list
op item list --vault "<会社名> Dev"
op item get "OpenAI" --vault "<会社名> Dev"
op read "op://<会社名> Dev/OpenAI/api_key" > /dev/null && echo "Reference OK"

値を表示しないことが重要です。


まとめ

Claude CodeやCodexはとても便利です。
しかし、.env にAPIキーやパスワードを直接書いたまま使うと、AIエージェントが秘密情報に触れられる状態になります。

この記事のおすすめは以下です。

.env に実キーを書かない
.env.1password に op://... の秘密参照を書く
Claude Code / Codex本体には秘密情報を渡さない
秘密情報が必要なコマンドだけ op run で実行する
.env 系ファイルの読み取りはdenyする
printenv / env / op read / op item get は基本denyする
コマンド許可時は、秘密表示・外部送信・本番影響・破壊的操作を見る
本番キーはローカルAIエージェント運用に渡さない
Vaultは個人用・開発用・本番用で分ける

AIエージェントを安全に使うコツは、「AIを完全に信頼する」ことでも、「AIを怖がって使わない」ことでもありません。

AIがミスしても秘密情報が漏れにくい構造にしておくことです。

1Password CLIと op run を使うと、通常の .env 直書き運用よりも、かなり安全な形に近づけられます。


参考ソース