Zurück zu allen Beiträgen
Veröffentlicht am · von Renaud Deraison

Der Build lieferte den Schlüssel aus

Der erweiterte Incident-Report von Beacon CRM führt den Diebstahl der gesamten Kundendatenbank, die mehr als 1.500 britische Wohltätigkeitsorganisationen umfasst, auf einen AWS-Access-Key zurück, den der eigene Build-Prozess in eine öffentliche JavaScript-Datei eingebacken hatte. Niemand ist in die Maschine eines Entwicklers eingebrochen. Ein Build-Werkzeug hat eine Umgebungsvariable in ein Artefakt kopiert, und genau das tun Build-Werkzeuge. Die Antwort von Bromure Agentic Coding lautet: Die Variable enthält nichts, was zu kopieren sich lohnt.

In der ersten Hälfte dieser Geschichte gibt es keinen Angreifer. Ein Build-Prozess kopierte ein Geheimnis aus einer Umgebungsvariablen in eine JavaScript-Datei, und ein Webserver lieferte diese Datei an jeden aus, der danach fragte. Der Diebstahl, als er kam, war ein GET-Request.

Beacon ist ein CRM, das britische Wohltätigkeitsorganisationen nutzen, um Spender, Unterstützer und Freiwillige zu verwalten. Am 12. August veröffentlichte ihr CTO, David Simpson, einen erweiterten Incident-Report zu einem Vorfall, den das Unternehmen erstmals am 4. August offengelegt hatte. The Register berichtete tags darauf, SecurityWeek zog am 14. August nach, und die Grundursache ist eine Zeile:

Ein AWS-Access-Key, möglicherweise exponiert in öffentlichen JavaScript-Build-Artefakten.

Um 01:20:16 UTC am 27. Juli begann jemand, diesen Key zu benutzen, und hielt den Zugriff eine Stunde und siebenundzwanzig Minuten lang. Beacons Einschätzung dessen, was abgeflossen ist:

Eine Kopie der Datenbank, die sämtliche Kundendaten von Beacon enthält, einschließlich Anhangsdateien, wurde erstellt und vom Bedrohungsakteur wahrscheinlich in lesbarem Format heruntergeladen.

Das umfasst mehr als 1.500 Organisationen: Namen von Unterstützern, Telefonnummern, E-Mail-Adressen, Postanschriften, Spendenhistorien und Anhangsdateien. Keine Karten- oder Bankdaten, da Beacons Kunden diese dort nicht speichern. Die ICO prüfte mindestens eine betroffene Organisation und stellte fest, dass diese keinerlei Verantwortung für den Vorfall trägt, was zutrifft und zugleich ein schwacher Trost ist für ein Fundraising-Team, das seinen Unterstützern erklären muss, wohin ihre Privatadressen gegangen sind.

Beacon verschlüsselte die Daten im Ruhezustand. Das änderte nichts. Cybersecurity News weist darauf hin, dass AWS für jeden entschlüsselt, der gültige Credentials besitzt. Verschlüsselung im Ruhezustand schützt Sie davor, dass jemand mit einer Festplatte davonspaziert, und sie hat keine Meinung zu einem Aufrufer mit dem Schlüssel.

Der Teil, den man anstarren sollte

Lesen Sie den Incident-Report auf der Suche nach dem Einbruch, und Sie werden keinen finden. Es gab keine Phishing-Mail, keinen kompromittierten Maintainer, keine vergiftete Abhängigkeit und keine Prompt Injection, und keine Malware auf dem Laptop eines Ingenieurs. Nichts in dieser Geschichte hat eine Kontrolle umgangen, denn niemand hat das Credential irgendwo herausgeholt. Beacons Build-Prozess kopierte einen Wert aus einer Umgebungsvariablen in ein Bundle, Beacon deployte das Bundle als statisches Asset, und ein Webserver reichte es an jeden Client weiter, der es anforderte, wie vorgesehen. Der Exfiltrationskanal war ein <script>-Tag.

Der Mechanismus ist nicht exotisch. Frontend-Builds inlinen Umgebungsvariablen mit Absicht, weil der Browser zur Laufzeit keine Umgebung zum Auslesen hat:

  • Vite ersetzt jede mit VITE_ präfixierte Variable zur Build-Zeit in import.meta.env. Next.js tut dasselbe für NEXT_PUBLIC_, und Create React App verwendete REACT_APP_. Das Präfix ist die Art, wie Sie die Ersetzung anfordern.
  • webpacks DefinePlugin und esbuilds --define ersetzen ein Token in Ihrem Quelltext durch eine Zeichenkette. Sie haben keinen Begriff davon, welche Zeichenketten geheim sind, und keine Möglichkeit, einen zu erwerben.
  • Eine Source Map ist eine zweite Kopie. Serverseitig rendernde Frameworks fügen einen dritten Weg hinzu: Ein im Code gelesener Wert, der in einer Client-Komponente landet, wird in die Payload serialisiert.

Jede dieser Kopien stammt aus der Umgebung dessen, wer oder was den Build gestartet hat. Die Umgebung ist der Rohstoff, und der Build ist eine Maschine, die ihn in eine Datei kopiert, die Sie anschließend veröffentlichen.

Der Weg eines Geheimnisses ins öffentliche WebBuild-UmgebungAWS_SECRET_…=realBundler inlined ihnTextersetzungDeploymentapp.[hash].jsöffentlich serviert200 OK, cachebarjeder liestview-sourceNichts davon ist ein Exploit. Darunter steht der einzige Schritt mit einem Angreifer.gültige Credentials, gültige API-Calls27. Juli, 01:20:16 UTC · 1 h 27 minganze Datenbank + Anhänge1.500+ Organisationen, lesbarVerschlüsselung at rest: ohne WirkungAWS entschlüsselt für gültige AufruferGrundursache, Zeiten und Umfang aus Beacons Incident-Report vom 2026-08-12, via The Register und SecurityWeek.
Der Weg des Credentials ins öffentliche Web bei Beacon. Nichts auf diesem Pfad ist eine Schwachstelle, ein Einbruch oder eine umgangene Kontrolle. Jeder Schritt ist ein Werkzeug, das tut, wofür es existiert.

Die Workstation, auf der das beginnt

Beacons Vorfall ist in seinen Folgen eine Cloud-Geschichte. Seine Ursache sitzt auf einer Entwickler-Workstation, und die Workstation verändert sich.

Ein Agent, der ein Ticket abarbeitet, erledigt all das an einem gewöhnlichen Nachmittag. Er fügt eine Umgebungsvariable hinzu, damit ein Feature einen Dienst erreichen kann. Er bearbeitet vite.config.ts oder next.config.js. Er schreibt die Zeile, die die Variable liest, und er wählt das Modul, in dem diese Zeile lebt, was die Entscheidung ist, die festlegt, ob der Wert auf dem Server bleibt oder ins Bundle übergeht. Er führt npm run build aus. Er committet dist/, wenn ein Deploy-Ziel das Artefakt eingecheckt haben will.

Nichts davon verlangt, dass der Agent einen Fehler macht, und Sie bekommen kein Signal, wenn er einen macht. Der Build gelingt, denn eine Zeichenkette ist eine Zeichenkette. Das Bundle geht als minifizierte Einzelzeile raus, also fällt Ihnen im Diff nichts ins Auge. Der Wert funktioniert, also funktioniert das Feature und das Ticket schließt sich.

Unterdessen ist der Workspace, in dem ein Agent läuft, mit Credentials bestückt, denn Credentials sind es, die ihn nützlich machen: Cloud-Keys, damit er einen Bucket prüfen kann, ein GitHub-Token, damit er einen Branch pushen kann, Registry- und Datenbank-Credentials, ein Modell-API-Key. Jedes davon ist eine Zeichenkette, die in einer Umgebung liegt, die ein Build-Prozess vollständig ausliest.

Beacons Report stellt dieser Anordnung eine direkte Frage. Irgendetwas in Ihrem Workspace kopiert Ihre Umgebung früher oder später in eine Datei. Fragen Sie sich, was es dabei bekommt.

In einem Bromure-Workspace bekommt es einen Region-String

Bromure Agentic Coding lässt den Agenten in einer verwerfbaren Ubuntu-VM auf Apples Virtualization-Framework laufen, mit einem hostseitigen MITM-Proxy als einziger Route ins Netz. Das Credential-Design folgt daraus: Echte Geheimnisse bleiben auf Ihrem Mac, und die VM bekommt Werte, die richtig aussehen und nichts wert sind.

AWS bekommt eine eigene Behandlung, denn SigV4 sendet das Geheimnis nie. Das SDK verbraucht das Geheimnis im Client, um ein HMAC zu berechnen, und legt die Signatur auf die Leitung, sodass Bromure nicht wie bei einem Bearer-Token einen Fake unterwegs gegen einen echten Wert tauschen kann. Stattdessen verlagert es das Signieren.

Beginnen Sie damit, was ein Build-Prozess vorfinden würde. Bromure exportiert AWS_DEFAULT_REGION und AWS_REGION in die VM und keinerlei Key, Secret oder Session-Token. Die Begründung steht im Quelltext, in SessionDisk.swift:

Wir dürfen AWS_ACCESS_KEY_ID / AWS_SECRET_ACCESS_KEY / AWS_SESSION_TOKEN hier NICHT exportieren: Umgebungsvariablen schlagen credential_process in der Kette des SDK, und sie würden außerdem die Zusicherung „kein Geheimnis auf der Platte“ aushebeln (Umgebungsvariablen lecken über /proc, ps -E und die Shell-History).

Diese Liste von Leck-Kanälen beschreibt Prozessinspektion. Ein Bundler, der process.env liest, gehört auf sie, und dieselbe Entscheidung deckt ihn ab. Es existiert kein AWS_SECRET_ACCESS_KEY in der Umgebung, das ein Build inlinen könnte, also ersetzt DefinePlugin nichts, import.meta.env trägt nichts, und NEXT_PUBLIC_AWS_SECRET_ACCESS_KEY, Beacons Fehler mit Absicht begangen, veröffentlicht eine leere Zeichenkette.

Das SDK funktioniert trotzdem. ~/.aws/config zeigt auf einen credential_process-Helfer:

[default]
credential_process = /mnt/bromure-meta/bromure-aws-creds.py
region = eu-west-2

Der Helfer liest ein JSON-Dokument von einem Socket. Er gibt die echte AccessKeyId zurück, die Sie identifiziert statt Sie zu authentifizieren, gepaart mit einem SecretAccessKey, den Bromure für die Sitzung erzeugt: vierzig Zeichen aus dem Alphabet, das ein echtes AWS-Secret verwendet, sodass boto3, die aws-CLI und Terraform ihn akzeptieren und wie gewohnt signieren. Die Signatur, die sie erzeugen, ist zum Scheitern bestimmt.

Dann repariert der Host sie. AWSResigner erkennt jede *.amazonaws.com-Anfrage, die durch den Proxy kommt, entfernt den Authorization-Header des Gasts und berechnet SigV4 neu, mit Credentials, die nur im Adressraum des Host-Prozesses existieren, und fügt das echte X-Amz-Security-Token hinzu, wenn das Profil STS-Material trägt. Ihr terraform apply funktioniert, und das Geheimnis hat die Maschine, die es ausgeführt hat, nie betreten.

Gehen Sie am Proxy vorbei, und AWS antwortet:

An error occurred (InvalidSignatureException) when calling the
ListBuckets operation: The request signature we calculated does not
match the signature you provided.

Das Credential fällt geschlossen aus. Eines, das nur funktioniert, wenn es über Ihren Mac geroutet wird, lässt sich vom Laptop eines Fremden aus nicht ausüben, und genau diese Eigenschaft fehlte Beacons veröffentlichtem Key.

KONVENTIONELL: das Geheimnis ist im Raum mit dem BuildEntwicklermaschineAWS_SECRET_ACCESS_KEY = realSigV4 hier signiert · Bundler liest hierAWS akzeptiertvon überalljede Kopie der Umgebungist ein gültiges CredentialBROMURE: das Geheimnis ist nie in diesem RaumVM: Agent, Build, Bundlerkein Key, Secret, Tokensigniert mit 40-Zeichen-FakeHost-Proxy: AWSResignerentfernt Authorizationsigniert mit echtem Secret neuAWS akzeptiertnur über Ihren MacProxy umgangen:InvalidSignatureException
Bromure berechnet die Signatur woanders. Ein Geheimnis in die Maschine zu schicken, die nicht vertrauenswürdigen Code ausführt, ist das, was eine Umgebungsvariable des Diebstahls wert macht; außerhalb dieser Maschine zu signieren nimmt den Grund, überhaupt hinzusehen.

Der Rest der Tafel besteht aus Attrappen

AWS ist der Sonderfall. Für den Rest erledigt die Leitungsgrenze die Arbeit: Jedes Credential, das Sie konfigurieren, erscheint in der VM als strukturerhaltender Fake, abgeleitet aus dem echten Wert plus einem 32-Byte-Salt pro Installation über HKDF-SHA256. Der echte Wert bleibt verschlüsselt auf Ihrem Mac, und der Proxy setzt ihn erst in die Anfrage ein, nachdem die Bytes die VM verlassen haben, und nur dann, wenn die Anfrage an den Host geht, für den dieses Credential geprägt wurde.

Das löst die ganze Klasse „ein Werkzeug hat die Umgebung irgendwohin kopiert, wo sie nicht hingehört“ auf dieselbe Weise, ganz gleich, welches Werkzeug es war:

Modell- und Anbieter-API-Keys

ANTHROPIC_API_KEY ist sk-ant-api03-brm-…. OPENAI_API_KEY ist sk-brm-…, XAI_API_KEY ist xai-brm-…. Strukturerhaltend, sodass die CLIs sie klaglos akzeptieren, und überall sonst wirkungslos.

Git-, Registry- und Cloud-Tokens

GH_TOKEN ist ghp_ plus 36 Zeichen, GitLab ist glpat- plus 20, DigitalOcean ist dop_v1_ plus Hex. ~/.git-credentials, ~/.docker/config.json, ~/.kube/config und ~/.config/doctl/config.yaml sind alle vorhanden, alle befüllt, alle gefälscht.

Ein NEXT_PUBLIC_-Präfix

Setzen Sie eines davor, und der Bundler tut, was Sie ihm gesagt haben. Das veröffentlichte Artefakt trägt Füllmaterial in der Form brm-docker-… oder ghp_, und der Fehler kostet Sie ein Redeployment statt einer Meldepflicht.

Ein eingechecktes dist/ oder eine Source Map

Dieselbe Antwort, und sie hängt nicht davon ab, dass es jemand bemerkt. Das Artefakt kann in einem öffentlichen Repository liegen, indexiert und von jedem laufenden Secret-Crawler abgegrast werden, und die Zeichenkette, die sie einsammeln, authentifiziert gegen nichts.

Bromure leitet die Fakes jedes Mal gleich ab, sodass ein Werkzeug, das seinen eigenen Key fingerabdruckt, wie Claude Code es beim Cachen eines Key-Hashes tut, das Credential zwischen Sitzungen nie wechseln sieht. Es gibt auch keinen Schalter, den man vergessen könnte. Der Proxy ist die einzige Route der VM nach draußen, also trägt eine Anfrage, die an ihm vorbeigeht, einen Platzhalter und scheitert stromaufwärts.

Die veröffentlichte Attrappe ist ein Stolperdraht

Beacons Key lag vor dem 27. Juli für einen unbekannten Zeitraum in einer öffentlichen Datei, und das erste Signal, das überhaupt jemand bekam, war ein Ausschlag in den AWS-Cost-and-Usage-Reports für den 27. und 28. Juli, im Nachhinein gelesen.

Bromures Kompromittierungsdetektor überwacht Ihre Credentials statt der Reputation des Ziels, und genau das lässt ihn bei einem Host auslösen, den nie jemand benannt hat. Ein Aho-Corasick-Automat, gebaut aus den vom Workspace selbst geprägten Fakes, durchkämmt jede ausgehende Anfrage, Header und Body. Ein Fake auf dem Weg zu einem Host außerhalb des Bereichs, für den er geprägt wurde, gilt als versuchte Exfiltration, und Bromure reagiert:

  1. Der Proxy weist die Anfrage mit HTTP 451 ab, und kein einziges Byte erreicht das Ziel.
  2. Bromure pausiert die VM auf der Stelle.
  3. Bromure löst einen Alarm aus, der das Credential nennt, den Host, für den es geprägt wurde, und den Host, zu dem es beobachtet wurde.

Bromure markiert den Workspace anschließend als kompromittiert, sodass der nächste Start das Löschen des VM-Disk-Images und des persistenten Home erfordert. Ihre Tokens, SSH-Keys und Einstellungen überleben das.

Richten Sie den Mechanismus auf das Beacon-Szenario. Ein für ein Ziel geprägter Fake, der in einer Anfrage an ein anderes auftaucht, ist die Signatur, und es spielt keine Rolle, wie der Fake herauskam: ein geleaktes Bundle, ein gestohlenes Dotfile, ein neugieriger Fremder mit einem Browser. Die erste Person, die das gefundene Credential ausprobiert, kündigt sich selbst an, in einem Log, das Ihnen gehört, in dem Moment, in dem sie es probiert.

Der Nachweis, den Beacon nach eigener Aussage nie haben wird

Simpson schließt den Incident-Report mit den Grenzen dessen, was Beacon belegen kann:

Es gibt Dinge, die wir über diesen Vorfall vielleicht nie herausfinden werden.

Beacon benennt die Grenze unumwunden. Die konkreten Objekte, das genaue Ziel der Downloads und die endgültige Zuordnung, auf welche Objekte zugegriffen wurde, lassen sich aus den verfügbaren Logs nicht bestimmen. Beacons Schluss, dass die gesamte Datenbank abgeflossen ist, ist eine Ableitung aus der Form eines Abrechnungsberichts: ein Transfervolumen in den Cost-and-Usage-Daten, das der ungefähren Größe dessen entspricht, was Beacon speichert. Das ist gute forensische Arbeit ohne jede Grundlage, und jede Organisation, die jetzt an ihre Unterstützer schreibt, verlässt sich darauf.

Ein Bromure-Workspace erzeugt diesen Nachweis als Nebenprodukt seiner Funktionsweise. Jede Anfrage überquert den Host-Proxy, also schreibt der Proxy auf, was geschehen ist:

$ 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 listet jeden einzelnen Host auf, den eine Sitzung kontaktiert hat, trace summary aggregiert das Ganze, und trace leaks zeigt die unverwalteten Credentials. Der Trace Inspector (⇧⌘I) liefert dieselbe Sicht mit Request-Bodies, und das Sicherheitslog (Window → Supply Chain Log…) verfolgt Lieferketten- und 451-Entscheidungen, während sie geschehen. Bromure hält alles davon verschlüsselt im Ruhezustand auf Ihrem Mac unter dem Master-Key des Tresors.

Die Fragen, die Beacon nur durch Ableitung beantworten kann, sind ein Nachschlagen, das Sie selbst ausführen: Womit hat dieser Workspace gesprochen, wann, was tragend, und ist irgendetwas Credential-Förmiges abgeflossen. Gegen Ihre eigenen Daten, in der Minute, in der Sie es sich zum ersten Mal fragen.

Die Anordnung, die die Lücke schließt

Beacons Ingenieure haben nichts Ungewöhnliches getan. Ein Credential in eine Umgebungsvariable zu legen, ist die empfohlene Praxis, und Umgebungsvariablen während eines Builds zu lesen, ist das, was Builds tun. Eine ganze Kundendatenbank ging durch die Lücke zwischen diesen beiden vernünftigen Dingen, und kein Maß an Sorgfalt schließt sie, denn Sorgfalt ist eine Hoffnung auf Aufmerksamkeit statt einer Kontrolle.

Die Kontrolle besteht darin, die Dinge so anzuordnen, dass die Kopie wertlos ist. Behalten Sie das echte Credential auf dem Host, geben Sie dem Workspace einen Platzhalter, der jedes Werkzeug zufriedenstellt, das ihn liest, signieren und ersetzen Sie außerhalb der Maschine, die den Code ausführt, und führen Sie Ihren eigenen Nachweis über jede Anfrage, die hinausgeht. Dann kommen ein Build-Tool, das seine Arbeit macht, ein Agent, der eine vernünftig aussehende Änderung vornimmt, und ein Fremder, der Ihr Bundle in aller Ruhe liest, alle am selben Ort an: bei einer Zeichenkette, die außerhalb Ihres Macs nichts bedeutet.


Quellen: The Register, „AWS key exposed in JavaScript may have lit way to Beacon's charity data“ (13. August 2026) · SecurityWeek, „Over 1,000 Charities Hit by Beacon CRM Data Breach“ (14. August 2026) · 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“