ビルドが鍵を出荷した
Beacon CRM の拡充版インシデント報告は、1,500 を超える英国のチャリティ団体を含む顧客データベース全体の窃取を、自社のビルド工程が公開 JavaScript ファイルに焼き込んだ AWS アクセスキーへとたどっている。誰も開発者のマシンに侵入していない。ビルドツールが環境変数をアーティファクトへコピーしただけであり、それはビルドツールの仕事そのものだ。Bromure Agentic Coding の答えは、その変数にはコピーする価値のあるものが何も入っていない、というものである。
この物語の前半に攻撃者はいない。ビルド工程が環境変数から秘密を JavaScript ファイルへコピーし、ウェブサーバがそのファイルを求める者すべてに配信した。 窃取は、それが起きたとき、GET リクエスト一本だった。
Beacon は、英国のチャリティ団体が寄付者・支援者・ボランティアを管理するために使 う CRM である。8月12日、CTO の David Simpson が、同社が8月4日に最初に開示した侵 害について拡充版のインシデント報告を公開した。The Register は 翌日にこれを報じ、 SecurityWeek が 8月14日に続いた。 そして根本原因は一行で済む。
公開された JavaScript ビルドアーティファクトの中で露出した可能性のある AWS アクセスキー。
7月27日 01:20:16 UTC、何者かがそのキーを使い始め、1時間27分にわたってアクセスを 保持した。何が出て行ったかについての Beacon の評価はこうだ。
Beacon の全顧客データを保持するデータベースのコピーが、添付ファイルを含めて 作成され、脅威アクターによって可読な形式でダウンロードされた可能性が高い。
対象は 1,500 を超える団体に及ぶ。支援者の氏名、電話番号、メールアドレス、住所、 寄付記録、そして添付ファイル。カードや銀行の情報は含まれない。Beacon の顧客はそ れらをここに保存していないからだ。 ICO は被害団体を少なくとも一つ精査し、 その団体にこの侵害の責任は一切ないと結論した。これは正しく、そして支援者に自宅住 所の行方を説明しなければならないファンドレイジングのチームにとっては、冷たい慰め でもある。
Beacon はデータを保存時に暗号化していた。それは何も変えなかった。 Cybersecurity News が指摘している とおり、AWS は有効な資格情報を持つ者のために復号する。保存時暗号化はディスクを持 ち去られることからは守ってくれるが、鍵を携えた呼び出し元については何の意見も持た ない。
じっと見つめるべき箇所
侵入を探しながらこのインシデント報告を読んでも、侵入は見つからない。フィッシング
メールもなく、侵害されたメンテナもなく、汚染された依存関係もプロンプトインジェク
ションもなく、エンジニアのノート PC 上のマルウェアもない。この物語の中で制御を回
避したものは何ひとつない。誰も資格情報をどこからも抜き出していないからだ。Beacon
のビルド工程が環境変数から値をバンドルへコピーし、Beacon がそのバンドルを静的アセ
ットとしてデプロイし、ウェブサーバが要求してきたすべてのクライアントへ、設計どお
りにそれを手渡した。持ち出しの経路は <script> タグだった。
この仕組みに奇抜なところはない。フロントエンドのビルドは意図的に環境変数をインラ イン展開する。ブラウザには実行時に読み取れる環境が存在しないからだ。
- Vite は
VITE_を接頭辞に持つ変数をビルド時にimport.meta.envへ置換する。 Next.js はNEXT_PUBLIC_について同じことをし、Create React App はREACT_APP_を使っていた。接頭辞とは、その置換を頼むための作法である。 - webpack の
DefinePluginと esbuild の--defineは、ソース中のトークンを文 字列に置き換える。どの文字列が秘密なのかという概念を持たず、それを獲得する手段 も持たない。 - ソースマップは二つ目のコピーである。サーバレンダリングのフレームワークは三つ目 の経路を足す。コード中で読まれた値がクライアントコンポーネントに行き着けば、そ れはペイロードへシリアライズされる。
これらのコピーはいずれも、そのビルドを起動した者、あるいは何かの環境から取られ る。環境は原材料であり、ビルドはそれをあなたが後で公開するファイルへ写し取る機械 である。
これが始まるワークステーション
Beacon の侵害は、その帰結においてはクラウドの物語である。原因は開発者のワークステ ーションに座っており、そのワークステーションは今、変わりつつある。
チケットを片付けるエージェントは、普通の午後にこれをすべてやる。機能があるサービス
に到達できるよう環境変数を足す。vite.config.ts や next.config.js を編集する。
その変数を読む行を書き、その行がどのモジュールに置かれるかを選ぶ。この選択こそ、値
がサーバに留まるのかバンドルへ渡るのかを決める判断である。npm run build を実行す
る。デプロイ先がアーティファクトのコミットを求めれば dist/ をコミットする。
そのどれも、エージェントが誤りを犯すことを必要としないし、犯したところで合図は届か ない。ビルドは成功する。文字列は文字列でしかないからだ。バンドルは圧縮された一行と して出荷されるので、差分の中で目を引くものは何もない。値は機能するので、機能は動 き、チケットは閉じる。
その一方で、エージェントが走るワークスペースには資格情報がぎっしり詰まっている。資 格情報こそがそれを有用にするからだ。バケットを確認するためのクラウドキー、ブランチ を push するための GitHub トークン、レジストリとデータベースの資格情報、モデルの API キー。そのいずれもが、ビルド工程が丸ごと読み取る環境の中に置かれた文字列であ る。
Beacon の報告は、この配置に率直な問いを突きつける。あなたのワークスペースの中の何か は、遅かれ早かれあなたの環境をファイルへ写し取る。それが何を手に入れるのかを、自分 に問うてほしい。
Bromure のワークスペースでは、手に入るのはリージョン文字列だけだ
Bromure Agentic Coding は、Apple の Virtualization フレームワーク上の使い捨て Ubuntu VM でエージェントを走らせ、ホスト側の MITM プロキシだけをネットワークへの唯 一の経路とする。 資格情報の設計 はそこから導かれる。 本物の秘密はあなたの Mac に留まり、VM には正しく見えて何の価値もない値が渡される。
AWS だけは扱いが別になる。SigV4 は秘密そのものを送らないからだ。SDK はクライアント 側で秘密を消費して HMAC を計算し、線上に載せるのは署名である。だから Bromure は、 ベアラトークンに対して行うように、転送中に偽物と本物を差し替えることができない。代 わりに、署名する場所を動かす。
まず、ビルド工程が見つけるであろうものから始めよう。Bromure が VM へエクスポートす
るのは AWS_DEFAULT_REGION と AWS_REGION であり、キーもシークレットもセッション
トークンも一切ない。その理由はソース中、SessionDisk.swift に書かれている。
ここで
AWS_ACCESS_KEY_ID/AWS_SECRET_ACCESS_KEY/AWS_SESSION_TOKENを エクスポートしては絶対にならない。環境変数は SDK のチェーンでcredential_processに優先し、さらにディスク上に秘密を置かないという保証をも 損なう(環境変数は/proc、ps -E、シェル履歴から漏れる)。
この漏洩経路の一覧は、プロセスの覗き見を描いたものだ。process.env を読むバンドラ
もそこに属し、同じ判断がそれを覆う。ビルドがインライン展開できる
AWS_SECRET_ACCESS_KEY は環境に存在しないので、DefinePlugin は何も置換せず、
import.meta.env は何も運ばず、Beacon の過ちを意図的に犯した
NEXT_PUBLIC_AWS_SECRET_ACCESS_KEY は空文字列を公開する。
それでも SDK は動く。~/.aws/config は credential_process ヘルパーを指してい
る。
[default]
credential_process = /mnt/bromure-meta/bromure-aws-creds.py
region = eu-west-2
ヘルパーはソケットから JSON ドキュメントを一つ読む。返すのは本物の AccessKeyId
で、これはあなたを認証するのではなく識別するものであり、これに Bromure がセッション
ごとに生成する SecretAccessKey が組み合わされる。本物の AWS シークレットが使う文
字集合から取った四十文字なので、boto3、aws CLI、Terraform はこれを受け入れ、い
つもどおり署名する。彼らが作る署名は、必ず失敗するようにできている。
そこでホストがそれを直す。AWSResigner はプロキシを通る *.amazonaws.com 宛のリ
クエストをすべて見分け、ゲストの Authorization ヘッダを剥がし、ホストプロセスのア
ドレス空間にしか存在しない資格情報で SigV4 を計算し直す。プロファイルが STS の材料
を持つ場合には本物の X-Amz-Security-Token を添える。あなたの terraform apply
は動き、そして秘密はそれを走らせたマシンに一度も入っていない。
プロキシを迂回すれば、AWS はこう答える。
An error occurred (InvalidSignatureException) when calling the
ListBuckets operation: The request signature we calculated does not
match the signature you provided.
資格情報は閉じる方向に倒れる。あなたの Mac を経由したときにしか機能しないものは、見 知らぬ他人のノート PC から行使できない。それこそ、Beacon の公開された鍵に欠けていた 性質である。
盤上の残りはすべて囮である
AWS は特別な場合だ。それ以外については、線上の境界が仕事をする。あなたが設定した資 格情報はどれも、VM の中では構造を保った偽物として現れる。本物の値とインストールごと の 32 バイトのソルトから HKDF-SHA256 で導出されたものだ。本物の値は暗号化されたまま あなたの Mac に留まり、プロキシはバイト列が VM を出たあとで、しかもその資格情報が発 行された相手のホスト宛のリクエストに限って、それを差し込む。
これによって「ツールが環境を行ってはいけない場所へコピーした」という一群の問題は、 そのツールが何であれ、同じやり方で片づく。
モデルとベンダーの API キー
ANTHROPIC_API_KEY は sk-ant-api03-brm-… になる。OPENAI_API_KEY は
sk-brm-…、XAI_API_KEY は xai-brm-…。構造が保たれているので CLI は文句
を言わずに受け入れ、それ以外の場所では不活性である。
Git・レジストリ・クラウドのトークン
GH_TOKEN は ghp_ に 36 文字、GitLab は glpat- に 20 文字、DigitalOcean
は dop_v1_ に十六進。~/.git-credentials、~/.docker/config.json、
~/.kube/config、~/.config/doctl/config.yaml はすべて存在し、すべて中身が
入っており、すべて偽物である。
NEXT_PUBLIC_ という接頭辞
どれに付けても、バンドラはあなたが命じたとおりに振る舞う。公開されたアーティ
ファクトが運ぶのは brm-docker-… や ghp_ の形をした詰め物であり、その誤り
が招くのは開示通知ではなく再デプロイである。
コミットされた dist/ やソースマップ
答えは同じで、しかも誰かが気づくかどうかに左右されない。アーティファクトが公 開リポジトリに置かれ、索引され、稼働中のあらゆるシークレットクローラに収集さ れても、彼らが集める文字列はどこにも認証されない。
Bromure は偽物を毎回同じやり方で導出するので、Claude Code がキーのハッシュをキャッ シュするときのように自分のキーの指紋を取るツールも、セッション間で資格情報が変わる のを目にすることはない。忘れる余地のあるスイッチも存在しない。プロキシが VM の唯一 の出口なので、それを迂回するリクエストはプレースホルダを携え、その先で失敗する。
公開された囮は仕掛け線である
Beacon の鍵は7月27日より前、どれだけの期間かも分からないまま公開ファイルの中に置か れていた。そして誰かが得た最初の合図は、事後に読まれた7月27日と28日の AWS Cost and Usage レポート の急増だった。
Bromure の侵害検知は、宛先の評判ではなく あなたの 資格情報を見張る。だからこ そ、誰も名前を挙げたことのないホストに対しても発火できる。ワークスペース自身が発行 した偽物から構築した Aho-Corasick オートマトンが、ヘッダも本文も含め、外向きのリク エストをすべて掃く。ある偽物が、発行されたときの範囲の外にあるホストへ向かっていれ ば、それは持ち出しの試みと数えられ、Bromure はこう応じる。
- プロキシが HTTP 451 でリクエストを拒み、1 バイトも 宛先に届かない。
- Bromure がその場で VM を一時停止する。
- Bromure が、その資格情報と、それが発行された相手のホストと、向かおうとしていた ホストを名指しした警告を上げる。
続いて Bromure はワークスペースを侵害済みと印付けるので、次回の起動には VM のディス クイメージと永続ホームの消去が必要になる。あなたのトークン、SSH 鍵、設定はそれを生 き延びる。
この仕組みを Beacon の筋書きに向けてみよう。ある宛先のために発行された偽物が別の宛 先へのリクエストに現れる、それが署名であり、その偽物がどうやって外へ出たのかは問題 にならない。漏れたバンドルでも、盗まれたドットファイルでも、ブラウザを持った物見高 い他人でもよい。拾った資格情報を最初に試した者は、それを試したその瞬間に、あなたの 所有するログの中へ自ら名乗り出る。
Beacon が決して持てないと言う記録
Simpson は、Beacon に確定できることの限界をもってインシデント報告を締めくくってい る。
このインシデントについては、我々が決して知り得ないかもしれないことがある。
Beacon はその限界を率直に述べている。どのオブジェクトだったのか、ダウンロードの正確 な宛先はどこか、どのオブジェクトがアクセスされたのかの確定的な帰属は、 入手可能なログからは判定できない。データベース全体が出て行ったという Beacon の 結論は、請求レポートの形からの推論である。Cost and Usage のデータに現れた転送量が、 Beacon の保管しているものの概算サイズと一致するという推論だ。これは手掛かりが何もな い中での立派なフォレンジック作業であり、いま支援者へ手紙を書いているすべての団体は、 それに頼っている。
Bromure のワークスペースは、その記録を動作の副産物として生み出す。すべてのリクエス トはホストのプロキシを渡るので、プロキシが何が起きたかを書き留める。
$ bromure-cli trace ls
HOST METHOD STATUS MS FLAGS
api.anthropic.com POST 200 412 swap×1
s3.eu-west-2.amazonaws.com GET 200 88
registry.npmjs.org GET 200 31
api.github.com POST 201 140 swap×1
trace hostnames はそのセッションが接触した個別のホストを列挙し、trace summary
は全体を集計し、trace leaks は管理外の資格情報を示す。Trace Inspector(⇧⌘I)は
リクエストの本文まで含めて同じ眺めを与え、セキュリティログ
(Window → Supply Chain Log…)はサプライチェーンと 451 の判断を起きるそばから
追う。Bromure はそのすべてを、あなたの Mac 上で金庫のマスターキーの下、保存時に暗号
化して保持する。
Beacon が推論でしか答えられない問いは、あなた自身が実行する一回の検索になる。このワ ークスペースは何と話したか、いつか、何を携えていたか、資格情報らしきものが出て行か なかったか。自分自身のデータに対して、疑問を抱いたその一分のうちに。
隙間を塞ぐ配置
Beacon のエンジニアは何ひとつ変わったことをしていない。資格情報を環境変数に置くのは 推奨される作法であり、ビルド中に環境変数を読むのはビルドの仕事である。この二つの理 にかなった事柄のあいだの隙間を、顧客データベースがまるごと通り抜けた。そしてどれだ け注意を払っても、その隙間は塞がらない。注意とは、制御ではなく、集中力への希望だか らだ。
制御とは、コピーが無価値になるように物事を配置することである。本物の資格情報はホス トに留め、それを読むあらゆるツールを満足させるプレースホルダをワークスペースへ渡 し、コードを走らせるマシンの外で署名と差し替えを行い、出て行くすべてのリクエストの 記録を自分の手元に持つ。そうすれば、仕事をこなすビルドツールも、もっともらしい編集 をするエージェントも、あなたのバンドルをゆっくり読む見知らぬ他人も、みな同じ場所に たどり着く。あなたの Mac の外では何の意味も持たない、一本の文字列に。
出典:The Register「AWS key exposed in JavaScript may have lit way to Beacon's charity data」(2026年8月13日) · SecurityWeek「Over 1,000 Charities Hit by Beacon CRM Data Breach」(2026年8月14日) · Infosecurity Magazine「Exposed AWS Access Key Linked to Data Breach Affecting 1500+ UK Charities」 · Cybersecurity News「Beacon CRM Confirms Full Database Theft After AWS Access Key Breach」