1Password EnvironmentsをWindows実機で一周 — op runの値注入テストとconcealedマスクが外れる罠

開発claude-code-tools

1Password EnvironmentsをWindows実機で一周 — op runの値注入テストとconcealedマスクが外れる罠

1PasswordがCodexに対応したというブログ記事を朝に見つけて、7月2日に「Windows未対応」で撤退したメモを思い出した。調べ直させると、7月14日の安定版8.12.28でMCP連携が解禁されていた。Claude CodeとCodexの両方から1Password MCPサーバーへの接続を確認し、設定手順は別記事としてまとめさせた。

書き上がった記事を読み返すと、設定手順は揃っているのに「これができると何が嬉しいのか」を正面から書いた節が無い。指摘して追記させた。セッションログにシークレットが乗らない、.env 直読みの綱渡りをやめられる——並んだ言葉は正しそうに見える。

ただ、この時点では「1Passwordを挟むと何が良くなるのか」を自分の言葉で説明できずにいた。AIにシークレットの管理を任せたいのに、AIが値を読めてしまうなら本末転倒だ。その線引きは本当に成立しているのか。ダミー値で一周して確かめることにした。

注入テストを自分の手でなぞる

進め方は二段構えにした。まずClaude Code側で模擬検証を実行させ、各ステップの実測出力を「想定出力」として先に出させる。次に私が自分のターミナルで同じコマンドを打ち、想定と一致するかを見る。AIの説明を鵜呑みにするのではなく、同じ結果を自分の手で再現できて初めてクリアという建て付けだ。

模擬検証の段階で、実行のたびに承認ダイアログが出ることも見えていた。2回目は13.5秒待ち。3回目で承認がキャッシュされるかも試させたが、やはり出た。この時点では「まあテストだから」と流していた。

私の側の手順はこう進んだ。

  • Step 0: 作らせたEnvironment「claude-mcp-test」をアプリで目視確認——のはずが、どこにあるのか分からない。サイドバーを眺めても見当たらず、結局アプリの検索バーに名前を打ち込むのが最速だった。Windows版アプリでEnvironmentを表示できると分かったのはここでの収穫。TEST_LOGIN_ID / TEST_LOGIN_PASSWORD の2変数が入っている
  • Step 1: 注入に使う op run --environment は安定版CLIにまだ無い。ベータ版2.38.0-beta.01を op-beta.exe という別名でダウンロードし、既存の安定版 op と併置した。バージョン表示が想定出力と一致
  • Step 2: 注入を実行。承認ダイアログを1回OKすると、Nodeプロセスに値が届いた
.\op-beta.exe run --environment <Environment ID> -- node -e "console.log(process.env.TEST_LOGIN_ID)"

IDはそのまま表示され、パスワードは <concealed by 1Password> に置き換わって出た。模擬検証の想定出力と完全一致。マスクは効いている。この仕組みは信用していい——そう思っていた。

Step 0の画面はダミー値しか映っていないので、スクリーンショットを撮って「こういう画面だよ」という案内ごと記事に足させた。第三者の手順書としては、コマンドより先にこの1枚があるほうが迷わない。

マスクが外れた

最後のステップは「AIに見せずに値を変える」実験だった。アプリでパスワードの値を書き換えてから、同じコマンドを再実行する。AIのセッションを経由せずに値が変わったことを、注入結果で確認する段取りだ。

再実行した出力を見て、二度見した。さっきまで <concealed by 1Password> だった行に、書き換えたばかりの値がそのまま出ている。マスクが消えていた。

Claude Codeに投げると、「op run の伏字化はconcealedフラグが立っている変数の値だけを出力から探して置き換える動作なので、アプリで値を編集したときにフラグが外れたのでは」という仮説が返ってきた。アプリの編集画面を開くと、変数ごとの「⋮」メニューに「デフォルトで値を非表示にする」という項目がある。これを「表示する」に切り替えて再実行すると値が生で出て、「非表示」に戻すと <concealed by 1Password> が復活した。フラグの実体はこのメニューで、編集時に表示へ切り替えるとフラグごと外れる。原因が一本の線でつながった。

マスクを仕組みごと理解できたのは、この脱線のおかげだ。伏字は「秘密だから隠す」のではなく「concealedと印を付けた変数を隠す」のであって、印の管理は人間側の責任として残る。「⋮」メニューのスクリーンショットも撮って、記事に罠として追記させた。

「なぜ良くなるのか」を図にさせた

テストは通ったが、朝の疑問は開いたままだった。そもそもClaude自身はパスワードの値をどうやって取っているのか。シーケンス図のSVGを作らせて整理した。

答えは「取っていない」だった。値の通り道は 1Password → op → node の3プロセスのメモリ内だけで、ディスクにも、Claudeのセッションログにも現れない。Claude Codeが扱っているのは変数の名前とEnvironmentの操作だけ。値は、私が承認した実行プロセスへ直送される。

厳密に言うと、値はこのPCのメモリ内では復号済みの平文で存在する。私は「暗号化されたまま渡るのか」と勘違いしかけたが、守られているのは「ディスクに書かれない・AIのセッションに乗らない」の2点であって、実行プロセスの中では普通の環境変数だ。この区別も図を作らせる過程で正した。

これまでの .env は、gitignoreしていても平文でディスクに残り続け、「AIに読ませない」という運用ルールで守っていた。1Passwordを挟むと、この守りが運用の注意から構造に変わる。Turso接続情報を移行した場合のシーケンス図も2枚目として作らせたが、スクリプト側は無変更で、変わるのは値の出どころだけという構図も腹に落ちた。

本番用の箱を作る

理解が固まったので、本番用のEnvironmentに手を付けた。手を付ける前に確かめたのは「AIに値を読ませずにEnvironmentへ変数を入れられるのか」だ。私は「値を変数で渡せばAIに読まれずに済むのでは」と考えていたが、もっと簡単な経路があった。アプリの画面から .env を直接インポートすればよく、AIはそもそも経由しない。分担は「箱と名前はAI、値は人間」。

  • Environment「turso-replicas」の箱をMCP経由でClaude Codeに作らせた。MCPサーバーには値を読むツールがそもそも無いので、ここは安心して任せられる
  • 値の投入は私の担当。1Passwordアプリの画面から .env をインポートした
  • 変数一覧を確認させたら「0件」と返ってきた。アプリを見ると、右上に保存ボタンが残っていた。押し忘れである
  • 保存後、26変数が入った。1Password経由でTursoの財務データ再生成スクリプトを回すと、.env 経由の結果と完全一致した(差分は生成時刻の2行のみ)

成果物と学び

今日のコンテンツ化は、設定手順と実地テストをまとめた記事1本、Environment検索画面と「⋮」メニューのスクリーンショット2枚、シーケンス図SVG2枚。実験の脱線も含めて記事の「実地テスト」節に履歴として残させた。6月に「Windows未対応」で撤退したまま宙に浮いていた検討メモ2件にも完了を追記させ、宿題を閉じた。

  • op run の出力マスクはconcealedフラグに連動する。アプリで値を編集したら、非表示設定が残っているかを必ず確認する
  • MCPサーバーには値を読むツールが無い。「AIは名前だけ、値は人間」の分担が仕組みで強制される
  • CLIベータは op-beta.exe の別名併置で安定版と共存できた(2.38.0-beta.01)
  • デスクトップ認証は実行のたびに承認ダイアログが出る。実測3回中3回、各約14秒

残った引っかかりは、この承認ダイアログだ。設定でスキップできないかを調べさせたが、そういう設定は存在しない。人間を挟むのが設計思想だからだ。日次バッチに組み込むには毎回の承認が壁になるが、自動化にはService Accountという別の認証方式が用意されていて、テスト環境では初回だけ承認すれば以降は無人で通ることまで確認した。本番のTurso接続情報をこの方式へ移すのは、明日の作業に積んだ。