Zum Inhalt springen

Ermitteln gültiger Benutzernamen

CWE-204CWE-203OWASP A07:2021Aktualisiert 4. September 20265 Min. Lesezeit

Reagiert eine Anwendung auf ein vorhandenes Konto anders als auf ein nicht vorhandenes, kann ein Angreifer feststellen, welche Benutzernamen gültig sind. Dieser Unterschied steckt nicht nur in der Fehlermeldung, sondern auch in Statuscodes, Weiterleitungen und Antwortzeiten. Mit einer gültigen Liste wird jeder Folgeangriff gezielter und billiger.

Eine Anmeldemaske, die hilfsbereit meldet, das Passwort sei falsch, bestätigt damit zugleich, dass der Benutzername stimmt. Das ist angenehm für alle, die sich vertippen, und wertvoll für alle, die eine Liste aufbauen. In diesem Artikel lesen Sie, über welche Wege dieser Unterschied sichtbar wird, meist nicht allein über die Fehlermeldung, und wie Sie die Antworten angleichen, ohne Ihre Nutzer im Unklaren zu lassen.

Was ist Username Enumeration?

Username Enumeration, also das Ermitteln gültiger Benutzernamen, bezeichnet das Feststellen, welche Konten in einem System bestehen, indem das Verhalten der Anwendung verglichen wird. Der Angreifer benötigt kein Passwort und muss nirgends einbrechen; er leitet die Information aus dem Unterschied zwischen zwei Antworten ab.

Dieser Unterschied muss nicht im Text liegen. Es kann ein abweichender HTTP-Statuscode sein, eine andere Weiterleitung, eine etwas längere Antwortzeit, ein Unterschied in der Länge der Antwort oder das Erscheinen eines Captchas. Jede wahrnehmbare Unterscheidung zwischen “dieses Konto besteht” und “dieses Konto besteht nicht” ist verwertbar.

Es gleicht dem Klingeln an einem Mehrfamilienhaus, bei dem bei dem einen Namen ein Summer ertönt und bei dem anderen nichts geschieht. Sie kommen nirgends hinein, wissen aber nach einer Viertelstunde genau, wer dort wohnt. Und das ist die Information, mit der der eigentliche Angriff beginnt.

Wo steckt der Unterschied?

Die Anmeldeseite ist die bekannteste Stelle, aber selten die einzige.

Verwundbar:

app.post('/anmelden', async (req, res) => {
  const nutzer = await nutzerRepo.findeUeberEmail(req.body.email);
  if (!nutzer) {
    return res.status(404).send('Diese E-Mail-Adresse ist uns nicht bekannt');
  }

  const stimmt = await bcrypt.compare(req.body.passwort, nutzer.hash);
  if (!stimmt) {
    return res.status(401).send('Das Passwort ist falsch');
  }
  // ...
});

Hier laufen Informationen auf drei Wegen zugleich aus: Der Text unterscheidet sich, der Statuscode unterscheidet sich (404 gegenüber 401), und die Antwortzeit unterscheidet sich, da bcrypt.compare nur ausgeführt wird, wenn das Konto besteht. Letzteres ist der hartnäckigste Punkt, denn er bleibt bestehen, nachdem die ersten beiden behoben sind. Ein Angreifer lässt einfach eine Liste hindurchlaufen:

info@firma.de          → 404, 12 ms   → besteht nicht
j.walter@firma.de      → 401, 180 ms  → besteht

Derselbe Fehler kehrt häufig bei der Funktion zum Zurücksetzen des Passworts wieder (“es gibt kein Konto mit dieser Adresse”), bei der Registrierung (“diese Adresse wird bereits verwendet”) und beim Ändern einer E-Mail-Adresse.

Sicher:

const DUMMY_HASH = '$2b$12$c8y3Nq0Wl0mVQe1n6JbEZeQmQm2Yy8kQF9nZ0Xf1yqk8dK1vI0Iuq';

app.post('/anmelden', async (req, res) => {
  const nutzer = await nutzerRepo.findeUeberEmail(req.body.email);

  // Immer vergleichen, auch ohne Konto: gleiche Rechenzeit
  const hash = nutzer?.hash ?? DUMMY_HASH;
  const stimmt = await bcrypt.compare(req.body.passwort, hash);

  if (!nutzer || !stimmt) {
    await zufaelligeVerzoegerung();
    return res.status(401).send('Die Kombination aus E-Mail-Adresse und Passwort ist falsch');
  }
  // ...
});

Die Antwort ist nun in jeder Hinsicht identisch: derselbe Text, derselbe Statuscode und, da auch bei einem unbekannten Konto ein Hashvergleich ausgeführt wird, nahezu dieselbe Antwortzeit. Für die Funktion zum Zurücksetzen des Passworts gilt dieselbe Regel mit einer neutralen Bestätigung:

app.post('/passwort-vergessen', async (req, res) => {
  const nutzer = await nutzerRepo.findeUeberEmail(req.body.email);
  if (nutzer) await sendeWiederherstellungsmail(nutzer);   // sonst stillschweigend übersprungen

  res.send('Sofern uns diese Adresse bekannt ist, erhalten Sie in wenigen Minuten eine E-Mail.');
});
Vergessen Sie die Nebenkanäle nicht. Ein Captcha, das nur bei bestehenden Konten erscheint, eine Kontosperre, die nur dann greift, oder ein Zwei-Faktor-Schritt, der nur gültigen Nutzern gezeigt wird, verrät genau dasselbe wie eine abweichende Fehlermeldung, auch wenn der Text selbst tadellos neutral ist.

Welche Auswirkungen hat Username Enumeration?

Als eigenständiger Befund ist dies eine Sache von niedrigem Schweregrad: Es wird kein Zugang erlangt, und es fließen keine Daten aus den Konten selbst ab. Die Bedeutung liegt in dem, was dadurch möglich wird.

Mit einer geprüften Liste bestehender Konten wird jeder Folgeangriff wirksamer. Ein Passwortangriff, der zuvor Tausende nicht existierender Adressen abarbeiten musste, richtet sich nun ausschließlich auf echte Konten, was die Erfolgsaussicht erhöht und die Entdeckungswahrscheinlichkeit senkt. Beim sogenannten Password Spraying, bei dem ein einziges verbreitetes Passwort auf viele Konten angewandt wird, ist eine solche Liste sogar die wichtigste Voraussetzung.

Daneben gibt es einen unmittelbaren Missbrauch, der mit dem Anmelden nichts zu tun hat. Die Bestätigung, dass jemand ein Konto besitzt, ist für sich genommen eine sensible Information. Bei einem medizinischen, juristischen, finanziellen oder politisch heiklen Dienst kann das Bestehen eines Kontos mehr verraten als dessen Inhalt. Und für gezieltes Phishing ist es ein Geschenk: Eine E-Mail, die sich auf einen Dienst bezieht, den der Empfänger tatsächlich nutzt, überzeugt deutlich mehr.

Wie spürt man Username Enumeration auf?

Ein Tester sendet zwei Reihen von Anfragen: eine mit Adressen, zu denen sicher ein Konto besteht, und eine mit Adressen, die es nicht gibt. Anschließend werden die Antworten auf alles Wahrnehmbare verglichen: Text, Statuscode, Länge der Antwort, Header, Weiterleitung und Antwortzeit.

Das geschieht nicht nur auf der Anmeldeseite. Gerade die umliegenden Funktionen sind häufiger verwundbar: Passwort vergessen, Registrierung, Ändern einer E-Mail-Adresse, Einladungen und API-Endpunkte, die für das Frontend die Verfügbarkeit eines Benutzernamens prüfen. Letztere Kategorie ist bekannt: Ein Endpunkt, der beim Ausfüllen eines Registrierungsformulars meldet, ob ein Name noch frei ist, ist per Definition eine Enumerationsschnittstelle. AssistSec misst dabei zusätzlich die Antwortzeiten statistisch, denn ein Zeitunterschied, der in einer einzelnen Messung verschwindet, wird über Hunderte Anfragen deutlich sichtbar, und genau so würde ein Angreifer vorgehen.

Wie verhindert man Username Enumeration?

  • Geben Sie beim Anmelden stets dieselbe Meldung und denselben Statuscode aus, unabhängig davon, ob das Konto besteht.
  • Führen Sie auch bei einem unbekannten Konto einen Hashvergleich durch, damit die Antwortzeit nicht verrät, was geschehen ist.
  • Bestätigen Sie beim Zurücksetzen des Passworts neutral, dass eine E-Mail versandt wurde, sofern die Adresse bekannt ist.
  • Melden Sie bei der Registrierung nicht, dass eine Adresse bereits verwendet wird; erläutern Sie das in der E-Mail an diese Adresse.
  • Sorgen Sie dafür, dass Captchas, Verzögerungen und Sperren bei bestehenden und nicht bestehenden Konten gleich greifen.
  • Vermeiden Sie Endpunkte, die die Verfügbarkeit eines Benutzernamens oder einer E-Mail-Adresse prüfen, oder begrenzen Sie sie streng.
  • Begrenzen Sie die Zahl der Versuche je IP-Adresse und Zeiteinheit, damit großflächige Enumeration auffällt und stockt.
  • Überwachen und alarmieren Sie bei einer ungewöhnlichen Zahl fehlgeschlagener Anmeldungen mit wechselnden Benutzernamen.

Quellen

Häufige Fragen

Ist das schlimm, wenn Benutzernamen ohnehin E-Mail-Adressen sind?

Es macht die Sache weniger gravierend, aber nicht unwichtig. Ein Angreifer kennt dann vielleicht die Adresse, weiß aber nicht, ob bei Ihnen ein Konto dazu besteht. Diese Bestätigung ist wertvoll: Sie macht gezieltes Phishing glaubwürdiger und begrenzt einen Passwortangriff auf tatsächlich vorhandene Konten.

Wie verhindere ich Enumeration bei der Registrierung?

Melden Sie beim Anlegen eines Kontos nie unmittelbar, dass eine Adresse bereits verwendet wird. Bestätigen Sie stattdessen, dass eine E-Mail versandt wurde, und erläutern Sie in dieser E-Mail, ob es um ein neues oder ein bestehendes Konto geht. Die Information gelangt dann nur zu demjenigen, der das Postfach verwaltet.

Warum ist die Antwortzeit relevant?

Weil eine Anwendung bei einem vorhandenen Konto meist das Passwort prüft und bei einem unbekannten nicht. Diese Berechnung kostet messbar Zeit, oft einige zehn Millisekunden. Ein Angreifer, der Tausende Versuche unternimmt, sieht diesen Unterschied deutlich, auch wenn die Fehlermeldung identisch ist.

Ist das mit Zwei-Faktor-Authentifizierung noch ein Problem?

Der Schweregrad sinkt, denn ein gültiger Benutzername und ein erratenes Passwort genügen dann nicht. Die Liste bleibt aber für gezieltes Phishing und für Angriffe auf den zweiten Faktor selbst nützlich, etwa das Bombardieren einer Person mit Push-Benachrichtigungen, bis sie eine davon bestätigt.

Verwandte Artikel

Zum Suchen / drücken · Esc