スマホの 1Password は、もう手放せません。指紋認証や顔認証でロックを解除すれば、マスターパスワード以外は何も覚えなくていい。サイトごとに違う長いパスワードを、頭の中から完全に追い出せます。
ただ、その便利さを「スマホとブラウザの中だけ」で終わらせているなら、開発者としては少しもったいないかもしれません。同じ Vault は、ターミナルからも引けます。op(1Password CLI)を噛ませると、.env に平文の API キーを置く生活から抜けられました。
この記事は、難しい構成の話ではありません。やることは「.env に参照だけ書いて、op run で実行する」のほぼ一点です。前半だけ読んでもらえれば、十分に持ち帰れるはずです。
先に結論
.envに実値ではなくop://参照を書く- 実行時に
op run --env-file=.env -- <コマンド>で包むと、参照が実値に解決されて渡る - 実値を
.envやシェル履歴に平文で残さずに済む。.envの中身は参照文字列だけ - WSL から使う場合は、Windows 側の
op.exe runを叩く - AI エージェント(Claude Code など)にも「
op runを使って」と作法として渡せる
想定読者
- すでに 1Password を契約していて、スマホ・ブラウザでは使っている開発者
.envに API キーやパスワードを平文で書くことに、うっすら不安がある人- Claude Code などのエージェントにコマンドを任せていて、シークレットの渡し方に悩んでいる人
「1Password CLI って何?」という段階の方は、1Password CLI の公式ドキュメントを先に眺めておくと読みやすくなります。
きっかけ:.env の平文が、急に怖くなった
発端は、Claude Code に自動化スクリプトを任せるようになったことでした。
ちょっとした作業を Claude Code にスクリプト化させて動かす。便利なのですが、その過程で ANTHROPIC_API_KEY や、自分が管理するサービスの認証情報をスクリプトに渡す場面が、どんどん増えていきました。素朴にやれば、.env に平文で書くことになります。
手が動き始めたところで、ふと止まりました。これらの鍵を、どこに置くのか。ちょうど X(旧 Twitter)で「.env をうっかり公開リポジトリに上げて事故った」という話題が流れてきて、急に怖くなりました。エージェントにスクリプトを書かせて動かす以上、鍵の置き場所は自分で設計しておかないと、いつか事故りそうです。
そこで思い出したのが、自分がすでに 1Password を使っているという事実です。スマホで便利に使っているあの Vault は、op を入れればコマンドラインからも引けるはず。調べてみると、まさにそのための仕組みが用意されていました。
本題:.env に「参照」を書く
ポイントは、.env に実値を書かないことです。代わりに、1Password のどのアイテムを見ればいいかを示す op:// 参照だけを書きます。
# .env (中身は参照だけ。実際の鍵は1Password側にある)
ANTHROPIC_API_KEY=op://Dev/anthropic/credential
MY_SERVICE_USER=op://Dev/my-service/username
MY_SERVICE_PASS=op://Dev/my-service/password
基本形は op://Vault名/アイテム名/フィールド名 で、1Password 内のシークレットを指し示します(セクションや ID 指定もできます)。これだけでは、ただの文字列です。鍵そのものはどこにもありません。
実行するときに op run で包みます。--env-file は、読み込む環境変数ファイルを指定するオプションです。
op run --env-file=.env -- node task.js
op run は、--env-file で渡した .env の中の op:// 参照を 1Password から解決し、実値を環境変数としてサブコマンドに渡します。task.js の側は process.env.MY_SERVICE_PASS(プログラムから環境変数を読む書き方。ここでは Node.js の例ですが、お使いの言語の環境変数の読み方で構いません)を読むだけで、本物のパスワードを受け取れます。コマンドが終われば、その環境変数は消えます。
詳しい挙動は op run の公式リファレンスにまとまっています。
何が嬉しいのか
整理すると、主に次の事故を減らせます。
.envやスクリプトに平文で保存せずに済む:.envの中身は参照だけ。本物の鍵は 1Password 側に置く- シェル履歴に残らない:
export PASS=...のようにコマンドで直接入力しないので、履歴に鍵が残らない - ログにも出にくい:
op runは解決した秘密情報を標準出力・標準エラーでマスキングしてくれます(ただしこれは多層防御の一枚で、そもそも秘密をログに出さない設計が前提です)
ただし、万能ではありません。op run が解決した実値は、実行中プロセスの環境変数には載ります。同一ユーザー権限で動く別プロセスからは読めますし、子プロセスにも継承されます。つまりこの方式が守るのは「平文ファイル・コマンド履歴・ログ経由の漏洩」であって、端末そのものが侵害された場合の防御ではない、と割り切っています。
個人的にいちばん効いたのは、最初の一点でした。.env をうっかり commit しても、漏れるのは「どの Vault のどのアイテムを使っているか」という参照情報だけで、鍵そのものは漏れません。事故ったときのダメージが、桁違いに小さくなります(とはいえ参照先の構造は見えるので、commit を積極的に推奨するわけではありません)。
なお、シークレットを平文から逃がす手段は op だけではありません(sops、direnv、各種 Vault など)。ただ、それらはシークレット管理の専用ツールで、普段のパスワード管理には使いません。私が op を選んだ理由は機能比較ではなく、もっと単純です。スマホで指紋認証ログインに使っているあの便利さの延長で、開発のシークレットまで同じ Vault に乗せられる。新しいツールを増やすのではなく、すでに毎日使っているものがターミナルにも来る——それが決め手でした。
WSL から使う場合は op.exe run
ここは、WSL を使っている人だけ気にすればいい補足です。
私の環境は Windows(WSL2)が主戦場です。WSL の中に Linux 版の op を入れる手もありますが、1Password のデスクトップアプリ連携(CLI が 1Password アプリ本体に認証を委ねる仕組み。生体認証や Vault の解決をアプリ側が担う)は Windows 側にあります。そこで、WSL から Windows 側の op.exe を直接呼ぶ形にしています。
# WSL内から、Windows側のバイナリを叩く(Vaultの確認など)
op.exe vault list
op ではなく op.exe という点だけ違います。WSL と 1Password を組み合わせるとき、ここで一度つまずく人が多い印象です(少なくとも自分はそうでした)。
ただし、op.exe run でコマンドを包む場合は注意が必要です。
op.exe run --env-file=.env -- node task.js
op.exe は Windows 側のプロセスとして動くため、後ろに続く node も Windows 側で解決されます。WSL の中だけに Node.js を入れている場合、「WSL のスクリプトがそのまま動く」とはなりません。Windows 側にも実行環境が必要になります。このあたりは環境差が大きいので、まず PowerShell で op run を動かしてから WSL 連携を試すのが安全です。ここで書いているのは、あくまで自分の環境での運用です。
Claude Code には「作法」として渡す
ここまでくると、AI エージェントとの相性の良さが見えてきます。
Claude Code に自動化スクリプトを任せるとき、私が毎回コマンドを手で打つ必要はありません。「op run を使って実行して」と一度伝えておけば、Claude Code がコマンドを組み立てるときに op run(WSL なら op.exe run)を前置してくれます。
人間が鍵を扱うのではなく、エージェントに「シークレットはこの作法で触る」というルールごと渡しておく。この発想に切り替えてから、鍵の扱いがだいぶ気楽になりました。
ただし、ここは正直に書いておきます。op run は、AI エージェントから秘密を完全に隠す仕組みではありません。エージェントにコマンド実行やファイル編集を許可している以上、実行中の環境変数にはアクセスできます。たとえば printenv ANTHROPIC_API_KEY を実行されれば、値そのものを読めてしまいます。
私が op run に期待しているのは、秘密へのアクセス権限を消すことではなく、API キーを .env やスクリプトに平文で書き残す事故を減らし、鍵の置き場所を 1Password に寄せることです。その上で、用途ごとに Vault を分けて権限を最小化する、実行させるコマンドを確認する、秘密をログに出さない——この基本は op run を使っても変わりません。
前半の持ち帰りは、ここまでです。.env に参照を書いて op run で包む。これだけで、.env 平文の不安からはほぼ抜けられます。
ここから先は、長時間の無人自動化まで踏み込みたい人向けの補足です。前半で十分という方は、ここで離脱して問題ありません。
補足:無人で長時間回すときに踏んだこと
ここからは、ログインが必要な自分のアカウントでの定型作業を、無人で自動化する場合の話です。自分が利用権を持つサービスに、自分の認証情報でログインして使う前提で書いています。対象サービスの利用規約や、サーバへの負荷には配慮してください。
ログインのセッション(cookie)をどう扱うか
ブラウザ自動化でログインを挟むと、ログイン後のセッション(cookie 等)を storageState として保存し、次回から使い回す手法があります。毎回ログインし直さなくて済むので、速い。
ただ、ここに落とし穴があります。storageState を保存すると、ログイン済みの cookie が平文の JSON でディスクに残ります。op でパスワードを守っても、実質的な「鍵」がパスワードからセッション cookie に移り、そこが平文になっているわけです。攻撃面がスライドしただけ、とも言えます。Playwright の公式ドキュメントでも、認証状態を保存したファイルにはなりすましに使える cookie やトークンが含まれ得るため、リポジトリに commit しないよう注意されています(公開・非公開を問わず)。
私はこの構成では、storageState を使わず毎回ログインし直す方を選びました。無人で動かすのが目的で、速さより「鍵を 1Password の中に留めておく」ことを優先したかったからです。遅くはなりますが、ディスクにセッションを残さずに済みます。
このあたりは正解が一つではなく、再ログインの頻度やサービス側の仕様によって判断が変わる部分だと思います。
毎回ログインの代償と、その解
毎回ログインを選ぶと、当然ログインの回数が増えます。すると op.exe のデスクトップアプリ連携では、認証セッションのタイムアウトに引っかかります。1Password の公式ドキュメントによると、デスクトップアプリ連携で CLI を認可した場合、一定時間の非アクティブでセッションが失効する仕様です。長時間の無人運用だと、途中で生体認証を求められて止まってしまいます。
これを回避するために、生体認証を経由しないサービスアカウントトークンに切り替えました。結果として、4 時間ほどの無人自動化が安定して回るようになりました。
なお、サービスアカウントトークンは生体認証を省ける代わりに、それ自体が強い認証情報です。用途専用の Vault だけを許可して権限を最小化し、通常の .env やリポジトリに平文で置かない、漏洩が疑われたら速やかにローテーション(再発行)する——この前提はセットで運用します。
サービスアカウントの作成手順や、設定でハマったポイントは、別記事で詳しく書いています。必要な方はそちらをどうぞ。
まとめ
スマホで便利に使っている 1Password は、op を入れればそのままターミナルにも来ます。
.envには実値ではなくop://参照を書くop run --env-file=.env -- <コマンド>で包めば、実値を.envやシェル履歴に平文で残さずに済む- WSL なら
op.exe runを使う - Claude Code などのエージェントには「
op runを使って」と作法ごと渡せる - 長時間の無人自動化まで踏み込むなら、サービスアカウントという選択肢がある(別記事参照)
API キーや認証情報の置き場所に、うっすら不安を感じている開発者は少なくないはずです。すでに 1Password を使っているなら、op を足すだけで、その不安の大部分は手当てできます。まずは .env の参照化から試してみてください。
備考:エージェントに対してもう一段絞りたい場合
本文で書いたとおり、op run は実行権を持つエージェントから秘密を完全に隠すものではありません。もう一段絞りたい場合、方向としては次のようなものがあります。いずれも単体で完全に防げるものではなく、多層防御の一枚として考えるのが現実的です。
- Claude Code の hook で危険なコマンドを抑止する:
env/printenvや.envの読み出しなどを、実行前のフックで弾く。ただし別経路での環境変数参照など抜け道は残るので、過信はしない - 用途ごとに Vault やサービスアカウントを分ける:1 つのトークンに全権限を持たせず、必要最小限のスコープに絞る
- 権限の強い操作は人間が確認する:エージェントに丸投げせず、実行コマンドをレビューするフローを挟む
- そもそも見せたくない秘密は、エージェントの実行環境に置かない:別プロセス・別ホストに分離する
ここでは方向性の提示にとどめます。それぞれの具体的な設定は、また別の機会に。

コメント