作業は13体のエージェントに分割した。Paperclipは鍵を分割しなかった。
71,000スターのエージェントフレームワークをスキャンしたところ、13体中12体のエージェントが同じ平文トークンを保持していました。エージェント群を運用し始めると、「秘密情報は設定ファイルにある」というやり方は、近道ではなく増幅装置になります。
現代的なやり方を選びました。大きなエージェント1体を置くのではなく、エージェント群を立ち上げました — チケットを仕分けるもの、文章を書くもの、デザインパイプラインを動かすもの、十数体それぞれに担当と設定を持たせています。そのほうが安全に感じられます。影響範囲は狭く、最小権限も効きやすい。
ところが、セキュリティスキャンで Paperclip と呼ばれる 71,000スターのエージェントフレームワークの設定を調べたところ、13体中12体のエージェントが同じ資格情報 — 同一のトークンを、各エージェントの設定に平文で貼り付けていたことが判明しました[1]。ある Anthropic API キーは、デザインエージェントの設定に平文のままハードコードされていました。ある bot トークンは、本来関係のないエージェントから読み取れる状態にありました。
13のドア。1つの鍵が12か所にコピーされている。最も弱いエージェントから盗めば、残りの11が手に入る。
実際に起きていたこと
Paperclip は各エージェントに MCP サーバー設定を与えます — どのツールやサービスにアクセスでき、どう認証するかをエージェントに伝えるファイルです。過程のどこかで、秘密情報はそのファイルの中に文字列として直接書き込まれていました。n8n の JWT、bearer トークン、Anthropic の API キーです。参照でもなく、実行時の注入でもなく、打ち込まれた — そして次のエージェントを立ち上げる作業がコピーペーストだったため、エージェント群全体に複製されました。
スキャンは3点を検出しました。12体のエージェントに共有される平文トークン(HIGH)。デザインエージェントの設定に平文で置かれた Anthropic キー(CRITICAL)。そして、本来対象とすべきでないエージェントに与えられていた bot トークン — 単純な最小権限の逸脱です[1]。評価に値するのは、Paperclip チームが迅速に対応した点です。資格情報参照への移行、エージェント間読み取り時の設定のマスキング、バインディング同期の強制を実施しました[2]。方向としては正しい。
これは Paperclip の不注意ではない
じっくり考えるべきなのはここです。Paperclip は、ほぼすべてのフレームワークが行っていることをしたに過ぎません。設定ファイルに秘密情報を置くのは、ソフトウェアが30年間行ってきた認証の方法です。成り立っていたのは、アプリが1つ、設定が1つ、鍵がどこにあるかを知る運用者が1人だったからです。
その習慣の下で時代が変わりました。マルチエージェントシステムは、設定が1つのアプリではありません — 十数のプロセスがあり、それぞれにファイルがあり、それぞれが前のコピーです。設定内平文は、漏えい箇所が1か所であれば許容できる近道でした。13か所では、同じ近道は漏えい1件が漏えい13件を意味します。そして「どのエージェントがやったのか」には答えがありません。ログの中のトークンは、すべてのエージェントに属していたものだからです。
資格情報参照 — Paperclip が投入した修正 — は確かに改善です。ただし、何が変わり、何が変わらないかに注意してください。参照であっても、エージェントが実行される場所で実際の秘密情報に解決されます。エージェント自身、またはそれを侵害するものは、解決後の値を読み取れます。そしてフレームワーク自体のバグトラッカーには、次の失敗パターンがすでに現れています。参照がバインディングから乖離し、設定は記入されているように見えながら、検証が黙って失敗する問題です[3]。秘密情報は1レイヤー後ろに移動しただけです。建物の外には出ていません。
Paperclip だけの話ではない
同じ週、同じ根本原因、別のリポジトリ。広く使われているコーディングエージェントが、生の .env の値 — パスワード、トークン、API キー — をチャット出力にそのまま表示するとして報告されました。別のエージェントランナーは、親プロセスの環境変数を子プロセスにすべて引き渡しており、すべてのプロバイダーのキーが子プロセスから見える状態でした[4]。音声フックは、会話記録を資格情報ごと、誰でも読み取れる /tmp に書き込んでいました[5]。独立したチーム、独立した脅威モデル、共有された仮定が1つあります — 秘密情報がエージェントから見える場所にあってよい、というものです。攻撃者が主張すべきなのは、それではいけないということだけです。
このために、意図して作られている
Clavitor は逆の仮定から始めます。エージェントは資格情報を一切保持しません。エージェントは操作を要求し、その要求は傍受され、エージェントが読めない秘密情報に対して認証され、実行されます。トークンを貼り込む設定は存在しません。設定にトークンが存在しないからです。13体のエージェントにコピーするものもありません。エージェントの環境が、盗む価値のあるものをそもそも保持していないからです。
各エージェントは、名前に示された対象にのみアクセスします — ストア全体ではありません。そのため、bot トークンが、要求したことのないエージェントから読み取られることはありません。そしてすべての操作は、実行した特定のアクターに紐づいて記録されます。12体のエージェントが共有していたトークンに紐づくことはありません — 「どちらがやったのか」に答えがあるということです。
正直に述べると、これによりエージェントがハッキング不能になるわけではありません。侵害されたエージェントは、その時点で、認可されていた操作を実行できます。できないのは、鍵を持ち出して他の12体になりすますことです — 持ち出す鍵がその手に存在しないからです。
教訓は「トークンをローテーションする」ことではない
Paperclip はトークンをローテーションし、移行を完了し、issue を閉じるでしょう。それで構いません — そうすべきです。しかし、ローテーションが教訓なのではありません。教訓は、1つのアプリではなくエージェント群を持つようになった時点で、「秘密情報は設定ファイルにある」というやり方は近道ではなく増幅装置になる、ということです。増幅装置は、秘密情報の読み取りを少し難しくしても修正できません。修正するのは、秘密情報がそもそもエージェントの手に渡らないようにすることです。
エージェントの時代に資格情報ツールが守るべきルールを書き留めました — 秘密情報がコードの実行場所に置かれないこと、エージェントは名前に示された対象にのみアクセスすることなどです。あなたの環境をそれに照らして確認してください: clavitor.ai/rules。
Clavitor(@clavitorai)は、AIエージェントのために、そしてAIエージェントから守るために作られた資格情報保管庫です。clavitor.ai
出典
[1] Paperclip エージェントフレームワーク — 資格情報の管理に関する調査結果(CFG-H1 共有平文トークン、CFG-C1 ハードコードされた Anthropic キー、CFG-H2 スコープ設定を誤った bot トークン): https://github.com/paperclipai/paperclip
[2] Paperclip — エージェントの secret-binding 同期をライフサイクル処理全体で強制する(マージ済み): https://github.com/paperclipai/paperclip/pull/8307
[3] Paperclip — secret_ref の env エントリが secret_bindings の行と乖離することがあり、設定は記入されているように見えながら検証が黙って失敗する(#8309): https://github.com/paperclipai/paperclip/issues/8309
[4] Chetter — runBatchAgent がランナーの環境変数をすべて継承し、プロバイダーの API キーをサブプロセスに公開してしまう(#56): https://github.com/flatout-works/chetter/issues/56
[5] Claude Code voice hook — 会話記録全体(資格情報を含む)が誰でも読み取れる /tmp に書き込まれる(#58): https://github.com/rodlaneedu-hash/claude-code-voice-hook/issues/58