Sicherheitsgrundlagen
Dein OpenAI-API-Schlüssel liegt im Frontend. Tausch ihn aus.
Ein OpenAI-API-Schlüssel im Frontend lässt sich an keine Domain binden. Tausch ihn heute, leg den Aufruf hinter deinen Server und begrenze das Budget.

Kurz gesagt
- Ein OpenAI-API-Schlüssel im Frontend ist der Fall, in dem der Alarm recht hat. Keine Einstellung macht so einen Schlüssel im Browser unbedenklich.
- Er ist ein Bearer-Token, es genügt also, ihn zu besitzen. Es gibt keine Domain-Einschränkung, die ihn an deine eigene Website bindet, so wie es sie für einen Google-Schlüssel gibt.
- Tausch ihn heute im OpenAI-Dashboard aus, verleg den Aufruf hinter einen eigenen Endpunkt und setz ein Ausgabenlimit für das Projekt.
- Wir fanden einen in 33 von 30.998 gescannten Apps. Selten, und alle 33 kamen mit Note D oder F heraus.
Öffne deine App in einem Browser, sieh dir den Seitenquelltext an und such darin
nach sk-proj-. Kommt eine lange Zeichenkette zurück, liegt dein
OpenAI-API-Schlüssel offen im Frontend, und jeder Besucher, den du je hattest,
hätte ihn kopieren können.
Hier ist der Teil, den die meisten Ratschläge dazu falsch machen: ein OpenAI-Schlüssel ist kein Google-Schlüssel mit anderem Präfix, und nichts, was du in einem Dashboard einstellen kannst, macht ihn dort unbedenklich. Ein Google-Maps-Schlüssel gehört in deine Seite, und eine kostenlose Einstellung schützt ihn. Ein OpenAI-Schlüssel hat nirgends eine vergleichbare Einstellung, und deshalb lautet die einzige ehrliche erste Anweisung, ihn auszutauschen.
Zwischen dem 12. und 14. August 2026 haben wir neun externe Prüfungen über 30.998 live erreichbare Apps laufen lassen, gebaut mit Lovable, Bolt, v0, Replit und Base44. In 33 davon tauchte ein OpenAI-Schlüssel auf. Alle 33 kamen mit Note D oder F zurück, weil ein einziger kritischer Fund die Note deckelt, egal was die App sonst richtig gemacht hat. Die vollständigen Zahlen stehen in unserem Scan-Bericht.
Ist ein OpenAI-API-Schlüssel im Frontend ein Problem?
Ja. Das ist der Fall, in dem der Alarm recht hat.
Ein OpenAI-Schlüssel ist ein Bearer-Token, und das Wort trägt die ganze
Erklärung: wer ihn trägt, darf ihn benutzen. Er reist in einem Header, der
Authorization: Bearer sk-proj-… lautet, und die Server von OpenAI fragen nach
nichts weiter. Nicht, welche Website ihn geschickt hat. Nicht, aus welchem Land er
kam oder ob der Absender du bist.
Denk an eine Fahrkarte statt an einen Reisepass. Ein Schaffner prüft nicht, wessen Name auf einer Fahrkarte steht, denn sie zu besitzen ist die ganze Berechtigung. Das macht eine Fahrkarte diebstahlswert und einen Reisepass meistens nicht.
Ein in deine App gedruckter Schlüssel ist also eine Fahrkarte, die du jedem Besucher in die Hand gedrückt hast. Die meisten werden nie hinsehen. Die, die hinsehen, sind meist überhaupt keine Menschen: automatische Scraper durchsuchen öffentliche Seiten nach schlüsselförmigen Zeichenketten, und sie müssen nicht wissen, wer du bist, um deine zu finden.
Warum die Umgebungsvariable ihn nicht versteckt hat
Weil ein Frontend-Build Umgebungsvariablen in die Datei kompiliert, die er ausliefert.
Das ist der Schritt, der die Leute sicher sein lässt, der Schlüssel sei
geschützt. Du hast ihn aus deinem Code in eine .env-Datei geholt, ihn
VITE_OPENAI_API_KEY oder NEXT_PUBLIC_OPENAI_API_KEY genannt, und jetzt nennt
der Code eine Variable, wo vorher der Schlüssel stand. An der ausgelieferten App
hat sich nichts geändert. Das Build-Werkzeug hat diese Variable auf dem Weg nach
draußen durch ihren Wert ersetzt, und der Wert liegt in dem JavaScript, das dein
Besucher herunterlädt.
Die Dokumentation von Vite sagt es unmissverständlich: Variablen mit dem Präfix
VITE_ werden nach dem Bundling im clientseitigen Quellcode offengelegt, und
sensible Informationen wie API-Schlüssel gehören nicht in eine davon, weil die
Werte in deinen Quellcode gebündelt werden. Die Präfixe VITE_ und
NEXT_PUBLIC_ sind keine sichere Schachtel. Sie sind eine Erklärung, dass du
verstanden hast, dass diese Variable öffentlich ist.
Eine Umgebungsvariable hat sehr wohl eine echte Sache für dich getan: sie hat den Schlüssel aus deinem Repository herausgehalten, wo ihn jeder gefunden hätte, der deinen Code liest. Deine Besucher lesen deinen Code nie. Sie lesen die Datei, die dein Build daraus gemacht hat.
Warum du ihn nicht wie einen Google-Schlüssel einschränken kannst
Weil OpenAI keine Einschränkung dieser Art anbietet.
Wenn du über einen Google-API-Schlüssel im Frontend gelesen hast, bist du einer Lösung von fünf Minuten begegnet: Schlüssel in der Google Cloud Console öffnen, eine HTTP-Referrer-Einschränkung setzen, und der in deiner Seite abgedruckte Schlüssel funktioniert auf deiner Website und liefert überall sonst einen Fehler. Dieser Rat ist für einen Google-Schlüssel richtig, und er trägt nicht hinüber.
Es gibt kein Feld an einem OpenAI-Schlüssel für "nur von yourapp.com". Keine Domain-Zulassungsliste, keine Referrer-Prüfung und keine IP-Einschränkung, die einen Browser überlebt. Was OpenAI dir stattdessen gibt, sitzt am Konto hinter dem Schlüssel: zu welchem Projekt er gehört, was dieses Projekt im Monat ausgeben darf und ob der Schlüssel noch existiert. Das begrenzt, was ein gestohlener Schlüssel kosten kann. Der Schlüssel selbst funktioniert von überall weiter, bis du ihn löschst.
Was dangerouslyAllowBrowser tatsächlich tut
Es schaltet eine Absicherung ab, und sein Name ist die Dokumentation.
Die offizielle JavaScript-Bibliothek von OpenAI weigert sich, standardmäßig in
einem Browser zu laufen. Die README sagt, die Browser-Unterstützung sei
"disabled by default to avoid exposing your secret API credentials", und das
Einschalten von dangerouslyAllowBrowser "can be dangerous because it exposes
your secret API credentials in the client-side code".
Wenn deine App OpenAI aus dem Browser aufruft, steht diese Option irgendwo in
deinem Code auf true, denn die Bibliothek startet ohne sie nicht. Jemand hat das
Wort dangerously getippt, um eine Fehlermeldung loszuwerden. So kommt dieser
Fund üblicherweise zustande, und die Bibliothek hat es dir zuerst gesagt.
Die Option hat echte Anwendungen, und alle sind eng: ein internes Werkzeug, bei dem du jeden Nutzer kennst, ein vorübergehender Entwicklungsschlüssel, ein so eng zugeschnittener Schlüssel, dass ihn auszugeben nichts kostet. Eine öffentliche App im offenen Internet ist keines davon.
Was es kostet, wenn jemand deinen Schlüssel findet
Eine Rechnung, und eine App, die aufhört zu funktionieren.
Die Rechnung ist der Teil, den die Leute erwarten. Die Anfragen von jemand anderem werden deinem Konto belastet, und Modellaufrufe sind nach den Maßstäben eines Nebenprojekts nicht billig. Üblicherweise merkt ein Betreiber es an einer Rechnung, die größer ist als die des Vormonats, ohne dass er einen Grund benennen könnte.
Der Ausfall ist der Teil, den sie nicht erwarten. Dein Konto hat Ratenlimits, und der Verkehr eines Fremden verbraucht sie. Deine eigene App fängt an, in den Stunden Fehler zu liefern, in denen jemand anderes beschäftigt ist, und das liest sich wie ein Bug statt wie ein Diebstahl, also verbringen Leute einen Tag damit, das Falsche zu debuggen.
Es gibt eine dritte Kostenart, und sie hängt davon ab, wie eng der Schlüssel zugeschnitten war. Ein API-Schlüssel authentifiziert jeden Aufruf, den das Projekt machen kann, zu dem er gehört, also erreicht ein breit zugeschnittener Schlüssel alles andere, was dort liegt: in das Konto hochgeladene Dateien, feinabgestimmte Modelle, von dir gebaute Assistenten. Prüfe, was der Schlüssel tatsächlich erreichen konnte, bevor du entscheidest, dass es hier nur um Geld ging.
Wie du OpenAI aufrufst, ohne den Schlüssel auszuliefern
Stell etwas von dir zwischen deinen Besucher und OpenAI.
Die Form ist überall dieselbe. Deine App ruft einen kleinen Endpunkt auf, der dir gehört. Dieser Endpunkt hält den Schlüssel, ruft OpenAI auf und reicht die Antwort zurück. Der Browser sieht den Schlüssel nie, weil der Schlüssel deinen Server nie verlässt.
Wo dieser Endpunkt liegt, hängt davon ab, womit du gebaut hast:
- Supabase in deinem Stack: eine Edge Function, mit dem Schlüssel als Secret im Supabase-Dashboard.
- Auf Vercel oder Netlify deployt: eine Serverless Function unter
/api, mit dem Schlüssel in den serverseitigen Umgebungsvariablen des Projekts. - Lovable, Bolt oder Replit: jedes hat einen eigenen Secrets-Speicher. Die Regel ändert sich nicht, und die Falle auch nicht: ein Speicher mit der Aufschrift Secrets liefert den Wert trotzdem an den Browser, wenn der Code, der ihn liest, dort läuft.
Zwei Dinge lohnen sich, solange du dabei bist. Gib deinem Endpunkt ein Ratenlimit, denn ein Endpunkt, der OpenAI für jeden aufruft, der fragt, ist dieselbe Rechnung auf einem langsameren Weg. Und setz ein monatliches Ausgabenlimit für das OpenAI-Projekt, die eine Kontrolle, die dem schlimmsten Fall einen Boden einzieht.
Wenn deine App die Realtime-API von OpenAI für Sprache nutzt, braucht der Browser tatsächlich Zugangsdaten, und OpenAI dokumentiert, wie man ihm welche gibt: dein Server erzeugt ein kurzlebiges Client-Secret und reicht der Seite dieses. Die Fahrkarte existiert weiter, sie läuft in Minuten ab, und dein eigener Server hat sie ausgestellt.
Wie du kostenlos jeden Schlüssel findest, den deine App ausliefert
Fang von Hand an, das kostet fünf Minuten und braucht keine Installation. Sieh
dir den Quelltext deiner Live-Website an und such darin nach sk-proj- für einen
OpenAI-Schlüssel, sk-ant- für einen von Anthropic, AIza für Google und eyJ
für ein Supabase-Token.
Was dabei durchrutscht, ist das JavaScript, das die Seite danach nachlädt, und das ist bei einer vibe-codeten App fast alles. Unser Scanner öffnet deine App in einem echten Browser, wartet, bis die Bundles eintreffen, und liest diese statt des HTML. Er entschlüsselt außerdem jedes Supabase-Token und meldet die darin geschriebene Rolle, sodass ein Schlüssel, der in deine Seite gehört, als korrekt markiert zurückkommt und nicht in einer Wand aus Rot untergeht.
Die Schlüsselprüfung ist eine von neun, und die anderen acht sind der Grund, warum eine Note dir mehr sagt als eine Suche:
| Worauf der Scan schaut | Die Frage, die er beantwortet |
|---|---|
| Geheime Schlüssel in deinem Code | Ist ein kostenpflichtiger API- oder Admin-Schlüssel für jeden lesbar? |
| Datenbankregeln | Kann ein Fremder ohne Anmeldung die Zeilen deiner Nutzer lesen? |
| Private Dateien | Sind .env-Dateien oder Datenbank-Dumps über eine URL abrufbar? |
| Sicherheits-Header | Sind die Schutzmechanismen im Browser eingeschaltet? |
| Storage-Buckets | Kann jeder die von deinen Nutzern hochgeladenen Dateien auflisten? |
| Source Maps | Ist dein originaler Quellcode neben der App veröffentlicht? |
| Offene APIs und CORS | Antworten deine Endpunkte jeder Website, die fragt? |
| Zertifikatsablauf | Ist HTTPS gültig und läuft es nicht gleich bei deinen Besuchern ab? |
| Domain-Verlängerung | Ist der Name verlängert, bevor ihn jemand anderes nehmen kann? |
Du bekommst Note, Punktzahl und die Zählungen in etwa 20 Sekunden auf den Bildschirm, ohne Konto. Gib eine E-Mail-Adresse an, und die ausführliche Liste kommt dazu, samt einer für deinen Builder geschriebenen Lösung, die du direkt einfügen kannst.
Drei Dinge tut er nicht, und das sind die Gründe, warum man ihn auf eine Live-App
richten kann: er meldet sich nie an, er schreibt nie etwas, und er behält nie
einen gefundenen Schlüssel. Ein offenliegendes Geheimnis wird als maskierter
Hinweis der Form sk-proj-…a1b2 gespeichert, und der echte Wert wird verworfen.
Scanne deine App, oder lies vorher,
worauf jede der neun Prüfungen schaut.
Was jetzt zu tun ist
Was zu tun ist
- Tausch den Schlüssel zuerst aus, im OpenAI-Dashboard unter API keys. Ihn aus deinem Code zu löschen schließt nichts, denn der alte Wert steht weiterhin in deiner Versionsgeschichte und in jeder zwischengespeicherten Kopie deiner Seite.
- Setz ein monatliches Ausgabenlimit für das Projekt, zu dem der Schlüssel gehört. Es ist die eine Kontrolle, die deckelt, was dieser oder ein späterer Fehler dich kosten kann.
- Verleg den Aufruf hinter einen eigenen Endpunkt und gib diesem Endpunkt ein eigenes Ratenlimit. Ein Browser sollte nie einen Schlüssel halten, der Geld ausgibt.
- Lies deine Nutzungsseite für die Tage, an denen der Schlüssel aktiv war. Ein Austausch stoppt, was als Nächstes passiert, und sagt nichts über das, was schon passiert ist.
- Halte ein
VITE_- oderNEXT_PUBLIC_-Präfix von allem fern, dessen Lektüre durch einen Fremden dich stören würde. Diese Präfixe bedeuten öffentlich, und dein Build-Werkzeug nimmt sie beim Wort.
Wenn du das lieber in einer Sitzung durchgehst: die Sicherheits-Checkliste in 10 Minuten deckt das zusammen mit den anderen Dingen ab, die sich in einer frisch gestarteten App schließen lassen. Und für die größere Frage, welche Schlüssel überhaupt in einen Browser gehören, haben wir einen Leitfaden, der veröffentlichbare von geheimen unterscheidet und eine Erhebung dazu, was 30.998 Apps tatsächlich ausgeliefert haben.
FAQ
Jemand hat meinen OpenAI-Schlüssel in meiner App gefunden. Was mache ich zuerst?
Tausch ihn aus. Öffne dein OpenAI-Dashboard unter API keys, erzeuge einen neuen Schlüssel, leg den neuen auf deinen Server und lösche den alten. Ihn aus deinem Code zu löschen ist nicht dasselbe, denn der alte Wert steht weiterhin in deiner Versionsgeschichte und in jeder zwischengespeicherten Kopie deiner Seite. Setz danach ein Ausgabenlimit für das Projekt und lies deine Nutzungsseite für die Tage, an denen der Schlüssel aktiv war.
Kann ich einen OpenAI-Schlüssel auf meine Domain beschränken, so wie einen Google-Schlüssel?
Nein. Ein Google-API-Schlüssel nimmt eine HTTP-Referrer-Einschränkung an, die ihn auf deiner Website funktionieren und überall sonst scheitern lässt, und genau deshalb ist ein Google-Schlüssel in deiner Seite meistens unbedenklich. OpenAI bietet nichts Vergleichbares. Es gibt weder eine Domain-Zulassungsliste noch eine Referrer-Prüfung für einen API-Schlüssel, also hast du nur Kontrollen über das Konto dahinter: zu welchem Projekt der Schlüssel gehört, was dieses Projekt ausgeben darf und ob der Schlüssel überhaupt noch existiert.
Die OpenAI-Bibliothek hat eine Option namens dangerouslyAllowBrowser. Macht die den Schlüssel sicher?
Nein, und der Name ist die Warnung. OpenAI liefert die Browser-Unterstützung ausgeschaltet aus, und die eigene README sagt, dass die Option gefährlich ist, weil sie deine geheimen API-Zugangsdaten im clientseitigen Code offenlegt. Sie einzuschalten ändert nichts daran, was der Browser lesen kann; sie hält die Bibliothek nur davon ab, den Start zu verweigern. Die engen Fälle, für die sie gedacht ist, sind interne Werkzeuge mit bekannten Nutzern und kurzlebige Entwicklungsschlüssel, nicht eine öffentliche App.
Wie viel kann jemand mit einem gefundenen Schlüssel ausgeben?
So viel, wie dein Konto zulässt, und deshalb zählt das Ausgabenlimit mehr als die Höhe der Rechnung, die du bisher gesehen hast. Ein gestohlener Schlüssel zieht an denselben Ratenlimits wie deine App, also ist das erste Symptom oft gar nicht die Rechnung: deine eigene App fängt an zu scheitern, während jemand anderes beschäftigt ist. Setz ein monatliches Ausgabenlimit für das Projekt, dann hast du eine Obergrenze für alles davon.
Ich habe den Schlüssel nur für eine schnelle Demo benutzt. Spielt das trotzdem eine Rolle?
Ja. Ein Schlüssel bleibt aktiv, bis ihn jemand widerruft, und er weiß nicht, dass er vorübergehend sein sollte. Automatische Scraper sammeln fortlaufend schlüsselförmige Zeichenketten von öffentlichen Seiten, also arbeitet das Alter der Demo gegen dich statt für dich. Den Schlüssel zu löschen dauert kürzer als die Entscheidung, ob er das Löschen wert war.