D1 を編集できる AI エージェントは、隠した EmDash の鍵を自分で作れる
AI エージェントに本番の作業を任せるとき、鍵の実体を見せない方法に注入があります。エージェントの環境にはダミーの文字列だけを置き、外へ出るリクエストでプロキシが本物の鍵に差し替える方式です。ただし、その鍵を作れる権限までエージェントに渡していると、注入で隠しても実体は隠れていません。私の場合、Cloudflare のデータベース D1 を編集できるエージェントは、D1 にデータを置く CMS の EmDash の API トークンを自分で作れました。
私は個人で 8つのサイトを EmDash で運用し、作業を Claude Code に任せています。8 サイトのプライバシーポリシーの文言を直す作業を頼むと、このブログ(shinobiworks.com)以外のサイトでは NOT_AUTHENTICATED の認証エラーが返りました。EmDash の API トークンはサイトごとに別で、エージェントにはこのブログの 1本しか渡していなかったからです。
私が注入をやめてよいとしているのは、2つの条件を両方満たす鍵だけです。その鍵を作れる鍵(発行元の鍵)を既にエージェントに渡していることと、被害を限定できることです。2つを満たす鍵はエージェントが自分で発行して使い捨て、注入で隠すのはそれ以外の鍵にしています。
鍵を作れる権限を渡していれば、その鍵を注入で隠しても実体は隠れない
私が注入で渡していた鍵は 2種類でした。D1 の編集権限を含む Cloudflare のアカウントトークンと、このブログの EmDash の API トークン 1本です。EmDash の CLI を注入で使う方法は、サンドボックスに鍵を置かずに EmDash の CLI を使うに書いています。
EmDash は API トークンを D1 の表に 1 トークン 1行で持っています。この表に 1行入れるだけで、管理画面で発行したのと同じトークンになります。つまり Cloudflare のアカウントトークンは、各サイトの EmDash の API トークンの発行元の鍵です。アカウントトークンを渡した時点でエージェントはサイト用のトークンを作り放題で、このブログのトークンを注入で隠しても実体は隠せていません。
残りのサイトの鍵も 1本ずつ注入で渡す計画でしたが、D1 から発行できるので、この計画はやめました。
注入をやめてよいのは、2つの条件を両方満たす鍵だけ
エージェントに守らせる私の規則では、鍵は注入で渡すのが既定です。例外は次の 2つを両方満たすときだけで、このときはエージェントが自分で鍵を発行し、使い捨てます。
- その鍵を作れる鍵をエージェントが既に持っている
- 被害を限定できる。スコープ(トークンに許す操作の範囲)が狭く、使い終わったら破棄でき、後から取り消せる
各サイトの EmDash の API トークンは、この 2つを満たします。D1 の編集権限から発行でき、スコープを記事の読み書き(content:read と content:write)に絞れ、有効期限も設定できます。使い終わったら D1 の同じ行を消します。消し忘れは管理画面のトークン一覧に出て、そこから取り消せます。
プライバシーポリシーの作業では、このブログ以外のサイトごとにその都度 D1 にトークンを発行し、CLI で直してから消しました。終わったあと、全サイトでトークンの表が空であることを確かめました。
このブログの鍵は記事を CMS に入れるたびに使うので、注入したまま置いておきます。
発行元の鍵を渡していない鍵は、注入を続ける
Cloudflare のアカウントトークン、GitHub の個人用アクセストークン、X に投稿する bot のトークンは、作れる鍵をエージェントが持っていないので、1つ目の条件を満たしません。X の投稿トークンの扱いは、AI エージェントに X の鍵を渡さず投稿させる権限設計に書いています。
これらの鍵は注入をやめると実体をそのままエージェントに渡すことになるので、注入を続けています。