API-Antworten enthalten zu viele Daten
CWE-213CWE-200OWASP A01:2021Aktualisiert 4. September 20264 Min. Lesezeit
Viele APIs geben den vollständigen Datenbanksatz zurück und überlassen es dem Frontend zu bestimmen, was angezeigt wird. Was auf dem Bildschirm unsichtbar bleibt, steht dennoch in der Antwort: Passwort-Hashes, interne Vermerke, Daten anderer Nutzer. Das Filtern gehört auf die Serverseite.
Beim Bau einer API ist es verlockend, den vollständigen Datensatz zurückzugeben und das Frontend wählen zu lassen, was davon sichtbar wird. Es spart Arbeit, macht die API flexibel, und auf dem Bildschirm sieht niemand den Unterschied. Im Netzwerkverkehr schon, und dort schaut am Ende jeder hin, der möchte. Im Folgenden lesen Sie, was alles mitfährt und wie Sie das strukturell schließen.
Was bedeutet zu viele Daten zurückzugeben?
Von zu vielen Daten in API-Antworten sprechen wir, wenn ein Endpunkt mehr Felder zurückgibt, als der Anfragende benötigt oder sehen darf, in der Annahme, der Client werde schon filtern. Der international gebräuchliche Begriff lautet Excessive Data Exposure, heute der Autorisierung auf Eigenschaftsebene zugeordnet.
Der Denkfehler liegt in der Annahme darüber, wer die API nutzt. Während der Entwicklung ist das Ihr eigenes Frontend, und dieses zeigt sauber nur die vorgesehenen Felder an. Die API steht jedoch jedem mit einem gültigen Konto offen, und die Antwort ist im Entwicklerwerkzeug jedes Browsers sichtbar. Alles, was Sie mitsenden, ist damit ausgeliefert.
Vergleichen Sie es mit einer Akte, deren heikle Seiten Sie in dem Moment, in dem Sie sie jemandem zeigen, mit einem Blatt abdecken. Solange der andere brav dorthin schaut, wohin Sie zeigen, geht es gut. Doch Sie haben ihm die ganze Akte in die Hand gegeben, und das Blatt ist nicht festgeklebt.
Wie sieht eine solche API-Antwort aus?
Verwundbar:
// Der vollständige Datensatz geht nach außen
app.get('/api/nutzer/:id', erfordertAnmeldung, async (req, res) => {
const nutzer = await db.nutzer.finde(req.params.id);
res.json(nutzer);
});
Die Oberfläche zeigt eine Profilkarte mit Namen und Foto. Die Antwort enthält erheblich mehr:
{
"id": 4471,
"name": "J. Walter",
"email": "j.walter@firma.de",
"passwort_hash": "$2b$12$c8y3Nq0Wl0mVQe1n6JbEZe...",
"telefon": "+49 151 12345678",
"steuer_id": "12345678901",
"gehaltsstufe": 11,
"interner_vermerk": "Beurteilungsverfahren läuft bis Q3",
"mfa_secret": "JBSWY3DPEHPK3PXP",
"rolle": "mitarbeiter"
}
Hier läuft mehreres zugleich schief. Der Passwort-Hash ist Material für einen Knackversuch außerhalb des Systems. Das mfa_secret ermöglicht es, selbst gültige Zwei-Faktor-Codes zu erzeugen, womit diese gesamte Maßnahme umgangen ist. Der interne Vermerk und die Gehaltsstufe sind personenbezogene Daten, die nicht einmal für die betroffene Person bestimmt waren. Und da es sich um einen Endpunkt handelt, der anhand einer Kennung arbeitet, lässt sich das Ganze mühelos systematisch ablaufen.
Sicher:
// Ausdrücklich festlegen, was nach außen darf, je Rolle
function oeffentlichesProfil(nutzer) {
return {
id: nutzer.id,
name: nutzer.name,
abteilung: nutzer.abteilung,
};
}
function eigenesProfil(nutzer) {
return {
...oeffentlichesProfil(nutzer),
email: nutzer.email,
telefon: nutzer.telefon,
};
}
app.get('/api/nutzer/:id', erfordertAnmeldung, async (req, res) => {
const nutzer = await db.nutzer.finde(req.params.id);
if (!nutzer) return res.sendStatus(404);
const eigen = nutzer.id === req.nutzer.id;
res.json(eigen ? eigenesProfil(nutzer) : oeffentlichesProfil(nutzer));
});
Der entscheidende Unterschied ist die Richtung der Auswahl. Es wird festgelegt, was nach außen darf, statt was weggelassen werden muss. Das wiegt schwerer, als es scheint: Bei einer Liste von Ausnahmen landet jedes neue Datenbankfeld automatisch in der Antwort, und niemand denkt beim Hinzufügen einer Spalte daran.
Fragen Sie darüber hinaus nur ab, was Sie benötigen. Eine Abfrage, die SELECT * vermeidet, kann die heiklen Felder gar nicht versehentlich mitsenden, weil sie schlicht nicht vorhanden sind.
Welche Auswirkungen haben zu viele Daten in API-Antworten?
Der Schweregrad reicht von mittel bis hoch und bestimmt sich nach den preisgegebenen Feldern. Geht es um eine interne laufende Nummer, ist die Wirkung gering. Geht es um personenbezogene Daten, Passwort-Hashes oder Geheimnisse für die Zwei-Faktor-Authentifizierung, ist es schwerwiegend.
Das Unangenehme ist, dass das Leck in den üblichen Kontrollen vollständig unsichtbar bleibt. Es gibt keine Fehlermeldung, kein abweichendes Verhalten und kein ungewöhnliches Muster in den Protokollen; der Endpunkt tut genau das, was ihm aufgetragen wurde. Der Befund kommt bei einem Test ans Licht oder dadurch, dass die Daten anderswo auftauchen.
In Verbindung mit anderen Schwachstellen wächst die Auswirkung rasch. Fehlt zusätzlich die Autorisierung auf Objektebene, kann ein Angreifer die vollständige Nutzerverwaltung einschließlich aller heiklen Felder abrufen. Fehlt eine Ratenbegrenzung, geschieht das in Minuten. Was einzeln drei mittelschwere Befunde sind, ist zusammen ein meldepflichtiges Datenleck.
Wie spürt man zu viele Daten in API-Antworten auf?
Ein Tester schaut nicht auf den Bildschirm, sondern auf den Netzwerkverkehr. Jede API-Antwort wird mit dem verglichen, was die Oberfläche davon zeigt; der Unterschied ist der Befund. Das liefert häufig unmittelbar Ergebnisse, denn die Abweichung ist selten klein.
Zusätzlich wird auf Felder geachtet, die in der Oberfläche nicht vorkommen, deren Name aber etwas verrät: alles mit hash, secret, token, intern oder vermerk. Ebenso werden Listenendpunkte betrachtet, bei denen dasselbe Objekt oft in anderer Form zurückkommt als beim Abruf eines einzelnen Eintrags und bei denen die Filterung dann gerade nicht angewandt wurde. Auch Fehlerantworten und verschachtelte Objekte erhalten Aufmerksamkeit, und bei GraphQL Felder, die im Schema stehen, vom eigenen Frontend aber nie abgefragt werden. AssistSec beurteilt, ob sich die Felder je Rolle unterscheiden, denn eine Verwaltungsansicht und eine Nutzeransicht werden in der Praxis häufig von demselben Endpunkt bedient, ohne Unterschied in der Ausgabe.
Wie verhindert man zu viele Daten in API-Antworten?
- Legen Sie ausdrücklich fest, welche Felder ein Endpunkt zurückgibt, statt welche Sie weglassen.
- Definieren Sie je Rolle ein eigenes Ausgabemodell und serialisieren Sie darüber.
- Fragen Sie in Ihren Abfragen nur die Spalten ab, die Sie benötigen; vermeiden Sie
SELECT *. - Geben Sie nie Passwort-Hashes, Zwei-Faktor-Geheimnisse, Wiederherstellungstoken oder API-Schlüssel zurück.
- Behandeln Sie verschachtelte Objekte mit derselben Sorgfalt wie das Hauptobjekt.
- Überlassen Sie das Filtern nie dem Frontend; das ist Darstellung, keine Absicherung.
- Autorisieren Sie bei GraphQL je Feld, nicht nur je Abfrage.
- Prüfen Sie bei jedem neuen Datenbankfeld, ob es unbeabsichtigt in bestehenden Antworten auftaucht.
- Nehmen Sie eine Prüfung der Ausgabe in Ihre automatisierten Tests auf, damit ein neues Feld auffällt.
Quellen
Häufige Fragen
Der Nutzer sieht diese Felder in der Oberfläche doch nicht?
In der Oberfläche nicht, in der Antwort sehr wohl. Jeder kann die Netzwerkanfragen des eigenen Browsers betrachten oder die API unmittelbar aufrufen. Was das Frontend verbirgt, ist ausschließlich eine Frage der Darstellung; es ist keine Absicherung und mit zwei Klicks umgangen.
Wie verhindere ich das strukturell?
Indem Sie ausdrücklich festlegen, welche Felder nach außen gehen, statt welche Sie weglassen. Definieren Sie je Endpunkt und je Rolle ein Ausgabemodell und serialisieren Sie darüber. Ein neues Datenbankfeld landet dann nicht von selbst in der Antwort, und genau dieser Fehlermechanismus soll ausgeschlossen werden.
Gilt das auch für GraphQL?
Ja, mit einem eigenen Schwerpunkt. Bei GraphQL bestimmt der Client, welche Felder er abfragt, die Autorisierung muss also je Feld erfolgen. Ein Feld, das im Schema steht, aber nicht abgesichert ist, ist abrufbar, auch wenn Ihr eigenes Frontend es nie anfordert.
Ist das dasselbe wie ein Datenleck?
Es ist dessen Ursache, sobald es jemand bemerkt. Häufig fällt der Befund erst bei einem Test auf oder nachdem Daten anderswo auftauchen. Das Besondere daran ist, dass nichts eingebrochen wurde: Die Daten wurden schlicht an jeden ausgeliefert, der danach fragte.
Verwandte Artikel
- SchwachstellenCWE-285A01:2021Unzureichende Autorisierung auf FunktionsebeneEine nur aus dem Menü entfernte Verwaltungsfunktion bleibt über eine direkte Anfrage erreichbar. Lesen Sie, wie Sie Autorisierung je Funktion erzwingen.
- SchwachstellenCWE-639A01:2021Unzureichende Autorisierung auf Objektebene in APIsEine API, die nur prüft, ob Sie angemeldet sind, nicht aber, ob dieser Datensatz Ihnen gehört, gibt fremde Daten preis. Lesen Sie, wie Sie das erzwingen.
- SchwachstellenCWE-200A01:2021Information DisclosureInformation Disclosure erklärt: wie Stack Traces, .git-Verzeichnisse, Source Maps und zu großzügige API-Antworten Daten preisgeben.
- SchwachstellenCWE-915A01:2021Mass AssignmentWer einen ganzen Anfragekörper auf ein Modell bindet, macht isAdmin oder guthaben beschreibbar. Lesen Sie, wie eine Positivliste das verhindert.
- SchwachstellenCWE-770A04:2021Keine Ratenbegrenzung auf der APIOhne Ratenbegrenzung lässt sich eine API endlos abfragen. Lesen Sie, wie das zu Datendiebstahl, Kostenmissbrauch und Ausfällen führt.