Zum hauptinhalt springen

Wie wir ein Bahnticket buchen wollten und am Ende 245.000 Datensätze hatten

Ein Modell einer Dampflok mit französischen und deutschen Fahnen, einem angehängten Trans-Europ-Express-Waggon. Der Zug verliert Tickets

Um die deutsch-französische Freundschaft zu feiern, haben sich Bundesverkehrsminister Wissing und sein französischer Kollege Beaune etwas Besonderes ausgedacht: Je Land 30.000 kostenlose Interrail-Tickets für Reisen in Deutschland und Frankreich für junge Erwachsene zwischen 18 und 27.

Allerdings lief beim Verteilen der Interrail-Pässe einiges schief. Im Folgenden wollen wir euch mitnehmen auf eine Reise durch die Stationen des nicht ganz so gut umgesetzten Tickets und zeigen, wie man auch nach Ende der Registrierung noch an einen Pass kommen konnte.

Und wenn wir schonmal unterwegs sind, präsentieren wir gleich mit: Eine Sicherheitslücke in einem ähnlichen Projekt auf EU-Ebene – umgsetzt von der gleichen Agentur – wodurch die Daten von rund 245.000 Registrierungen nahezu ungeschützt im Netz standen.

Bitte zurücktreten – die Türen schließen und wir fahren ab! 🚄🚃🚃🚃🚃

Station 1: DDoSingen

Am 12. Juni um 10 Uhr war es so weit: Die Registrierung für den Freundschaftspass wurde geöffnet – und die Server waren sofort überlastet.

Dabei hätte man mit dem Ansturm rechnen müssen, denn die Tickets wurden nach dem “First come, first served”-Prinzip (“wer zuerst kommt, mahlt zuerst”) verteilt. Wer eine Chancen auf eins der begehrten Tickets haben wollte, musste also schnell sein. Dadurch probierten vermutlich zehntausende Nutzer*innen in Deutschland und Frankreich pünktlich um 10 Uhr gleichzeitig, an den Pass zu kommen.

Klar: Ein System aufzusetzen, das mit so einem Ansturm umgehen kann, ist nicht trivial. Aber es ist möglich! Ähnliche Situationen kennen beispielsweise Veranstalter*innen von Konzerten beliebter Bands und Künstler*innen – und können in der Regel damit umgehen.

So etwas muss gut durchdacht, vorbereitet und getestet werden. Hätte man das ordentlich gemacht, hätte man merken müssen: Verdammt, das können wir nicht stemmen – und man hätte sich eine andere Lösung überlegt.

Denn das “First come, first served”-Prinzip ist nicht alternativlos: Statt alles auf einen Moment zu setzen, kann man den Verkauf auf mehrere Zeitpunkte verteilen. So verteilt sich nicht nur der Ansturm – es können auch Personen teilnehmen, die an einem Montag um 10 Uhr keine Zeit haben, beispielsweise weil sie in der Schule, im Ausbildungsbetrieb oder in der Uni sitzen. #Zielgruppenorientierung

Man kann in einem solchen System auch Interessierten ein paar Tage Zeit geben, um sich anzumelden und dann die Pässe verlosen.

Denn: Überlastete Server sind kein Zeichen für den Erfolg einer Aktion, sondern ein Zeichen für Versagen bei der Planung.

Station 2: Passwort-Reset-Hausen

Während wir uns noch über dieses Versagen wunderten, bekamen wir einen Hinweis per Mail: Nicht nur an dieser Stelle war die Plattform mit der heißen Nadel gestrickt und ungetestet.

Wie zeigte sich das? Die Glücklichen, die sich einen Pass sichern konnten, mussten eine E-Mail-Adresse angeben und sich ein Passwort ausdenken. Mit dieser Kombination konnten sie sich dann auf der Freundschaftspass-Seite anmelden, um nach ihrem Pass zu schauen.

So ein Passwort ist leicht vergessen oder falsch eingetippt – gerade wenn es schnell gehen muss.

Glücklicherweise wurde für solche Situationen die “Passwort vergessen”-Funktion erfunden. Und wie gewohnt erhält man damit eine E-Mail mit einem Passwort-Reset-Link. Ungewohnt ist allerdings, dass dieser Link auf eine Fehlerseite führt:

Screenshot einer 404-Fehlerseite: 'deutschebahn-sncf-form.vercel.app might be available. Click here to learn how to assign it to a project'

Wenn wir uns die Fehlerseite genauer ansehen, stellen wir fest, dass hier auf ein Vercel-Projekt verlinkt wurde, das nicht registriert ist. Bei Vercel handelt es sich um einen Cloud-Anbieter, bei dem man seine Webanwendungen hosten kann. Diese Anwendungen sind dann unter <Anwendungsname>.vercel.app verfügbar. Ist, wie hier, jedoch keine mit dem entsprechenden Namen registiert, können sich die Adresse grundsätzlich alle unter den Nagel reißen.

Und wer das tut und eine solche Anwendungs-Adresse quasi kapert, könnte damit beispielsweise gezieltes Phishing betreiben. Zum Glück war es eine ehrliche Person, die den Fehler gefunden hat, die Anwendung selbst registrierte und den Verantwortlichen und uns Bescheid sagte. So führt der Link zur “Passwort vergessen”-Funktion nur zu einer harmlosen Testseite. Die gehörte dann zwar nicht den Anbietern des Freundschaftspasses, aber immerhin einer freundlich gesonnenen Person und keinen Kriminellen.

Station 3: Freitickets-für-alle-Allee

Damit kommen wir zur nächsten Station unserer Reise: Dem Bestellvorgang.

Da nur 30.000 Pässe vergeben werden sollten, muss das Bestellsystem genau darauf aufpassen. Sobald also die 30.001te Person versucht sich anzumelden, müsste das System einen Fehler anzeigen und keine Anmeldung mehr erlauben. Doch das war hier nicht der Fall: Stattdessen hat man sich – nachdem die ersten 30.000 Menschen angefangen hatten, das Bestellformular auszufüllen – schlicht dafür entschieden, das Buchungsformular nicht mehr anzuzeigen. Auf der Website prangt nun ein Hinweis, dass alle Pässe vergeben sind.

Doch wer das Formular angefangen hat, war ja noch lange nicht fertig. Also hat man eine Hintertür eingebaut: Wer bereits einen Bestellvorgang begonnen, aber nicht abgeschlossen hatte, bekam eine E-Mail mit einem speziellen Link. Über diesen konnte man dann den Bestellvorgang abschließen.

Nur ein klitzekleines Problemchen gab es dabei:

Mit einem einfachen Kommando konnte man sich diesen Code selbst generieren – auch ohne den Bestellvorgang rechtzeitig begonnen zu haben.

Im Video sehen wir, wie zuerst links ein neuer Code generiert wird. Der wird dann rechts in den Browser kopiert, in dem sich daraufhin das Bestellformular des Freundschaftspasses öffnet. Dieses wird ausgefüllt und am Ende sieht man die Bestätigung, dass die Anmeldung für den Freundschaftspass erfolgreich war.

Ein paar Stunden später kam dann der Interrail-Pass per E-Mail.

Das Ganze ist also in etwa so, als hätte man eine Truhe voller Gold – und hängt an die Vordertür ein Schild: “Alles Gold ist bereits verteilt”. Die Hintertür bleibt aber sperrangelweit offen.

Wer genau wissen will, wann was passiert ist, findet am Ende des Artikels eine Timeline.

Umleitung über Meldungen

Der Modellzug mit den französischen und deutschen Fähnchen biegt ab, da vor ihm eine Baustelle mit Baggern liegt.

Natürlich wollten wir die Probleme schnell an die zuständigen Stellen melden, damit sie behoben werden. Das gestaltete sich leider schwieriger als gedacht: Ein Sicherheits-Kontakt war nicht zu finden.1

Also haben wir uns an alle Stellen gewandt, die so wirkten, als könnten sie hier etwas ändern: Die Werbeagentur, die im Impressum des Freundschaftspasses steht. Das IT-Sicherheits-Team der Bahn. Die Kontaktadresse aus dem Impressum des Freundschaftspasses. Den Bürgerservice des Verkehrsministeriums. Und schließlich auch das CERT-Bund. An all diese Adressen haben wir eine Beschreibung der Lücke geschickt und um eine Eingangsbestätigung sowie nächste Schritte gebeten.

Leider bekamen wir darauf zunächst keine Antwort. Einen Tag später war immerhin der Extra-Zugang nicht mehr verfügbar. Auch hier kam der Hinweis, das alle Tickets bereits vergeben wären.

Bildlich gesprochen wurde also auch die Hintertür mit einem Hinweis versehen: “Leider sind alle Goldbarren schon verteilt.”

Station 4: Wo die wilden APIs wohnen

Was aber niemand gemacht hat: Die Türen auch wirklich zugeschlossen. Denn wenn man direkt Anfragen an die Bestell-Schnittstelle geschickt hat, konnte man weiter Pässe generieren.

Und wenige Minuten später kam dann auch ein Pass per E-Mail:

Screenshot der Bestätigung des Freundschaftspass mit dem Code, um den Pass in der App einzulösen

Station 5: “Wir sind leider außerplanmäßig zum Halten gekommen…”

Langsam wussten wir nicht mehr, was wir machen sollten.

Also haben wir die Köpfe zusammengesteckt und überlegt. Wir wussten, dass alle Verantwortlichen nur auf die Pressestelle von Eurail (also der Firma hinter allen Interrail-Pässen, die wohl beauftragt war, auch diese Pässe zu vergeben) zeigten – und diese behauptete, es gäbe keine Lücke. Also haben wir der Pressestelle nochmal unsere Beschreibung geschickt.

Daraufhin – und nach nachdrücklichen Nachfragen auf anderen Wegen – bekamen wir endlich eine Antwort. Eurail dankte uns für den Hinweis, man habe die Lücke gestopft und suche jetzt nach den fälschlich herausgegebenen Pässen.

Wie Eurail die Lücke geschlossen hat

Das Backend für die Bestellseite läuft auf Supabase. Die API wird dabei mittels PostgREST umgesetzt.

Praktischerweise bietet PostgREST gleich automatische API-Doku mit an. So können wir leicht nachvollziehen, was sie wirklich geändert haben.

Und diese Änderung war tatsächlich relativ klein:

Alle Funktionen des Buchungsprozesses haben jetzt einen neuen Parameter secret_key. Wer diesen Parameter nicht angibt, kann die Funktionen gar nicht erst aufrufen.

{
  "code": "PGRST202",
  "details": "Searched for the function public.step_eligibility with parameters accepted_conditions, accepted_privacy, birth_date, last_step_completed, residence_country, user_session_code or with a single unnamed json/jsonb parameter, but no matches were found in the schema cache.",
  "hint": "Perhaps you meant to call the function public.step_eligibility(accepted_conditions, accepted_privacy, birth_date, last_step_completed, residence_country, secret_key, user_session_code)",
  "message": "Could not find the function public.step_eligibility(accepted_conditions, accepted_privacy, birth_date, last_step_completed, residence_country, user_session_code) in the schema cache"
}

Wer einen falschen secret_key angibt, bekommt eine Fehlermeldung:

{
  "code": "P0001",
  "details": null,
  "hint": null,
  "message": "Not allowed"
}

Was mit dem richtigen secret_key passieren würde, konnten wir noch nicht ausprobieren – denn den kennen wir (zum Glück) nicht.

Jetzt wurden also endlich sowohl die Vordertür als auch die Hintertür abgeschlossen.

Nur um dass nochmal ganz klar zu betonen: Eine API-Doku zu veröffentlichen, ist keine Sicherheitslücke! Im Gegenteil sollte es eigentlich gute Praxis sein, das zu tun. Denn eine Software wird nicht dadurch sicherer, dass niemand genau weiß, wie sie funktioniert.

Deine Haustür wird ja auch nicht dadurch sicherer, dass du sie in genau dem gleichen Farbton wie deine Hauswand anmalst, sodass sie nicht so leicht zu sehen ist. Das sieht sicher lustig aus und verwirrt erstmal – aber sicherer wird die Tür, indem du sie abschließt.

Also abschließen, nicht verstecken! 🤝

Station 6: “Datenleck mit 245.000 Datensätzen heute von Gleis 2 – direkt gegenüber”

Zwei Modellzüge stehen am Bahnsteig, aus dem Zug auf der gegenüberliegenden Seite fallen aus den offenen Türen geschredderte Papierschnippsel.

Doch diese unverschlossenen Türen waren nicht die einzigen, die wir gefunden haben. Nachdem die Lücke geschlossen wurde, sind wir zu benachbarten Projekten des Auftraggebers spaziert und fanden noch ein ähnliches Programm wie den Freundschaftspass: DiscoverEU.

Dieses Programm entstand bereits 2018 in Folge der #FreeInterrail-Kampagne. Hierbei können sich 18-Jährige anmelden, um einen kostenlosen Interrail-Pass zu gewinnen und Europa zu bereisen.

Während der Freundschaftspass von der deutschen und der französischen Regierung erdacht wurde, war bei DiscoverEU die Europäische Kommission am Werk. Beide Angebote haben aber eine entscheidende Gemeinsamkeit: Umgesetzt wurde dieses Projekt von den gleichen Agenturen – MCI zusammen mit Caracal.

Informationen zu DiscoverEU gibt es auf start-discover.eu. Da haben wir aber gar nicht viel Zeit mit Umschauen verbracht. Denn durch die Certificate Transparency Logs haben wir relativ schnell ein Dashboard unter dashboard.start-discover.eu entdeckt. Die Domain klingt spannend – wir werden aber nur mit einem Login-Screen begrüßt.

Technisch ist die Seite allerdings ähnlich gebaut wie der Freundschaftspass – und damit kannten wir schon die API, um einen Account anzulegen. Auf gut Glück haben wir uns auf diese Weise einen Account erstellt und uns damit auch noch erfolgreich angemeldet. Mit großer Sorge vor der jetzt folgenden Arbeit stellten wir fest:

In dem Portal waren 245.971 Registrierungen für DiscoverEU abrufbar. Dabei wurden folgende Daten angezeigt:

  • Name der Person
  • E-Mail-Adresse
  • Herkunftsland
  • Zustand ihrer Anmeldung
  • Art des Tickets
  • Interrail-Bestellnummer
Screenshot des Dashboard: Zu sehen eine Tabelle mit den Spalten 'A. Code', 'G.Code', 'E-mail', 'First name', 'Last name', 'Status', 'Special type', 'Type', 'County'

All diese persönlichen Daten lagen quasi offen im Internet und waren mit wenig Aufwand und Vorwissen abrufbar.

Technische Details

Die nötigen Schritte, um die Lücke auszunutzen, waren:

  1. Mit dem folgenden curl-Kommando einen Account anlegen. [EMAIL] und [PASSWORD] müssten dabei durch eine E-Mail-Adresse und ein Passwort ersetzt werden:

    curl --request POST \
      --url https://zaymuaqytesywmkspxqk.supabase.co/auth/v1/signup \
      --header 'Content-Type: application/json' \
      --header 'apikey: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6InpheW11YXF5dGVzeXdta3NweHFrIiwicm9sZSI6ImFub24iLCJpYXQiOjE2Njk5OTM3NTgsImV4cCI6MTk4NTU2OTc1OH0.JraN96zZgiM5iOkWFqNmQYWZG2jS7kTeEaYS6eW8xj4' \
      --data '{
        "email": "[EMAIL]",
        "password": "[PASSWORD]"
    }'
    
  2. Nach ein paar Sekunden kommt eine Bestätigungs-Email. Auf den darin enthaltenen Link klicken:

    Screenshot der Bestätigungs-Email: 'Follow this link to confirm your user.'
  3. Auf https://dashboard.start-discover.eu mit dem Account einloggen.

  4. Man kann alle Registrierungen einsehen.

Erneuter Halt in Meldungen

Der Postwaggon des Modellzugs, in der geöffneten Tür ein überdimensionales Papier mit der Aufschrift ZER - Report

Völlig schockiert über dieses Datenleck haben wir nochmal unser digitales Briefpapier ausgepackt und ganz schnell gemeldet: An die Agentur, die Kontakt-Adresse von Start-Discover.eu, das CERT-EU und die für DiscoverEU zuständige Stelle der Europäischen Kommission. Außerdem kannten wir zum Glück schon die richtige Ansprechperson in der Pressestelle von Eurail, die das ganze dann schnell an die zuständigen Personen weiterverteilte.

Die reagierten dann auch flott und nach weniger als einer Stunde war die Lücke geschlossen – man hat einfach die Anmeldung deaktiviert. Außerdem, teilte man uns mit, wolle man nun einen externen Sicherheitstest durchführen lassen und die nötigen Schritte, die in der DSGVO bestimmt sind, gehen.

Fazit – Dieser Zug endet hier

Freundschaftspass und DiscoverEU sind eigentlich tolle Ideen: Der Staat schenkt jungen Menschen Bahntickets, um ihre Nachbarländer kennenzulernen. Doch statt einfach möglichst vielen Menschen einen schönen Sommerurlaub und neue Bekanntschaften zu ermöglichen, entstand dabei leider wieder einmal ein Digital-Desaster.

Der Freundschaftspass ist außerdem ein gutes Beispiel für entstehende Macht-Ungleichgewichte durch schlechte digitale Lösungen: Wer genug technische Kenntnisse hat, bekommt auch dann noch einen Pass, wenn eigentlich schon alle vergeben sind. Alle anderen gehen leer aus oder verlieren sogar ihre Accounts.

Solche halbgaren Lösungen wären schon unzureichend, wenn ein Ticketanbieter ein paar Konzertkarten verkaufen will. Doch wenn ein Bundesministerium sich hinstellt und Zugtickets verschenkt, dann muss nochmal besonderes Augenmerk darauf gelegt werden, dass alles gut getestet ist.

Noch schlimmer wird es dann, wenn wir nicht nur neue Pässe anlegen, sondern sogar mehr als 245.000 Datensätze des DiscoverEU-Programms abrufen können. Wir sagen mal wieder: Wenn eine Website marktreif genug ist, um Daten zu verarbeiten, muss sie auch reif genug sein, diese für sich zu behalten.
Es ist erschütternd, dass hier so nachlässig gearbeitet wurde – und das anscheinend einfach so hingenommen wird.

Wir können nur hoffen, dass wir wirklich die Ersten waren, die diese Lücke entdeckt haben – und die Daten nicht schon längst von weniger wohlwollenden Websurfern weggeschafft wurden.

Timeline

Alle Zeiten sind CEST.

  • 2023-06-12 10:00 Uhr – Beginn des Verkaufs
  • 2023-06-12 – Nicht registrierte Vercel-Anwendung wird gefunden und gemeldet
  • 2023-06-13 23:00 Uhr – Wir finden die Hintertür
  • 2023-06-14 01:15 Uhr – Meldung an DB-CSIRT, MCI, Kontakt aus Impressum, BMDV, CERT-Bund
  • 2023-06-14 22:00 Uhr – Wir bekommen Freundschaftspässe an Test-Adressen
  • 2023-06-15 00:00 Uhr – Wir schicken die Meldung auch an Eurail
  • 2023-06-15 10:00 Uhr – Wir stellen fest, dass Hintertür auch durch Hinweis ersetzt ist
  • 2023-06-15 13:00-14:00 Uhr – Wir können verifizieren, dass Pässe weiter über API registrierbar sind
  • 2023-06-15 17:30 Uhr – Antwort von Eurail, dass die Lücken geschlossen wurden
  • 2023-06-16 13:00 Uhr – Wir entdecken die Lücke bei Start-Discover.eu
  • 2023-06-16 15:30 Uhr – Wir melden die Lücke
  • 2023-06-16 18:50 Uhr – MCI schreibt uns, dankt für die Meldung, um 16:20 Uhr sei die Lücke geschlossen gewesen

🤝

Wir haben unsere Funde mit Eva Wolfangel für ZEIT Online geteilt. Ihren Artikel findet ihr hier.

Ursprünglich wollten wir unsere Erkenntnisse am Freitag, 16.06.2023, veröffentlichen. Weil wir dann aber noch die Probleme bei DiscoverEU gefunden, gemeldet und abgewartet haben, bis alles abgesichert war, haben wir die Veröffentlichung verschoben.

Das Dokumentieren, Melden und Veröffentlichen solcher Lücken kostet Nerven und Zeit - die wir auch viel lieber mit Aus-dem-Zugfenster-Gucken verbringen würden. Wir machen das alles ehrenamtlich und in unserer Freizeit.

Wenn ihr uns unterstützen wollt, findet ihr hier Möglichkeiten: https://zerforschung.org/unterstuetzen/


  1. Wir empfehlen jedem Unternehmen, auf allen Webseiten einen Security-Kontakt und ggf. weitere Informationen zum Melden von Sicherheitslücken zu veröffentlichen. Am einfachsten geht das über eine security.txt. Dies würde (nicht nur) uns helfen, solche Lücken ohne Umwege an die richtigen Stellen in Unternehmen melden zu können. Mehr dazu findet ihr hier↩︎