Zum hauptinhalt springen

Deine ganze Stipendiums­bewerbung - in einem Tweet zerlegt

Ein Studium kostet viel Geld – zu viel für einige Menschen. Deshalb gibt es Stipendien als Unterstützung. Eigentlich eine gute Sache, jedenfalls so lange bei der Bewerbung alles gut läuft. Das mussten wir erfahren, als Teile unseres zerforschungs-Kollektivs sich für ein Stipendium bewerben wollten - und leider auch gleich wieder über einen ganzen Batzen persönlicher Dokumente von anderen Bewerber*innen gestolpert sind.

Aber von vorn: …

Es war einmal ein kleiner Teil von zerforschung, auch ein kleines zerforschi genannt. Eines Tages überlegte das kleine zerforschi, ob es durch ein Stipendium bei seinem Studium unterstützt werden könnte.

Also machte es sich auf die Suche nach geeigneten Institutionen, die Stipendien vergeben, und wurde schließlich fündig. Und oh Wunder: Bewerben konnte es sich sogar vollständig online! Das kleine zerforschi freute sich sehr, dass die sogenannte Digitalisierung Früchte trägt und es im Jahr 2021 bereits diese Annehmlichkeiten genießen konnte.

Dann stieß das zerforschi allerdings auf die ersten Hindernisse: Die Infos zur Bewerbung waren nicht offen zugänglich, man musste sich dafür erst registrieren, was es natürlich sofort tat. Doch große Enttäuschung nach der Registrierung: Mehr Informationen gab es nicht wirklich.

Kein zerforschi forscht alleine

An dieser Stelle sind wir dem kleinen zerforschi zu Hilfe geeilt und haben die Recherche aufgenommen.

Die erste Frage, die sich uns stellte: Wer baut so ein Bewerbungs-Portal? Entwickelt wird das Portal von einer Firma namens “javengo”, die – wie es sich für ein extrem hippes Start-Up gehört – in einem WeWork sitzt. Javengo hat schon “Online Genussrechte Service"s und Gutschein-Management-Tools entwickelt. Und nun eben Bewerbungs-Software.

Diese Software bauen sie aber nicht nur für eine Stiftung, sondern verkaufen sie als SaaS-Lösung gleich an eine ganze Reihe Institutionen: Stiftung der deutschen Wirtschaft, Rosa-Luxemburg-Stiftung, Children for a better World e.V. und Avicenna-Studienwerk. Die folgenden Beschreibungen treffen also nicht nur auf eines dieser Bewerbungsportale zu, sondern gleich alle! 😱

Und die zweite Frage war: Wie ist die Seite gebaut? Wagen wir doch einen Blick in die Entwicklungs-Tools unseres Browsers.

Wir schauen uns ein wenig um und stellen fest: Das Bewerbungsportal ist mit dem Meteor-Framework gebaut, ein JavaScript-Framework zur schnellen Webentwicklung.

☄️ Meteor?? Wir sind doch keine Dinos 🦖

Meteor verspricht, die Entwicklung von Webapps zu beschleunigen, indem es auf dem Server und im Browser die gleiche Codebase laufen lässt. Um zwischen Webbrowser und Server zu kommunizieren, werden Funktionen definiert, die der Client auf dem Server aufrufen kann. Meteor nennt diese explizit “Methods” (das große M ist wichtig), um sie von Funktionen abzugrenzen, die nur auf dem Client oder nur auf dem Server laufen würden.

Möchte der Browser auf dem Server die Methode MethodyMcMethodenface1 aufrufen und den Wert 1000 übergeben, würde folgender kurze Code reichen:

Meteor.call("MethodyMcMethodenface", 1000);

Das Konzept ist damit klar, für die Entwickler*innen ist es sicher praktisch - aber welche Methoden werden in diesem Bewerbungsportal überhaupt aufgerufen?

Vertrackte E-Mail-Verifikation 📧

Die erste ist setEmailVerifiedByToken, die zur Bestätigung der E-Mail-Adresse genutzt wird.

Denn wie in den meisten Web-Portalen üblich, muss man bei der Registrierung eine E-Mail-Adresse angeben. Und wie ebensfalls üblich, wird dann an diese E-Mail-Adresse ein Link gesendet, mit dem sich die Adresse bestätigen lässt.

Die in der E-Mail verlinkte Seite ruft dann setEmailVerifiedByToken mit einem speziellen Zugangscode (Token) auf, der in den Link einkodiert ist. Das heißt also, dass in der URL der Zugangscode steht und weil das ja ausschließlich über die E-Mail bekannt ist, kann man dadurch die E-Mail-Adresse verifizieren: Denn nur, wer die E-Mail bekommen hat, kennt die URL mit dem richtigen Token.

Damit dieser Verifikationsprozess auch funktioniert, darf dieser Link nicht erratbar oder über andere Wege als diese E-Mail zugänglich werden.

Doch betrachten wir den Netzwerkverkehr während des Registrierungsprozesses in den Entwicklungstools, fällt uns eine Antwort des Servers auf: Nachdem wir unseren Account registriert haben, sendet uns der Server das users-Objekt zu, also alle Informationen die der Server über unseren Account hat.

{
    "_id": "████████████████",
    "createdAt": {
        "$date": 1630055000000
    },
    "services": {
        "password": {
            "bcrypt": "$2a$10$██████████████████████.██████████████████████████████"
        },
        "email": {
            "verificationTokens": [
                {
                    "token": "████████████████████████████████████████████████",
                    "address": "text@example.com",
                    "when": {
                        "$date": 1630055000000
                    }
                }
            ]
        },
        "resume": {
            "loginTokens": [
                {
                    "when": {
                        "$date": 1630055000000
                    },
                    "hashedToken": "████████████████████████████████████████████████"
                },
                "..."
            ]
        }
    },
    "emails": [
        {
            "address": "test@example.com",
            "verified": false
        }
    ],
    "profile": {
        "avatar": "/images/avatar.jpg",
        "lastLogin": "2021-08-██T██:██:██+02:00"
    },
    "username": "test@example.com",
    "_searchable": {
        "fullName": "test@example.com"
    }
}

Und darin steht, neben unserer E-Mail-Adresse und unserem (gehashten) Passwort, auch der Zugangscode für die E-Mail-Verifikation 🤦

Aus diesem können wir den Bestätigungs-Link, der in der E-Mail steht, rekonstruieren und so den Account aktivieren.

Das bedeutet nun aber auch: Wir können Accounts für beliebige E-Mail-Adressen anlegen – und ohne Zugriff auf diese sogar bestätigen.

Die Ausgabe des E-Mail-Verifikationscodes lässt sich mit einem kleinen Stück JavaScript-Code automatisieren, das sogar kürzer als ein Tweet ist:

Meteor.call("setEmailVerifiedByToken", Meteor.user().services.email.verificationTokens[0].token, console.log)

Gib mir deine Sammlung!

Dabei ist uns aber noch mehr aufgefallen: eine weitere Meteor-Methode namens pagedQuery. Mit dieser kann man direkte Anfragen an die serverseitig angeschlossene Datenbank (in diesem Fall eine MongoDB) stellen.

In dieser Datenbank stehen alle im Portal gespeicherten Daten, der Zugriff darauf muss also streng geschützt werden. Meteor empfiehlt, hier spezifische Methoden zu schreiben, welche die Zugangsberechtigungen prüfen und nur die wenigen, wirklich zum Abruf bestimmten Informationen bereitstellen. Insbesondere wird darauf hingewiesen2, dass die Ein- und Ausgaben der Methoden gut kontrolliert werden müssen.

Doch wurde dieser wichtige Security-Hinweis von den Programmier*innen beachtet?

Nein, leider nicht 😞: pagedQuery tut nichts davon. Die normale Website fragt stets nur Daten an, auf die der aktuelle Account Zugriff haben darf. Lässt man diese Filter jedoch weg, so erhält man keine Fehlermeldung, sondern einfach alle Dokumente in der jeweiligen Collection3.

Kennt man den Namen einer Collection, passt auch hier der Code, um diese Lücke auszunutzen wieder in einen Tweet:

Meteor.call("pagedQuery", "COLLECTION_NAME", 0, 100, {}, (function(_, n) { console.log(n.data); }))

Und spannende Collections gibt es genug:

  • in Meteor.users werden die Accounts gespeichert, inklusive E-Mail-Adresse und Passwort-Hash
  • in PageObjects werden die Bewerbungen, Anträge etc. gespeichert. Inklusive aller dort gemachten Angaben, wie
    • Art des Stipendiums
    • Universität, Studiengang
    • Namen
    • Telefonnummern
    • Adressen (Sowohl E-Mail als auch Post 🐌)
    • Bankdaten (IBAN, auf die das Stipendium überwiesen werden soll)
    • Informationen über den Studien-/Promotionsverlauf
    • Informationen über Jahresgespräche
    • sämtliche weiteren im Rahmen des Bewerbungs- und Förderungsprozesses gemachten Angaben
  • in S3Documents werden Metadaten und URLs zu den im Portal hochgeladenen Dokumenten (PDFs, Bilder) gespeichert

Wir 🤝 Amazon-Server

Gerade diese letzte Collection ist besonders interessant. Das System von Javengo bietet “sichere[sic!] Dokument-Uploads”4. Über diese Funktion können alle Dokumente zu einem Antrag digital hochgeladen werden. “Ersparen Sie sich und Ihren Bewerbern/Antragsstellern die Erstellung von Unterlagen in Papierform”5, wirbt das Unternehmen auf seiner Website.

Die hochgeladenen Dokumente werden bei Amazon S3 gespeichert. Doch um sie abzurufen, gibt es einen eigenen Endpoint auf dem Javengo-Server: /documents/{ID}?hashedToken={TOKEN}.

Man benötigt also die ID des Dokuments und einen Zugangscode (TOKEN) namens hashedToken. Die ID steht in der Collection S3Documents und den hashedToken bekommen wir nach dem Login – er ist also account-spezifisch vergeben. Somit weiß der Server, welcher Account gerade die Daten abruft und sollte den Zugriff nur bestimmten Personen erlauben.

Doch bauen wir uns wieder einen tweetkompatiblen One-Liner, um das zu prüfen:

Meteor.call("pagedQuery", "S3Documents", 0, 10, {}, {"createdAt":-1}, (function(_, n) { console.log(n.data.map((d) => {return location.protocol + "//" + location.host + api.files.buildAuthURL(d)})); }))

Dieser erzeugt eine Liste von URLs nach dem gerade beschriebenen Schema. Klicken wir nun auf eine der URLs, sehen wir jedoch keine Fehlermeldung, sondern ein fremdes Schulzeugnis.

Die Daten quillen aus allen Öffnungen

In den Daten aus der Collection steht nicht nur eine ID, mit der wir das Dokument über das Portal abrufen können. Bei den meisten Dokumenten ist zusätzlich ein Direkt-Link zum Amazon S3-Speicher hinterlegt. Üblicherweise ist dieser Direktzugriff nur dem Portal-Server erlaubt, alle anderen müssen spezielle URLs benutzen, die nur vorrübergehend gültig sind. Doch bei den Daten der Stiftung der deutschen Wirtschaft wurde diese grundlegende Sicherungsmaßnahme nicht getroffen. Der in der Collection stehende Link lieferte in vielen Fällen die Dateien ohne Zugriffskontrolle an uns aus.

Das heißt die Dateien waren über zwei verschiedene Wege abrufbar.

Und beide Wege wären einfach vermeidbar gewesen: Das Portal selbst hätte eine Zugriffskontrolle für die Dateien implementieren müssen und auch Amazon S3 lässt sich so konfigurieren, dass direkte Zugriffe auf die Dateien unterbunden werden.

All your data are belong to us

Über die 4 Portale von Stiftung der deutschen Wirtschaft, Children for a better World e.V., Rosa-Luxemburg-Stiftung und Avicenna-Studienwerk hinweg waren damit eine ganze Menge an Daten betroffen:

  • ~350.000 Dokumente mit teils persönlichsten Daten (Immatrikulationsbescheinigungen, Empfehlungsschreiben, Zeugnisse, Ausweiskopien)
  • ~39.500 Bewerbungen und Anträge
  • ~53.500 Nutzer*innen-Accounts

Objekte in der Datenbank verändern

Beim weiteren Stöbern durch den Quellcode entdecken wir noch eine weitere Methode: updateObjectSet. Mit dieser kann man gezielt einzelne Objekte in der Datenbank ändern.

Ähnlich wie beim Lesen der Daten aus der Datenbank muss so ein API-Call gut geschützt werden, sodass nur bestimmte Daten und diese nur von berechtigten Accounts verändert werden können.

Doch dies wurde hier wieder nicht beachtet. Stattdessen konnte man beliebige Dokumente in der Datenbank auf beliebige Art verändern. Hier nur mal eine kurze Liste, was damit alles möglich wäre:

  • das Passwort eines fremden Accounts ändern
  • die in einem Account hinterlegte IBAN ändern um das Stipendium abzugreifen

Und wem das alles zu kompliziert war, der konnte einfach den eigenen Account zum Admin machen:

user = Meteor.user(); user.roles.push("admin"); Meteor.call("updateObjectSet", "Meteor.users", user._id, user, console.log)

Damit kann man sich dann im Admin-Portal der jeweiligen Instanz einloggen und ganz bequem durch alle Daten browsen.

Alarm, CERT-Bund, Alarm! 🚨

Nachdem wir die Sicherheitslücke fanden, haben wir sie feinsäuberlich dokumentiert und an die zuständigen Behörden (CERT-Bund und LfDI Berlin) sowie einen persönlichen Kontakt bei der Rosa-Luxemburg-Stiftung gemeldet.

Die Behörden verifizieren die Meldungen und gehen dann auf das betroffenen Unternehmen zu, damit die Lücken schnellstmöglich geschlossen werden können.

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.

Es wundert uns schon sehr, dass ein Unternehmen, das seit 2009 besteht, solch grobe Fehler wie bei der Stipendiums-Bewerbungs-Software machen kann.

Weg vom Netz und reparieren

Das Unternehmen hat relativ schnell reagiert und am nächsten Tag erstmal die Portale offline genommen. Danach haben sie nach eigener Aussage die Lücken geschlossen und danach das jeweilige Portal wieder online geschaltet. Zudem haben sie die Landesdatenschutzbehörde von Berlin informiert (was sie nach DSGVO auch innerhalb von 72h nach Bekanntwerden der Lücke müssen) und die betroffenen Stiftungen und Studienwerke informiert.

Diese haben teilweise die Stipendiat*innen informiert. Die E-Mail der Rosa-Luxemburg-Stifung liegt uns vor – und wir müssen diese leider scharf kritisieren. In der E-Mail schreiben sie:

[…] Unter Ausnutzung der Sicherheitslücke wäre eine Einsicht in die hinterlegten personenbezogenen Daten möglich gewesen. […] Laut Betreiber konnte nach Analyse kein Datenabfluss durch Ausnutzung der Schwachstelle festgestellt werden.

Wie der Dienstleister keinen Datenabfluss festgestellt haben will, wenn wir zu Testzwecken auf Daten zugegriffen haben, erschließt sich uns nicht. Auch dass nicht genannt wird, welche Daten betroffen waren, sehen wir kritisch: Hier waren nicht nur E-Mail-Adressen und (gehashte) Passwörter betroffen, sondern auch

  • Name
  • Informationen über das Studium
  • Empfehlungsschreiben
  • Bankverbindungen
  • E-Mail- und physikalische Adressen
  • Zeugniskopien
  • Ausweiskopien
  • u.v.m.

Diese schlechte Informationspolitik ist besonders zu verurteilen, da es sich bei den Daten teilweise um besonders schützenswerte Daten nach Art. 9 DSGVO handelt, nämlich um Daten zur politischen (bei der Rosa-Luxemburg-Stiftung) und religiösen (bei Avicenna) Überzeugung.

Hier hat die Rosa-Luxemburg-Stiftung einen Nachteil - von den anderen Institutionen wissen wir nicht ob sie überhaupt informiert haben und wenn ja mit welchem Text. Solltet ihr eine solche E-Mail bekommen haben, freuen wir uns über eine Weiterleitung an hallo@zerforschung.org.

Zugabe nach der Zusage

Wenn uns ein Unternehmen meldet, die Lücken geschlossen zu haben, schauen wir meist nur nochmal oberflächlich nach. Als wir dies bei Javengo taten sah es auch gut aus: Die von uns beschriebenen Lücken waren nicht mehr ausnutzbar.

Als wir uns allerdings während des Schreibens dieses Artikels ein paar Sachen nochmal genauer anschauten, staunten wir nicht schlecht: Viele Stiftungen betreiben ein weiteres “Self-Service-Portal” für Stipendiat*innen, auf dem diese nach der Förder-Zusage weitere Informationen finden. Und dort waren die Lücken immer noch ausnutzbar.

In diesem konnte man sich zwar nicht neu registrieren, wer allerdings bereits einen Account hatte, konnte sich weiter einloggen. Und noch viel schlimmer: Neben dem Self-Service-Portal gibt es ja auch das normale Bewerbungs-Portal. Und dort kann man sich auch weiterhin einen Account erstellen, welcher dann auch im Self-Service Portal gültig ist.

Again: Lücken stopfen

Reichlich genervt haben wir schnellstmöglich erneut eine Meldung an das CERT-Bund und die LfDIs geschrieben. Diese sind dann am nächsten Morgen erneut auf das Unternehmen zugegangen. Während wir diesen Artikel schreiben, kann man sich einfach gar nicht mehr auf dem Self-Service-Portal anmelden. Also ist damit diese Lücke nun auch einigermaßen geschlossen.

Fazit

Dieser Fall ist leider mal wieder einer, bei dem extrem viele Daten betroffen sind und noch dazu besondes schützenswerte: Daten welche einen weitreichenden Identitätsdiebstahl ermöglichen.

Auch wenn die Stiftungen das teilweise behaupten, wurde hier anscheinend eben nicht genau auf die IT-Sicherheit geachtet. Schon bei oberflächlichem Dagegentreten - denn mehr machen wir ja auch oft nicht - wären solche Fehler eigentlich aufgefallen.

Damit gilt mal wieder: Wer ein Portal bereitstellt, welches große Mengen Daten verarbeitet, insbesondere so sensible, muss dieses sehr gut schützen. Dabei darf die Verantwortung nicht auf Dienstleister verlagert werden. Wenn du eine Software für so einen Zweck einkaufst, schau sehr genau hin. Frag den Dienstleister nach einem Sicherheitskonzept, nach der genauen Ausgestaltung der technischen und organisatorischen Maßnahmen zum Schutz der Daten. Und frag ihn vielleicht nach einem Sicherheits-Audit bzw. den Ergebnissen eines Pentests.

Und wer eine solche Software schreibt, muss für die Sicherheit der darin geschriebenen Daten sorgen. Wenn die Software reif genug ist, um Kund*innen-Daten zu speichern, muss sie diese auch für sich behalten können. Wenn du nicht weißt wie sowas richtig geht (am besten auch wenn du glaubst es zu tun), such dir Hilfe. Beauftrage zum Beispiel ein externes Unternehmen, das die Sicherheitseinschätzung übernimmt.

Für Datensicherheit zu sorgen ist nun mal nicht nur eine moralische Verpflichtung, sondern auch einfach eine rechtliche: Die DSGVO wurde nicht zum Spass geschaffen, sondern legt wichtige Rechten und Pflichten zur Datenverarbeitung fest.

zerforschung hat vor kurzem einjährigen Geburtstag gefeiert. Wenn euch gefällt was wir machen, dürft ihr uns gern unterstützen.

  1. siehe Boaty McBoatface. Ja, wir erklären manche unserer Witze. ↩︎

  2. https://guide.meteor.com/security.html#attack-surface ↩︎

  3. MongoDB organisiert die Daten in sogenannten Collections, also Sammlungen ähnlicher Dokumente. So gibt es beispielsweise eine Collection, in der alle User*innen-Accounts gespeichert sind und eine andere mit allen Bewerbungen. In anderen Datenbanksystemen wären Tabellen wohl ein passendes Equivalent. ↩︎

  4. https://www.javengo.com/loesungen/bewerbung-stipendium/ ↩︎

  5. https://www.javengo.com/#contact-us ↩︎