AIエージェントの認証情報の問題は「権限」でなく「残り方」

AIエージェントに認証情報を扱わせる設計というと、多くの場合「AIに何をしてよいか」という権限の話が先に立ちます。どの操作を自動で実行してよいか、不可逆な操作は誰の承認が要るか、という許可規則の設計です。この論点はそれ自体が重要な別テーマなので、本記事では扱いません(許可規則の組み方は AIエージェント権限設計 にまとめています)。

ここで扱うのは、権限を正しく絞ってあっても起こる、もう一段手前の問題です。チャットに貼られた値は、会話ログにそのまま残ります。検査コマンドの出力に出た値も、そのセッションの記録に残ります。AIエージェントは会話の履歴や直前のコマンド出力を後続の判断材料として参照する仕組みで動いているため、一度どこかに値が「出て」しまうと、それは要約や引用のかたちで何度でも呼び出されうる状態になります。

つまり設計の目標は「AIに何をさせないか」を決めることだけでなく、「AIが値そのものを一度も見ずに機能を使える経路を作ること」です。この違いを最初に立てておかないと、権限設計だけを丁寧にやって値の受け渡しを素通しにする、という抜け方が起こります。

事故は2種類ある — 人が貼る事故と、AI自身が開いてしまう事故

relmeaの運用記録を見返すと、認証情報の露出は性質の異なる2種類で起きています。

人がAIに言われるままに値を貼る事故

広告APIの読み取りツールを構築していた段で、AIは「この形式でチャットに貼ってください」とクライアントID・クライアントシークレットの入力を求めました。同じ構築作業の中で、ログに秘密を出さないマスキングの実装は検品役に3回検品させていたにもかかわらず、値の入口となる受け渡し方法では自分が穴を開けかけていたことになります。運営者が「ここに貼ってはだめだ」と止めて事なきを得ましたが、これは典型的に「人が言われた通りに従うと起きる」事故です。

AI自身が自分の検査で値を開く事故

動画レンダリング基盤の本番デプロイ前に、「秘密がハードコードされていないか」を確かめるために対象を src scripts *.ts *.json という指定でgrepを実行したところ、*.json という拡張子ワイルドカードが、.gitignore 済みのはずのサービスアカウント鍵ファイルにヒットしました。結果として、サービスアカウントの秘密鍵が全文、検査結果としてそのままセッションの記録に転写されました。git管理そのものは無傷で、未追跡のファイルであり履歴にも混入していませんでしたが、鍵の実体はセッションの中に残ったため、鍵のローテーションが必要になりました。

一つ目は「人に値を貼らせない」という運用規律で防げます。しかし二つ目は、AI自身が善意で行う検査そのものが原因になるため、防ぎ方が別に要ります。同じ「認証情報の事故」という言葉でくくると、対処法を一つに絞ってしまい、どちらか片方しか防げません。

認証情報の値を見せずに使わせる3つの経路

relmeaの運用では、AIが値そのものを読まずに機能を使えるようにする経路を、状況に応じて3通り使い分けています。

経路1: ひな形ファイルを人が埋める

広告APIの構築では、AIが .env をプレースホルダ付きのひな形として作成し、権限を600に設定したうえで git check-ignore -v .env を実行して除外設定が効いていることを確かめます。そのうえで人がそのファイルを直接開いて値を書き込みます。AIはその後、プロセスの内部でだけ値を読み込み、会話には一切出しません。値が入っているかどうかの確認も「空でないか」だけを見て、値そのものは出力しません。

同じ考え方は、SNSアカウントのAPIキー4種を差し替える手順でも使われています。AIが権限600・4行の空欄を持つひな形ファイルを --init で作り、人が開発者ポータルの値をそこに貼り、AIがその値をスクリプトプロパティへ送信します。送信の過程で値はログに出しません。送信後は、自分のアカウント情報を返す認証確認用のAPIが通ることだけを確かめ、画面にはアカウントのハンドル名だけを表示します。ひな形を使い終えたら自動で削除します。

この経路でAIが触れているのは「ひな形を作る」「値を送る」「認証確認の結果を読む」「ひな形を消す」の4つの工程だけで、値そのものを読む工程は一度も存在しません。

経路2: OSのキーチェーンを第一の保管先にする

relmeaの運用規約では、認証情報はまずOSのキーチェーンに格納し、AIは名前で参照するだけで、取り出した値はそのプロセスの中でだけ使います。値をファイルやチャットに複製しない分、残る場所そのものが一つに絞られます。

経路3: 既に動いている配線に読み取り専用の口を足す

SNS2媒体のフォロワー数を確認しようとした際、公開ページからは取得できないという理由で一度は「取得不能」と報告しました。しかし実際には、投稿処理が毎日使っているAPIトークンがスクリプトプロパティに既に存在し、自動更新まで動いていました。このトークンを新しい場所へ複製して持ち出すのではなく、そのプロセス側に読み取り専用のGETを一つ追加しただけで、フォロワー数・日次リーチ・日次ビューが取得できるようになりました。

「公開ページで取れない」ことと「取得の経路が存在しない」ことは別の問題です。既にある配線の中に値が留まったまま機能だけを増やせる場合は、それが最も値の移動が少ない経路になります。

3つの経路に共通しているのは、AIが値を「読む」工程を持たないという点です。値を扱うのは、人が手で入力する瞬間か、あるいは値を最初から知っている既存のプロセスの内部だけです。

秘密のハードコード検査、正しいやり方

先に触れた通り、AI自身の検査行為が事故を起こすことがあります。動画レンダリング基盤の件では、検査対象を *.json のような拡張子ワイルドカードで指定したために、除外設定済みのファイルの中身がそのまま検査結果に出力されました。この事故からの対処は2点です。

対象ディレクトリを明示的に列挙する

src scripts のように対象を具体的に指定し、拡張子だけを頼りにしたワイルドカードでは検査を組まないようにします。検査したいのは「リポジトリに入るコードに秘密が混ざっていないか」であって、意図的に外に出してある認証ファイルの中身ではありません。

中身を開かない3つの確認方法に切り替える

認証ファイルがリポジトリに入っていないかを確かめる際は、次の3つを使います。

  • git ls-files --error-unmatch <path> — そのパスが追跡対象かどうかだけを返す
  • git check-ignore -v <path> — 除外設定が効いているかどうかだけを返す
  • git log --all -- <path> — そのパスの履歴に混入がないかを、内容を開かずに確かめる

いずれも「そのファイルがどう扱われているか」という事実だけを返し、ファイルの中身そのものを出力しません。検査の目的が「秘密が漏れていないかの確認」であるなら、検査の実行自体が漏えいの経路になってはいけない、という条件を、コマンドの指定方法で担保する必要があります。

露出した時の扱いと、鍵の「種類」を台帳にする

対策を組んでいても、値が一度どこかに出てしまうことは起こり得ます。動画レンダリング基盤の件では、露出が起きたことを隠さずに報告し、git側の履歴混入がないことを確認したうえで、鍵のローテーションが必要である、と扱いました。露出した値は「見なかったことにする」対象ではなく、露出した時点でその値は無効化の対象になる、という扱いです。

もう一つ、鍵まわりで見落としやすいのが「種類」の管理です。ある音声生成サービスでは、同じサービスに2種類の鍵(サブスクリプション用と従量API用)が存在し、誤った種類の鍵を使うと、支払いは済んでいるにもかかわらず「残高不足」という応答が返ってきました。原因は鍵の値そのものではなく、「どの種類の鍵をどの用途で使うか」を記録していなかったことです。

認証情報の管理は、値を隠すことだけでなく、値の性質(どのサービスの、どの種類の、どの用途向けの鍵か)を記録しておくことも含みます。値を見ずに扱う設計であっても、種類の取り違えは値を見ただけでは防げないため、台帳としてどこかに残しておく必要があります。なお、鍵の期限切れや認証の失効で自動化が止まる問題は、自動化が止まる原因と対策 で別に扱っています。

AIに任せる部分、人が握る部分

ここまでの3つの経路と検査方法を踏まえると、認証情報の扱いは、AIに任せてよい工程と、人が握るべき工程に自然に分かれます。

人が握る工程(4つ)

  • 値をひな形ファイルに直接貼る(AIには読ませない)
  • 開発者ポータル等で鍵を発行する
  • OAuthの許可画面で同意する
  • 露出が判明した後、ローテーションを実行するかどうかを判断する

AIに任せてよい工程(6つ)

  • 権限600のひな形ファイルを作成する
  • 除外設定が効いているかを git check-ignore -v 等で検証する
  • 値の有無だけを確認する(値そのものは出力しない)
  • ひな形の内容を保管先(スクリプトプロパティ・キーチェーン等)へ送信する
  • 用が済んだひな形ファイルを削除する
  • 秘密のハードコード検査を、対象を明示したうえで実行する

この切り分けの軸は一貫しています。「値そのものを見る・入力する・判断の要る不可逆な操作」は人が担い、「値を見ずに機械的に実行できる工程」はAIが担う、という一本の線です。権限設計が「何をしてよいか」を決めるものだとすれば、この線引きは「誰が値を見るか」を決めるものであり、両方が揃って初めて、AIエージェントに認証情報を安全に扱わせる設計になります。