Prüfung zum Registered Information Security Specialist – Frühjahr 2024 (Reiwa 6), Nachmittag, Aufgabe 1 erklärt – JWT alg=none, API-Autorisierung und vorläufige WAF-Abwehr

· · Registered Information Security Specialist, RISS-Prüfung, API, API-Sicherheit, JWT, Authentifizierung, Autorisierung, WAF, Log4Shell, Informationssicherheit, Schwachstelle, IPA

„Wir prüfen die JWT-Signatur, also lässt sich der Benutzer-ID vertrauen.“

Diese Aussage ist nur zur Hälfte richtig.

Aufgabe 1 der Nachmittagssitzung der Prüfung zum Registered Information Security Specialist, Frühjahr 2024 (Reiwa 6), dreht sich um eine von einer Smartphone-App aufgerufene API1. Nach erfolgreicher Authentifizierung wird ein JWT ausgestellt, und mit diesem JWT werden die APIs zum Abrufen und Aktualisieren der Benutzerinformationen aufgerufen. Auf den ersten Blick ein ganz gewöhnlicher Aufbau.

Doch die Prüfung deckt die folgenden vier Probleme auf.

  1. Ändert man im JWT-Header alg auf none, wird ein unsigniertes JWT akzeptiert
  2. Behält man ein gültiges JWT bei und ändert nur mid auf eine andere Benutzer-ID, lassen sich fremde Informationen abrufen und ändern
  3. Fügt man das nicht spezifizierte status=paid hinzu, wird aus einem kostenlosen ein zahlender Nutzer
  4. Der per E-Mail zugestellte vierstellige Authentifizierungscode lässt sich ohne Begrenzung der Versuchszahl per Brute-Force knacken

Alle vier wirken wie „Schwachstellen rund um die Authentifizierung“, doch die Ursache ist nicht dieselbe. Gebrochen werden die Integrität des Tokens, die Autorisierung auf Objektebene, die Autorisierung auf Eigenschaftsebene und die Begrenzung der Versuchszahl – vier verschiedene Grenzen.

In der zweiten Hälfte kommt noch ein weiteres Thema hinzu. In einer weit verbreiteten Open-Source-Bibliothek wird eine kritische Schwachstelle veröffentlicht, die es einem Angreifer erlaubt, über den Missbrauch von JNDI Lookup Code von außen auszuführen. Weder eine korrigierte Version noch eine fertige WAF-Regel existieren bislang. In der Zwischenzeit wird gefragt, wie sich die Auswirkung feststellen lässt, wo die WAF hinschauen soll und warum der WAF-Modus zunächst „Erkennung“ statt „Sperrung“ sein sollte.

Dieser Artikel stützt sich auf die offiziellen Musterlösungen2 und den Auswertungskommentar3 und ordnet nicht nur die Antwort zu jeder Teilaufgabe ein, sondern auch, warum das die Antwort ist und wie streng man das in der Praxis entwerfen sollte.

Gesamtbild der AufgabeZeigt die in jeder Phase gebrochene Vertrauensgrenze - Authentifizierungscode, JWT, API-Autorisierung, BibliotheksschwachstelleKeine VersuchsbegrenzungErlaubt alg=noneVertraut midstatus=paidJNDI/LDAP/HTTPNutzer-App4-stelliger Auth-CodeJWT-AusstellungBenutzer-APIProtokollausgabeVerwundbare BibliothekExterne CodeausführungFremde Daten lesen/ändernZahlungsstatus ändern

Abbildung 1: Gesamtbild der Aufgabe. In jeder Phase wird eine andere Vertrauensgrenze gebrochen.

1. Zunächst das Ergebnis

  • Dass eine RESTful API keinen Sitzungszustand hält, heißt zustandslos (stateless). Das bedeutet nicht, dass der Server überhaupt keine Datenbank oder keinen Nutzerzustand besitzt
  • Ein vierstelliger Authentifizierungscode hat 10.000 mögliche Werte. Bei 10 Versuchen pro Sekunde gelingt der Treffer im Durchschnitt nach 5.000 Versuchen, also 500 Sekunden – kürzer als die 10-minütige Gültigkeitsdauer, sodass die Ablaufzeit allein nicht schützt
  • Die Mindestmaßnahme gegen alg=none besteht darin zu bestätigen, dass das alg im JWT-Header nicht NONE ist. In der Praxis sollten Sie aber die Menge der erlaubten Algorithmen serverseitig festlegen
  • Auch bei einem gültigen JWT darf der mid der Anfrage nicht vertraut werden. Entweder wird die Benutzer-ID im JWT mit mid abgeglichen, oder – sicherer – mid wird vom Client gar nicht erst entgegengenommen und das Ziel aus dem JWT bestimmt
  • Das Hinzufügen von status=paid ist ein Problem vom Typ Mass Assignment, bei dem Eigenschaften außerhalb der Spezifikation direkt an das interne Objekt gebunden werden. Verwenden Sie das Aktualisierungs-DTO als Positivliste, und lassen Sie den Zahlungsstatus nie durch den Nutzer ändern
  • Die Musterlösung gegen Brute-Force lautet: Verarbeitung, die das Konto sperrt, sobald die Anzahl aufeinanderfolgender Fehlversuche einen Schwellenwert überschreitet. In der Praxis kommen gestaffelte Verzögerungen und quellenbezogene Kontrollen hinzu
  • Um die Auswirkung einer neu veröffentlichten kritischen Schwachstelle zu bestätigen, zeichnen Sie statt eines destruktiven Befehls Zugriffe auf die index.html eines Testservers auf, um zu bestätigen, dass externe Codeausführung tatsächlich erreicht wird
  • Da die Angriffszeichenfolge in einem HTTP-Header steckt, ist das Prüfziel der WAF Header. Als regulärer Ausdruck, der Groß-/Kleinschreibung berücksichtigt, eignet sich etwa \W[jJ][nN][dD][iI]\W
  • Der Vorteil, die WAF zunächst auf „Erkennung“ zu stellen, ist, dass eine Sperrung durch Fehlalarme verhindert wird. Bei einem Alarm wird geprüft, ob es sich um einen echten Angriff handelt, bevor nach der Feinabstimmung zur Sperrung gewechselt wird
  • Die WAF ist nur eine vorläufige Maßnahme; die grundlegende Abhilfe ist die Aktualisierung der betroffenen Bibliothek auf eine korrigierte Version

2. Wie das Szenario auf die Teilaufgaben abgebildet wird

Schauplatz ist Unternehmen G, das einen neuen Gesundheitsdienst startet. Nutzer geben über eine Smartphone-App Angaben wie Mahlzeiten und Körpergewicht ein und erhalten eine Einschätzung des Gesundheitsrisikos sowie Ernährungsempfehlungen. Das System läuft in der Cloud und kombiniert ein API-Gateway, ereignisgesteuerte Verarbeitung und eine verwaltete Datenbank.

Das Aufgabenheft abstrahiert konkrete Produkt- und Dienstnamen. Auch dieser Artikel gibt IPAs Abbildungen und Text nicht wortgetreu wieder, sondern paraphrasiert nur die für das Verständnis der Teilaufgaben nötige Struktur.

Teilaufgabe Thema Kapitel dieses Artikels
Teilaufgabe 1 Eigenschaft von RESTful APIs Kapitel 4
Teilaufgabe 2(1) Zeit zum Brute-Force des 4-stelligen Codes Kapitel 5
Teilaufgabe 2(2) JWT alg=none Kapitel 6
Teilaufgabe 2(3) Zugriff auf fremde Daten über mid Kapitel 7
Teilaufgabe 2(4) Das Leck, das ein nicht spezifiziertes status akzeptiert Kapitel 8
Teilaufgabe 2(5) Gegenmaßnahmen gegen Brute-Force Kapitel 9
Teilaufgabe 3(1) Sichere Bestätigung, dass eine Schwachstelle existiert Kapitel 11
Teilaufgabe 3(2)(3) Wo die WAF hinschaut, und der reguläre Ausdruck Kapitel 12
Teilaufgabe 3(4) Vorteil des Erkennungsmodus und dessen Betrieb Kapitel 13

Der Auswertungskommentar merkt an, dass die Gesamterfolgsquote durchschnittlich war. Er weist aber auch darauf hin, dass die Erfolgsquote bei der Gegenmaßnahme gegen JWT-Manipulation in Teilaufgabe 2(2) und beim für den Prüfserver nötigen Mechanismus in Teilaufgabe 3(1) etwas geringer war. Beides lässt sich nicht allein mit Vokabelwissen lösen. Man muss verfolgen, welchen Wert der Angreifer geändert hat, in welche Verarbeitung er einfließt und wo er letztlich vertraut wurde.

3. Dies ist nicht nur „ein Authentifizierungsproblem“

Legt man die gesamte Aufgabe nach Vertrauensgrenzen aus, ergibt sich Folgendes.

[Benutzer-ID / Passwort]
          |
          v
[Prüfung des 4-stelligen Codes] ---- keine Versuchsbegrenzung ----> Brute-Force
          |
          v
[JWT ausstellen]
          |
          v
[JWT-Bibliothek] ------- erlaubt alg=none ------> Manipulation der Benutzer-ID
          |
          v
[Benutzer-API]
    |             |
    |             +-- reicht status komplett durch ----> Autorisierungslücke auf Eigenschaftsebene
    |
    +-- vertraut mid -----------------------> Autorisierungslücke auf Objektebene

[Externe Eingabe protokollieren]
          |
          v
[Verwundbare Bibliothek] ---- JNDI/LDAP/HTTP ------> Externe Codeausführung

Die wichtigste Unterscheidung dabei ist diese:

Prüfung Beantwortete Frage In dieser Aufgabe gebrochenes Beispiel
Authentifizierung Wer sind Sie Brute-Force des 4-stelligen Codes
Tokenprüfung Wurde diese Identitätsinformation manipuliert alg=none
Autorisierung auf Objektebene Darf dieser Nutzer auf diese Daten zugreifen Austausch von mid
Autorisierung auf Eigenschaftsebene Darf dieses Feld geändert werden status=paid
Grenze von Eingabe zu Ausführung Wird externe Eingabe als Befehl interpretiert JNDI Lookup

Eine bestandene Prüfung ist niemals ein Grund, die nächste zu überspringen. Ein Nutzer mit einem gültigen JWT darf deshalb noch lange nicht die Daten eines anderen lesen. Ein Nutzer, der seine eigenen Daten aktualisieren darf, darf deshalb noch lange nicht auch seinen Zahlungsstatus ändern.

Trennt man diese Stufen sauber, wird die Antwort zu jeder Teilaufgabe zu etwas, das man nicht mehr auswendig lernen muss.

Der Unterschied zwischen Authentifizierung und AutorisierungAuthentifizierung bestätigt das Subjekt, Autorisierung bestätigt, was dieses Subjekt tun darfAuthentifizierungwer Sie sindAutorisierungwas Sie tun dürfen

Abbildung 2: Der Unterschied zwischen Authentifizierung und Autorisierung. Authentifizierung kommt zuerst, Autorisierung ist eine eigene Prüfung.

4. Teilaufgabe 1 – Was bedeutet „zustandslos“?

Teilaufgabe 1 fragt nach einem der Entwurfsprinzipien von RESTful APIs: der Eigenschaft, keine Sitzungsverwaltung durchzuführen.

Die Antwort lautet zustandslos (stateless).

Zustandslos bedeutet, dass der Server sich den Gesprächszustand der vorherigen Anfrage nicht merken muss, weil jede Anfrage für sich allein alle für die Verarbeitung nötigen Informationen mitbringt. In dieser Aufgabe hängt die Smartphone-App bei jeder Anfrage ein JWT an den Authorization-Header. Der Server prüft dieses JWT und bestimmt daraus den Nutzer dieser Anfrage.

Ein häufiges Missverständnis besteht darin, „zustandslos“ als „der Server hält überhaupt keinen Zustand“ zu lesen. Tatsächlich hält er normalerweise Folgendes:

  • Die Datenbank, die Benutzer- und Gesundheitsdaten speichert
  • Den Zahlungsstatus
  • Den Wert, die Gültigkeitsdauer und die Fehlerzahl des Authentifizierungscodes
  • Den Signaturschlüssel des JWT
  • Bei Entwürfen mit einer Widerrufsliste deren Widerrufsinformationen
  • Protokolle und Prüfnachweise

Was er nicht hält, ist ein serverseitiger Sitzungszustand, der nur dazu dient, ein Gespräch fortzusetzen, und den jeder API-Aufruf als Voraussetzung braucht.

Zustandslosigkeit erhöht die Sicherheit außerdem nicht automatisch. Das JWT bei jeder Anfrage mitzusenden erleichtert zwar horizontale Skalierung, aber ein Fehler bei der JWT-Prüfung breitet sich damit ebenso gleichmäßig auf alle Knoten aus. Eine architektonische Eigenschaft und sicherheitstechnische Korrektheit sind zweierlei.

5. Teilaufgabe 2(1) – Ein 4-stelliger Code fällt im Schnitt nach 500 Sekunden

Die Authentifizierungs-API sendet, sobald Benutzer-ID und Passwort übereinstimmen, eine vierstellige Zahl per E-Mail. Stimmen anschließend Benutzer-ID und der vierstellige Code überein, wird ein JWT ausgestellt. Der Code ist ab der Erzeugung 10 Minuten gültig.

Bei der Prüfung waren 10 Versuche pro Sekunde möglich. Gefragt ist, wie viele Sekunden im Durchschnitt bis zum Durchbruch nötig sind.

Die Rechnung ist „die Hälfte des Kandidatenraums“

Eine vierstellige Zahl hat, den führenden Nullwert eingeschlossen, die folgenden 10.000 Möglichkeiten.

0000, 0001, 0002, ... , 9999

Wird der richtige Wert gleichverteilt zufällig gewählt, erreicht ein Angreifer, der der Reihe nach ohne Wiederholung durchprobiert, den Treffer im Durchschnitt nach der Hälfte des Kandidatenraums.

Durchschnittliche Versuchszahl = 10.000 / 2 = 5.000
Durchschnittliche Zeit          = 5.000 / 10 Versuche pro Sekunde = 500 Sekunden

Also lautet Lücke b 500.

Im schlimmsten Fall dauert es bis zu 1.000 Sekunden, doch gefragt ist der Durchschnitt. Und die Gültigkeitsdauer des Codes beträgt 600 Sekunden – länger als die durchschnittliche Durchbruchszeit von 500 Sekunden. Deshalb wird beurteilt, dass „ein Durchbruch wahrscheinlich ist“.

Zeitgefühl für den 4-stelligen Authentifizierungscode10.000 Kandidaten bei 10 Versuchen pro Sekunde ergeben im Schnitt 5.000 Versuche und 500 Sekunden, kürzer als die 600-sekündige GültigkeitsdauerDurchschnitt 10.000 / 2 = 5.000 Versuche500 Sekunden < 600 Sekunden10.000 KandidatenDurchschnittliche Durchbruchszeit 500 SekundenGültigkeitsdauer 600 SekundenInnerhalb der Gültigkeitsdauer durchbrechbar

Abbildung 9: Zeitgefühl für den 4-stelligen Authentifizierungscode. Probiert man im Schnitt die Hälfte des Kandidatenraums durch, gelingt der Treffer innerhalb der Gültigkeitsdauer.

Nur die Ablaufzeit zu verkürzen, verliert bei einem kleinen Kandidatenraum trotzdem

Die Stärke eines Authentifizierungscodes bestimmt sich weder allein aus der Stellenzahl noch allein aus der Gültigkeitsdauer.

Mögliche Versuche innerhalb der Gültigkeitsdauer
= Versuche pro Sekunde x Gültigkeitsdauer
= 10 x 600
= 6.000 Versuche

Probiert man nicht wiederholte Werte der Reihe nach durch, lassen sich 60 % der 10.000 Möglichkeiten innerhalb der Gültigkeitsdauer prüfen. Eine Ablaufzeit allein genügt nicht, wenn die Anzahl der Versuche nicht begrenzt ist.

Der aktuelle NIST SP 800-63B verlangt für kurzfristige Geheimnisse bei der Out-of-Band-Authentifizierung mindestens sechs Stellen und schreibt bei weniger als 64 Bit Entropie eine Begrenzung der Versuchszahl vor. Zudem fordert er, E-Mail nicht für die Out-of-Band-Authentifizierung zu verwenden4. Die Prüfungsantwort arbeitet innerhalb der vorgegebenen Spezifikation eines vierstelligen, per E-Mail versendeten Codes, doch bei einem neuen Entwurf in der Praxis sollte diese Prämisse selbst überdacht werden.

6. Teilaufgabe 2(2) – alg=none ist das Problem, „den Angreifer die Prüfmethode wählen zu lassen“

Das JWT dieser Aufgabe besteht aus drei Teilen: Header, Payload und Signatur.

base64url(header).base64url(payload).base64url(signature)

Im Header war RS256 als Signaturalgorithmus vermerkt. Die Payload enthält Benutzer-ID, Ausstellungszeitpunkt und Ablaufdatum.

Die prüfende Person änderte die beiden folgenden Dinge.

  1. Das alg im Header von RS256 auf NONE ändern
  2. Die Benutzer-ID in der Payload auf einen anderen Nutzer ändern

Sendete man dieses JWT, gelang die Prüfung, und man konnte sich als jemand anderes ausgeben.

Ablauf des JWT-alg=none-AngriffsÄndert alg in einem gültigen JWT auf none und schreibt die Benutzer-ID um, sodass die Anfrage durchgehtHeader-alg auf none ändernÜberspringt SignaturprüfungGültiges JWTalg=RS256user=user01Manipuliertes JWTalg=noneuser=user02Server akzeptiert esals user02

Abbildung 3: Ablauf des JWT-alg=none-Angriffs. Der Angreifer wählt den Prüfalgorithmus selbst.

none ist kein Tippfehler

RFC 7519 definiert ein „Unsecured JWT“ – ein JWT ohne Signatur und ohne Verschlüsselung, dessen alg none lautet5. Der Wert none ist also nicht etwas, das in der Spezifikation schlicht nicht existiert.

Das Problem ist, dass eine API, die nur signierte JWTs akzeptieren sollte, das vom Angreifer vorgegebene none akzeptiert hat.

Konzeptionell aufgeschrieben, sieht die verwundbare Verarbeitung so aus.

1. JWT-Header lesen
2. Das im Header stehende alg ansehen und danach die Prüfmethode wählen
3. Ist alg gleich none, die Signaturprüfung nicht durchführen
4. Der Benutzer-ID in der Payload vertrauen

Aus einer vom Angreifer kontrollierten Eingabe wird die Stärke der Sicherheit selbst ausgewählt.

Die Prüfungsantwort

Die Teilaufgabe fragt, jeweils in ≤20 Zeichen, welche Daten die korrigierte Bibliothek Q prüfen soll und welche Prüfung dabei stattfinden soll.

Die Musterlösung lautet:

Punkt Kern der Antwort
Zu prüfende Daten Der im JWT-Header unter alg angegebene Wert
Inhalt der Prüfung Prüfen, dass er nicht NONE ist

Als unmittelbare Korrektur der im Aufgabentext beschriebenen Schwachstelle ist das die richtige Antwort.

In der Praxis nicht bei „alles außer NONE ist okay“ stehen bleiben

Hier muss man die Prüfungsantwort von der Empfehlung für die Praxis trennen.

RFC 8725 verlangt, dass eine JWT-Bibliothek den Aufrufer eine Menge erlaubter Algorithmen angeben lässt und dass nichts außerhalb dieser Menge verwendet werden darf6. Mit anderen Worten:

Schlechter Ansatz:
  Akzeptieren, wenn token.header.alg != "none"

Guter Ansatz:
  Nur akzeptieren, wenn in serverConfig.allowedAlgorithms enthalten
  Beispiel: allowedAlgorithms = ["RS256"]

Nur none abzulehnen, kann trotzdem andere schwache Algorithmen oder eine Algorithmus-Verwechslung offenlassen, bei der ein Verfahren mit öffentlichem Schlüssel mit einem symmetrischen Verfahren verwechselt wird. Das Prinzip lautet: nicht die Verneinungen dessen häufen, was abgelehnt wird, sondern eng festlegen, was erlaubt ist.

Bei der JWT-Prüfung sollten Sie, je nach Verwendungszweck, mindestens auch Folgendes prüfen – nicht nur den Algorithmus.

Punkt Was zu prüfen ist
Signatur Lässt sie sich mit dem erwarteten Schlüssel und Algorithmus verifizieren
iss Ist es ein vertrauenswürdiger Aussteller
aud Wurde das Token für diese API ausgestellt
exp Liegt es innerhalb der Gültigkeitsdauer
nbf Liegt es nicht vor dem „gültig ab“-Zeitpunkt
sub oder Benutzer-ID Ist es ein gültiges Subjekt in der Anwendung
Tokentyp Wird ein ID-Token nicht mit einem Access-Token o. Ä. verwechselt

In dieser Aufgabe lautet der Schlüsselname in der Payload user, in der Praxis sollten Sie aber entweder den Standard sub verwenden oder die Bedeutung eines eigenen Claims klar definieren.

Sichere und unsichere JWT-PrüfungUnsichere Prüfung hängt von alg ab, sichere Prüfung nutzt eine serverseitige PositivlisteSichere PrüfungServerseitig erlaubte Algorithmenz. B. RS256Prüfen, ob das alg im JWT-Headerin der Positivliste stehtSignatur, iss, aud, exp prüfenUnsichere Prüfungalg aus dem JWT-Header lesenAkzeptieren, wenn alg gleich none ist

Abbildung 4: Sichere und unsichere Prüfung. In der Praxis wird eine eng gefasste Menge erlaubter Algorithmen festgelegt.

Base64url ist keine Verschlüsselung

Ein weiteres bei JWTs häufiges Missverständnis: Header und Payload werden in base64url dargestellt, doch das ist keine Verschlüsselung. Jeder kann sie dekodieren und lesen.

Was die Signatur garantiert – und zwar nur, wenn sie sich korrekt verifizieren lässt –, ist, dass der Inhalt seit der Ausstellung nicht verändert wurde. Das bedeutet nicht, dass geheim zu haltende persönliche Daten in die Payload eines signierten JWT gehören.

7. Teilaufgabe 2(3) – Auch bei gültigem JWT ließ sich durch Ändern von mid fremdes lesen

Als Nächstes ein Angriff, der das JWT selbst nicht manipuliert.

Die Benutzer-API nimmt per GET oder PUT eine Benutzer-ID namens mid entgegen. Das gemeinsame Modul P ruft die zu dieser mid gehörenden Benutzerinformationen aus der Datenbank ab bzw. aktualisiert sie.

Der Aufbau des Angriffs ist einfach.

Benutzer-ID im JWT: user01    <- ein korrekt signiertes JWT
mid der Anfrage:    user02    <- vom Angreifer geändert

Die Signatur des JWT ist gültig, also gelingt die Authentifizierung. Doch die API vertraut mid=user02 unverändert und liefert die Informationen von user02 zurück.

Das ist ein Lehrbuchbeispiel dessen, was die OWASP API Security Top 10 2023 Broken Object Level Authorization (BOLA) nennt. Immer wenn auf Daten über eine vom Nutzer angegebene Objekt-ID zugegriffen wird, muss die Autorisierung für genau dieses Objekt jedes Mal geprüft werden7.

Der BOLA-AngriffVerwendet ein gültiges JWT, ändert aber die mid der Anfrage auf eine andere Benutzer-IDJWT: user01mid: user02Vertraut midAngreiferBenutzer-APILiefert user02s Daten aus der DB

Abbildung 5: Der BOLA-Angriff. Die Authentifizierung gelingt, die Autorisierung wurde nie geprüft.

Die Antwort der Teilaufgabe

Unterstreichung ② in Tabelle 5 fragt in ≤40 Zeichen nach der Verarbeitung, die dem Aufruf des gemeinsamen Moduls P hinzuzufügen ist.

Die Musterlösung lautet:

Verarbeitung, die prüft, ob die im JWT enthaltene Benutzer-ID mit dem Wert von mid übereinstimmt

Der Vorteil, dies im gemeinsamen Modul P zu prüfen, ist, dass sich dieselbe Autorisierung leicht sowohl auf GET als auch auf PUT anwenden lässt – und auf jede künftige API, die P nutzt. Kopiert man denselben Vergleich in jeden einzelnen Bildschirm oder Endpunkt, fehlt er irgendwo.

Sicherer entworfen ist, mid gar nicht erst entgegenzunehmen

Für eine API, die nur die eigenen Informationen des Aufrufenden abruft oder aktualisiert, ist es gar nicht nötig, eine Benutzer-ID vom Client entgegenzunehmen.

GET /users/me
Authorization: Bearer <JWT>

Serverseitig wird das Subjekt aus dem geprüften JWT entnommen.

principal = validateJwt(request.authorization)
userId = principal.subject
return repository.getUser(userId)

Für Aktualisierungen gilt dasselbe.

principal = validateJwt(request.authorization)
input = validateProfileUpdate(request.body)
repository.updateProfile(
    userId = principal.subject,
    name = input.name,
    age = input.age
)

Eine Vergleichsprüfung schützt, wenn man sie schreibt. Ein Entwurf, der die Ziel-ID aber gar nicht erst von außen entgegennimmt, verringert die Existenz dieser Fehlerklasse selbst – dass man den Vergleich schlicht zu schreiben vergisst.

Muss ein Administrator die Benutzerinformationen einer anderen Person bearbeiten können, trennen Sie das so:

PUT /users/me                  für allgemeine Nutzer
PUT /admin/users/{userId}      für Administratoren

Für die Administratorroute verlangen Sie separate Berechtigungen, Audit-Protokollierung und bei Bedarf eine erneute Authentifizierung. Das macht die Grenzen der Autorisierungsrichtlinie deutlich sichtbarer als „der allgemeinen Nutzer-API nur für Administratoren eine Ausnahme hinzufügen“.

Wie man BOLA verhindertStatt die mid der Anfrage zu nutzen, das Ziel aus dem subject des JWT bestimmen oder abgleichenGET /users/me + JWTsub aus JWT entnehmenÜbereinstimmungKeine ÜbereinstimmungKeine midNutzerAPIFalls mid vorhanden,stimmt es mit sub übereinEigene Daten zurückgeben403 ablehnenDB-Suche über sub aus JWT

Abbildung 6: Wie man BOLA verhindert. Entweder mid gar nicht entgegennehmen, oder gegen das subject des JWT abgleichen.

Authentifizierung und Autorisierung in einem Satz unterscheiden

Sowohl in der Prüfung als auch in der Praxis hilft diese Formulierung:

  • Authentifizierung: wer Sie sind
  • Autorisierung: was diese Person tun darf

Eine erfolgreiche JWT-Signaturprüfung bringt Sie nur so weit wie „dem Subjekt, das dieses Token darstellt, kann vertraut werden“. Ob „dieses Subjekt user02 lesen darf“, muss gesondert geprüft werden.

8. Teilaufgabe 2(4) – status=paid ist eine Autorisierungslücke auf Eigenschaftsebene

Die Spezifikation der Benutzer-API definiert die folgenden Aktualisierungsparameter.

mid   Benutzer-ID
name  Name
age   Alter

Die prüfende Person fügte jedoch den folgenden, nicht spezifizierten Wert hinzu.

status=paid

Daraufhin wechselte der Status eines kostenlosen Nutzers zu dem eines zahlenden Nutzers.

Laut Aufgabentext validierte Dienst L die empfangenen Parameter nicht, sondern reichte alle unverändert an das gemeinsame Modul P weiter, das so gebaut war, dass es die Datenbank direkt aktualisieren konnte.

Die Antwort für Lücke c lautet gemeinsames Modul P.

Mass AssignmentEin nicht spezifiziertes status=paid wird hinzugefügt und vollständig auf das interne Objekt übertragenAngreifer fügt status=paid hinzuAutomatisch gebundenIn DB gespeichertAPI-Spezifikationmid / name / ageRequest-BodyGemeinsames Modul PZahlungsstatus auf paid geändert

Abbildung 7: Mass Assignment. Eine nicht spezifizierte Eigenschaft wird vollständig auf das interne Objekt übertragen.

Der Unterschied zu BOLA

Der mid-Austausch aus dem vorigen Kapitel und das jetzige Hinzufügen von status sehen sich ähnlich, doch die geschützte Granularität ist unterschiedlich.

Schwachstelle Was der Angreifer ändert Was tatsächlich geprüft werden müsste
mid-Austausch Das Zielobjekt Darf dieser Nutzer auf diesen Benutzerdatensatz zugreifen
status-Zusatz Eine Eigenschaft innerhalb des Objekts Darf dieser Nutzer dieses Feld ändern

Die OWASP API Security Top 10 2023 führt Letzteres als Broken Object Property Level Authorization und ordnet das früher als Mass Assignment bezeichnete Problem dieser Kategorie zu8.

„JSON direkt in die Entität schreiben“ ist gefährlich

Konzeptionell sieht die verwundbare Implementierung so aus.

entity = repository.find(body.mid)
bindAllProperties(entity, body)
repository.save(entity)

Auch wenn der Bildschirm nur Eingabefelder für name und age zeigt, kann ein Angreifer die HTTP-Anfrage direkt selbst bauen. Dass ein Feld in der Oberfläche fehlt, ist keine Sicherheitsgrenze.

Eine sichere Implementierung macht die aktualisierbaren Felder explizit.

input = parseExactSchema(body, fields = ["name", "age"])

entity = repository.find(authenticatedUserId)
entity.name = input.name
entity.age  = input.age
repository.save(entity)

Hier zählen zwei Dinge.

  1. Der Eingabetyp für Aktualisierungen sollte nur die Felder enthalten, die ein Nutzer ändern darf
  2. Unbekannte, nicht spezifizierte Felder sollten nach Möglichkeit als Fehler abgelehnt statt stillschweigend ignoriert werden

Unbekannte Felder stillschweigend zu ignorieren verbirgt zwar, dass ein Angriff fehlschlug, lässt aber auch Implementierungsfehler des Clients und Anzeichen eines Angriffs übersehen. Ohne Kompatibilitätsgrund lässt sich mit einem strengen Schema leichter untersuchen, wenn abgelehnt wird.

Autorisierung auf EigenschaftsebeneDas Aktualisierungs-DTO enthält nur eine Positivliste, unbekannte Eigenschaften werden abgelehntSchemaprüfungJaNeinEigener WegAktualisierungs-DTO (Positivliste)nameageRequest-BodyNur erlaubteFelder vorhandenname/age der Entität aktualisierenFehler zurückgebenZahlungsdienstverifizierte Benachrichtigungstatus=paid aktualisieren

Abbildung 8: Autorisierung auf Eigenschaftsebene. Aktualisierbare Felder per Positivliste beschränken, Zahlungsstatus nur über einen separaten Weg ändern.

status nur aus dem Zahlungsergebnis ändern

status=paid ist kein Teil des Benutzerprofils. Es ist ein Zustand, der aus einer serverseitigen Tatsache abgeleitet wird: dass die Zahlung erfolgreich war.

Aktualisierung des Benutzerprofils
  -> nur name / age änderbar

Verifizierte Benachrichtigung vom Zahlungsdienst
  -> paymentId abgleichen
  -> doppelte Verarbeitung verhindern
  -> status auf paid ändern

Auch wenn beides in derselben Datenbankspalte gespeichert wird, sind die Berechtigung zur Änderung und der Weg der Änderung zweierlei. Verwendet man eine interne Entität unmittelbar als Eingabetyp einer externen API, verschwindet diese Grenze.

9. Teilaufgabe 2(5) – Die Gegenmaßnahme gegen Brute-Force hält die Fehlerzahl als Zustand

Gegen das Brute-Forcing des 4-stelligen Codes fragt Lücke d in Tabelle 5 in ≤30 Zeichen nach der dort einzusetzenden Verarbeitung. Der Schwellenwert ist 10.

Die Musterlösung lautet:

Verarbeitung, die das Konto sperrt, sobald die Anzahl aufeinanderfolgender Fehlversuche den Schwellenwert überschreitet

Das widerspricht nicht der Zustandslosigkeit aus Teilaufgabe 1. Den Gesprächszustand eines API-Aufrufs nicht als Serversitzung zu halten und die für eine Sicherheitsentscheidung nötige Fehlerzahl dauerhaft zu speichern, sind zwei verschiedene Dinge.

Mit und ohne Begrenzung der VersuchszahlOhne Begrenzung wird der Code im Schnitt nach 500 Sekunden geknackt, eine Fehlerzahlbegrenzung bremst den Angriff drastischMit BegrenzungAngriffsgeschwindigkeit bricht einKonto gesperrtSperre nach 10 FehlversuchenGestaffelte VerzögerungOhne BegrenzungEtwa 500 SekundenAuthentifizierung gelingt10 Versuche pro Sekunde

Abbildung 10: Mit und ohne Begrenzung der Versuchszahl. Eine Fehlerzahlbegrenzung kann Brute-Force praktisch stoppen.

In der Praxis nicht allein auf „dauerhafte Sperre“ setzen

Eine kontobezogene Versuchsbegrenzung ist nötig, doch kennt ein Angreifer die Benutzer-ID einer anderen Person, kann er absichtlich zehnmal fehlschlagen lassen und so den rechtmäßigen Nutzer aussperren. In der Praxis kombiniert man deshalb Folgendes.

Kontrolle Rolle
Kontobezogene Fehlerzahl Stoppt Brute-Force gegen ein einzelnes Konto
Gestaffelte Wartezeiten Toleriert Tippfehler rechtmäßiger Nutzer und bremst gleichzeitig den Angriff
Kontrolle nach Quell-IP, Gerät, ASN usw. Bremst Angriffe, die viele Konten mit jeweils wenigen Versuchen ausprobieren
Risikobasierte Beurteilung Schränkt bei ungewöhnlicher Region, ungewöhnlichem Gerät oder Geschwindigkeit stärker ein
Benachrichtigung des Nutzers Lässt Nutzer einen Angriff oder eigenen Fehler bemerken
Sicheres Wiederherstellungsverfahren Verhindert, dass der Entsperrkanal selbst zum Angriffsweg wird

Zudem darf beim erneuten Versand eines Codes die Fehlerzahl nicht auf null zurückgesetzt werden – sonst kann ein Angreifer sein Versuchskontingent bei jedem Aufruf der Neuversand-API wieder auffüllen. Auch der aktuelle NIST SP 800-63B verlangt, die Fehlerzahl selbst bei der Erzeugung eines neuen Authentifizierungsgeheimnisses nicht zurückzusetzen4.

Den Authentifizierungscode nur einmal verwendbar machen

Der Aufgabentext konzentriert sich auf die Gültigkeitsdauer, doch in der Praxis braucht es zusätzlich:

  • Einen erfolgreichen Code sofort ungültig machen
  • Die Wiederverwendung desselben Codes ablehnen
  • Den Code selbst nicht in Protokollen belassen
  • Die Antwort so gestalten, dass sich aus Erfolg oder Misserfolg der Codeprüfung nicht ableiten lässt, ob ein Nutzer existiert
  • Auch die Code-Versand-API mit einer Versuchsbegrenzung versehen

Solange ein kurzes Geheimnis verwendet wird, lässt sich die Sicherheit nicht allein der Zufallserzeugung überlassen.

Gegenmaßnahmen für den AuthentifizierungscodeÜber Stellenzahl und Ablaufzeit hinaus mit Versuchsbegrenzung, Wiederverwendungssperre, Benachrichtigung usw. schützenAuthentifizierungscodeStellenzahl erhöhenGültigkeitsdauer verkürzenBegrenzung der VersuchszahlNach Erfolg ungültig machenBei Neuversand Fehlerzahl nicht zurücksetzenCode nicht im Protokoll belassenQuellbezogene Kontrolle

Abbildung 11: Gegenmaßnahmen für den Authentifizierungscode. Stellenzahl und Ablaufzeit mit Versuchskontrolle und Betriebspraxis kombinieren.

10. Die vier Punkte aus Teilaufgabe 2 auf einer Seite unterscheiden

Die in Teilaufgabe 2 leicht zu verwechselnden Punkte, geordnet nach dem vom Angreifer kontrollierten Wert.

Angriff Vom Angreifer geänderter Wert Was nicht hätte vertraut werden dürfen Grundlegende Abhilfe
JWT-Manipulation alg im JWT-Header, Benutzer-ID in der Payload Der vom Token selbst angegebene Prüfalgorithmus Erlaubte Algorithmen serverseitig festlegen
Fremde Informationen abrufen mid der Anfrage Die vom Client angegebene Ziel-ID Gegen das subject des JWT abgleichen oder die Ziel-ID aus dem JWT bestimmen
Wechsel zu zahlendem Nutzer Nicht spezifiziertes status Alle automatisch gebundenen Eigenschaften Aktualisierbare Eigenschaften als Positivliste führen
Durchbruch des 4-stelligen Codes Kandidaten für otp Unbegrenzte Authentifizierungsversuche Fehlerzahlbegrenzung, Verzögerung, Risikobeurteilung einführen

Wichtig ist, das nicht alles unter „Eingabewerte validieren“ zusammenzufassen.

  • alg ist eine kryptografische Richtlinie
  • mid ist Autorisierung auf Objektebene
  • status ist Autorisierung auf Eigenschaftsebene
  • otp ist Widerstandsfähigkeit gegen Online-Raten

Selbst innerhalb derselben HTTP-Anfrage ist der Schutzgrund jeweils ein anderer.

11. Teilaufgabe 3(1) – Externe Codeausführung bestätigen, ohne etwas zu zerstören

Nach dem Start des Diensts wird in der weit verbreiteten Open-Source-Bibliothek H eine kritische Schwachstelle V veröffentlicht. Der Ablauf im Aufgabentext ist folgender.

  1. Der Angreifer sendet eine Zeichenfolge mit einem JNDI Lookup in einem HTTP-Header
  2. Der angegriffene Server protokolliert diesen Wert
  3. Die verwundbare Bibliothek wertet den JNDI Lookup aus und fragt den Angriffs-LDAP-Server ab
  4. Die LDAP-Antwort liefert die URL des Angriffs-HTTP-Servers
  5. Der angegriffene Server lädt die Klassendatei und führt den Befehl aus

Mit verschwiegenem Produktnamen liest sich das naheliegend als Angriff vom Typ Log4Shell (CVE-2021-44228). Auch Apaches eigene Beschreibung stellt die Schwachstelle so dar, dass ein Angreifer, der Protokollmeldungen oder Parameter kontrolliert, beliebigen Code ausführen kann, der von einem LDAP-Server geladen wird9.

Bestätigungsablauf für eine Schwachstelle vom Typ Log4ShellMit einem harmlosen Callback bestätigen, ob die Kette von JNDI bis zur externen Codeausführung tatsächlich durchgehtInjiziert eine jndi/ldap-Nutzlastin x-api-versionJNDI LookupHTTP-URL-AntwortGET aufzeichnenErreichbarkeit bestätigtAngreiferVerwundbarer ServerProtokollverarbeitungBösartiger LDAP-ServerBösartiger HTTP-Serverindex.htmlTestserverZugriffsprotokollSchwachstelle bestätigt

Abbildung 12: Bestätigungsablauf für eine Schwachstelle vom Typ Log4Shell. Statt eines destruktiven Befehls wird die Erreichbarkeit durch Aufzeichnung eines HTTP-Zugriffs bestätigt.

Der Prüfcode löst nur einen harmlosen HTTP-Zugriff aus

Unternehmen G führt einen Prüfcode aus, der das System nicht beeinträchtigt, um zu bestätigen, ob sich Schwachstelle V von außen ausnutzen lässt. Der einzige Befehl, den der Prüfcode auslöst, ist das Abrufen der index.html des Testservers.

Teilaufgabe 3(1) fragt, was auf dem Testserver implementiert werden muss, um zu bestätigen, dass der Befehl ausgeführt wurde.

Die Musterlösung lautet:

Ein Mechanismus, der Zugriffe auf die index.html des Testservers aufzeichnet und deren Bestätigung ermöglicht

Verzeichnet das Zugriffsprotokoll des Webservers ein GET vom angegriffenen Server, bestätigt das mindestens, dass die folgende Kette durchgegangen ist.

Externe HTTP-Anfrage
  -> Protokollverarbeitung
  -> JNDI Lookup
  -> LDAP-Antwort
  -> Klasse laden
  -> Prüfbefehl ausführen
  -> HTTP-Zugriff auf den Testserver

Warum „Text auf dem Bildschirm anzeigen“ nicht genügt

Ziel des Angriffs ist der Server. Es ist nicht garantiert, dass sich im Browserbildschirm des Nutzers irgendetwas ändert. Zudem kann die ausgehende Kommunikation auf halbem Weg von einer Firewall gestoppt werden, selbst wenn die Schwachstelle existiert.

Zeichnet der Testserver den Zugriff auf, entsteht ein beobachtbarer Nachweis, dass der angegriffene Server tatsächlich nach außen gelangt ist.

Führen Sie eine solche Prüfung in der Praxis durch, beachten Sie unbedingt Folgendes.

  • Holen Sie eine ausdrückliche Genehmigung des Eigentümers des Zielsystems ein
  • Wählen Sie eine Prüfmethode ohne oder mit vertretbar geringer Auswirkung auf den Produktivbetrieb
  • Verwenden Sie keine destruktiven Befehle wie Schreiben, Löschen oder Konfigurationsänderungen
  • Verwalten Sie Prüfdomäne und -server selbst
  • Zeichnen Sie Prüfzeitpunkt, Quelle, Ziel und erwarteten Callback auf
  • Entfernen Sie temporäre LDAP- oder HTTP-Server sowie Zugangsdaten nach der Prüfung

„Bestätigen, dass beliebige Codeausführung möglich ist“ und „beliebigen gefährlichen Code ausführen“ sind nicht dasselbe. Beschränken Sie die Nebenwirkungen auf das für das Ziel nötige Minimum.

12. Teilaufgabe 3(2)(3) – Die WAF prüft den HTTP-Header

Die WAF von Dienst N lässt als Prüfziel GET, POST, PUT, ANY, Header, COOKIE oder Multipart zu.

Der Angriffscode steckt im Wert eines HTTP-Headers namens x-api-version. Die Lücken e und f in Tabelle 6 lauten daher beide Header.

Den im Text genannten Ort direkt auf das WAF-Prüfziel abbilden

Hier geht es weniger um allgemeines Wissen als darum, den Datenfluss im Aufgabentext zu lesen.

Ort der Angriffszeichenfolge:
  x-api-version-Header
          |
          v
Prüfziel der WAF:
  Header

Es ist weder ein GET-Parameter noch ein POST-Body. Statt aus der Funktionsliste der WAF „klingt nach Angriff, also ANY“ zu wählen, antworten Sie mit dem Ort, an dem der Aufgabentext den Angreifer den Wert platzieren lässt.

Groß-/Kleinschreibung berücksichtigen

Der erste Vorschlag folgte konzeptionell dieser Regel.

Header  \Wjndi\W  sperren
Header  \Wldap\W  sperren

Vertauscht man aber, wie bei jNdI, Groß- und Kleinschreibung, lässt sich ein einfaches, nur auf Kleinbuchstaben passendes Muster umgehen.

Die Musterlösung zu Teilaufgabe 3(3) ist einer der beiden folgenden Ausdrücke.

\W[jJ][nN][dD][iI]\W
\W(j|J)(n|N)(d|D)(i|I)\W

Im Aufgabenheft kann der Backslash in der japanischen Schriftform wie ein Yen-Zeichen aussehen, als regulärer Ausdruck ist es aber \W. \W passt auf jedes Zeichen außer Buchstaben, Ziffern und Unterstrich. In der Syntax von JNDI Lookup stehen unmittelbar vor und nach jndi Nicht-Wort-Zeichen wie ${ oder :, und das Muster ist so geschrieben, dass es diese mit erfasst.

Nach demselben Prinzip lässt sich auch die ldap-Seite groß-/kleinschreibungsunabhängig machen.

\W[lL][dD][aA][pP]\W

Diesen regulären Ausdruck nicht für eine „vollständige Log4Shell-Abwehr“ halten

Die Prüfung verlangt einen regulären Ausdruck, der die im Aufgabentext gezeigte Umgehungsmethode abdeckt. In echten Angriffen sind Aufteilung von Zeichenketten, andere Lookup-Varianten, Kodierung, andere Protokolle und weitere Abwandlungen möglich, die sich mit Signaturen allein schwer vollständig abdecken lassen.

Die praktische Einordnung ist daher folgende.

  1. Bereits bekannte Angriffsmuster vorläufig per WAF stoppen
  2. Untersuchen, ob die betroffene Bibliothek tatsächlich enthalten ist
  3. Ausgehendes LDAP, RMI und unnötigen HTTP-Verkehr einschränken
  4. Auf eine korrigierte Version aktualisieren
  5. Auch nach dem Update die Protokolle prüfen und auf einen möglichen Einbruch untersuchen

Die WAF ist eine Schicht, die Zeit erkauft, bis eine korrigierte Version verfügbar ist.

Einordnung der WAFDie WAF ist eine vorläufige Abwehrschicht, die grundlegende Abhilfe ist die Aktualisierung der Bibliothek auf eine korrigierte VersionWAF-RegelErkennung/SperrungAusgehenden Verkehr einschränkenKritische Schwachstelle veröffentlichtAuswirkung bestätigenVorläufige AbwehrAngriffsmuster vorübergehend stoppenMissbrauchsweg schließenAuf korrigierte Bibliothek aktualisierenNachträgliche Prüfung und Vorbeugung

Abbildung 13: Einordnung der WAF. Die WAF erkauft nur Zeit bis zum Patch; die grundlegende Abhilfe ist das Update.

13. Teilaufgabe 3(4) – Warum zuerst „Erkennung“ verwenden

Zur geänderten WAF-Regel rät Z, ein Registered Security Specialist, den Modus für einen bestimmten Zeitraum nach dem Produktivstart nicht auf „Sperrung“, sondern auf „Erkennung“ zu stellen.

Die Teilaufgabe fragt, jeweils in ≤25 Zeichen, nach dem Vorteil des Erkennungsmodus und danach, was zur Schadensminimierung zu tun ist.

Die Musterlösung lautet:

Punkt Kern der Antwort
Vorteil Kann eine Sperrung durch Fehlalarme verhindern
Durchzuführende Maßnahme Bei Eingang eines Alarms prüfen, ob es sich um einen Angriff handelt

Der Erkennungsmodus ist kein „Nichts-tun“-Modus

Im Erkennungsmodus wird Datenverkehr, der auf eine Regel passt, weiterhin durchgelassen, aber protokolliert und mit einem Alarm versehen. Taucht in einem normalen API-Aufruf zufällig die Zeichenfolge jndi oder ldap auf, wird der Geschäftsbetrieb dadurch nicht sofort gestoppt.

Im Gegenzug braucht es auf der Betriebsseite Folgendes.

Alarm eingegangen
   |
   v
Betroffene Anfrage prüfen
   |
   +-- Normaler Verkehr -> Regel einschränken, Ausnahme erwägen
   |
   +-- Angriff          -> Ziel isolieren, Protokolle sichern, Auswirkung untersuchen, zu Sperrung wechseln

Sieht sich niemand die Alarme an, hat der Erkennungsmodus überhaupt keine Schutzwirkung. Erkennung wirkt nur zusammen mit einem Betrieb, der beobachtet und beurteilt.

Der Weg von Erkennung zu Sperrung

Ein typisches Einführungsverfahren sieht so aus.

  1. Erkennungsmodus auf echten Datenverkehr anwenden
  2. Fehlalarme von echten Treffern unterscheiden
  3. Betroffenen Header, Pfad, API, Wortgrenzen usw. abstimmen
  4. Bestätigen, dass die Auswirkung auf normalen Verkehr vertretbar ist
  5. In den Sperrmodus wechseln
  6. Anzahl der Sperrungen und geschäftliche Auswirkung überwachen

Das ist allerdings das Prinzip für den Normalfall. Ist eine Schwachstelle kritisch, wird sie bereits aktiv ausgenutzt und gibt es keine Alternative, kann der Schaden durch einen Einbruch höher eingeschätzt werden als der durch einen Fehlalarm verursachte Ausfall, sodass von Anfang an gesperrt wird. Im Szenario der Prüfung wird zunächst die Erkennung gewählt, um zu bestätigen, dass sich der Dienst wie bisher weiter nutzen lässt.

Von der WAF-Erkennung zur SperrungAlarme im Erkennungsmodus beobachten, Fehlalarme abstimmen, dann in den Sperrmodus wechselnEchter VerkehrFehlalarmAngriffErkennungsmodusAlarm ausgelöstAngriff oderFehlalarmRegel anpassenIn den Sperrmodus wechselnAnzahl Sperrungen und geschäftliche Auswirkung überwachen

Abbildung 14: Von der Erkennung zur Sperrung. Erst beobachten und abstimmen, dann die Auswirkung als vertretbar bestätigen und zur Sperrung wechseln.

14. Die WAF ist eine vorläufige Maßnahme, das Update die grundlegende Abhilfe

Im Aufgabentext hatte die offizielle Website der Bibliothek H weder eine korrigierte Version noch eine vorläufige Abhilfe, und selbst die umfassende WAF-Regel des Cloud-Anbieters sollte bis zu 72 Stunden dauern. Also bestätigt Unternehmen G die Auswirkung selbst und blockiert vorläufig zumindest die bereits bekannten Muster.

Diese Reihenfolge ist die Grundform der Vorfallsreaktion.

Phase Zweck Umsetzung in dieser Aufgabe
Auswirkung bestätigen Beurteilen, ob das eigene Unternehmen tatsächlich gefährdet ist Ausnutzbarkeit von außen mit einem harmlosen Callback bestätigen
Vorläufige Abwehr Zeit bis zur Korrektur erkaufen WAF-Regeln, Erkennung/Sperrung, Einschränkung ausgehenden Verkehrs
Grundlegende Korrektur Die verwundbare Ursache beseitigen Auf eine korrigierte Bibliothek aktualisieren
Nachträgliche Prüfung Untersuchen, ob bereits ausgenutzt wurde Protokolle von WAF, Anwendung, DNS, Proxy usw. untersuchen
Vorbeugung Die nächste Entscheidung beschleunigen Bestandsaufnahme der Abhängigkeiten, SBOM, Aktualisierungsverfahren, Kontaktweg

„Wir wissen nicht, ob wir es benutzen“ ist die größte Ursache für Verzögerung

Im Aufgabentext dauert es, selbst als Unternehmen G bei Unternehmen F anfragt, ob Bibliothek H genutzt wird, wegen einer nötigen detaillierten Konfigurationsanalyse.

In der Praxis kommt es zu Verzögerungen, wenn man erst nach der Veröffentlichung einer kritischen Schwachstelle anfängt, JAR-Dateien zu suchen. Mindestens Folgendes sollte schon im Normalbetrieb vorhanden sein.

  • Eine Liste direkter und transitiver Abhängigkeiten
  • Die tatsächlich in den Auslieferungsartefakten enthaltenen Komponenten und Versionen
  • An welche Dienste, Container und Endgeräte diese verteilt sind
  • Ein Verfahren, um eine abhängige Bibliothek zu aktualisieren und neu zu bauen bzw. auszuliefern
  • Einen Kontaktweg zur Genehmigung von Notfalländerungen
  • Erlaubte Ziele für ausgehenden Verkehr und die Auswirkung, sie zu sperren
  • Speicherort der Protokolle und wie man darin sucht

Ein SBOM ist nicht das Ziel an sich. Es ist ein Index, um in kurzer Zeit zu beantworten: „Welche laufenden Systeme betrifft diese Schwachstelle?“

Nach dem Update die Untersuchung nicht beenden

Möglicherweise wurde bereits vor oder nach Veröffentlichung der Schwachstelle angegriffen. Ein Update auf die korrigierte Version stoppt künftigen Missbrauch, beseitigt aber weder bereits kompromittierte Zugangsdaten noch eine bereits eingerichtete Hintertür.

Bei einer Schwachstelle vom Typ Log4Shell untersuchen Sie mindestens die folgenden Aspekte.

  • HTTP-Anfragen mit verdächtigen Zeichenfolgen, die auf JNDI oder LDAP hindeuten
  • Kommunikation vom Anwendungsserver nach außen zu LDAP, RMI oder HTTP
  • Ungewöhnlich gestartete Kindprozesse
  • Erstellung verdächtiger JAR-, Class-, Skript- oder ausführbarer Dateien
  • Zugriffe auf Cloud-Zugangsdaten oder Umgebungsvariablen
  • Authentifizierung, Berechtigungsänderungen und ausgehende Übertragungen rund um den Update-Zeitpunkt

Wichtig ist, allein aus WAF-Protokollen nicht auf „kein Angriff“ zu schließen. Es gibt interne Wege, die nie durch die WAF laufen, und Protokolle, die in der Vergangenheit nicht aufbewahrt wurden.

15. Eine Lesart, mit der sich in der Prüfung leichter punkten lässt

Diese Aufgabe ist weniger eine Wissensfrage als eine Übung darin, die Lücke zwischen Spezifikation und Implementierung zu lesen.

15.1 „Spezifikation“ und „Implementierung“ in den Tabellen trennen

Beim status-Problem geht ein Wert, der in der API-Spezifikation fehlt, in der Implementierung trotzdem durch.

Spezifikation:
  mid / name / age

Implementierung:
  alle empfangenen Parameter an P senden

Ist diese Lücke sichtbar, wird klar, dass Lücke c das gemeinsame Modul P ist.

15.2 Eine Linie unter den vom Angreifer geänderten Wert ziehen

Der bei jedem Angriff geänderte Wert ist:

  • alg im JWT-Header
  • Benutzer-ID in der JWT-Payload
  • API-Parameter mid
  • Nicht spezifiziertes status
  • otp der Authentifizierungs-API
  • HTTP-Header x-api-version

Fast jede Teilaufgabe fragt, „wo dieser Wert geprüft werden sollte“.

15.3 Die Antwort auf die Begriffe des Aufgabentexts zurückführen

In der Praxis lässt sich das „BOLA“, „Mass Assignment“ und „Rate Limiting“ nennen. Doch gefragt ist eine konkrete, an den Aufbau des Aufgabentexts angepasste Verarbeitung.

Schlechtes Beispiel:

Autorisierung angemessen durchführen.

Gutes Beispiel:

Prüfen, ob die im JWT enthaltene Benutzer-ID mit dem Wert von mid übereinstimmt.

Schlechtes Beispiel:

Brute-Force-Gegenmaßnahmen treffen.

Gutes Beispiel:

Das Konto sperren, sobald die Anzahl aufeinanderfolgender Fehlversuche den Schwellenwert überschreitet.

Allein den abstrakten Namen zu kennen, ergibt noch keine im Zeichenlimit bewertbare Antwort.

15.4 Bei der WAF verfolgen, „wo es hineingelangt ist“

Das Prüfziel der WAF bestimmt sich nicht durch Raten anhand der Angriffsart, sondern anhand des Orts, an dem die Angriffszeichenfolge platziert wurde.

In den x-api-version-Header gelegt
        ↓
Prüfziel ist Header

Dass die Erfolgsquote bei Teilaufgabe 3(1) laut Auswertungskommentar etwas geringer war, lag auch daran, dass viele Antworten nicht zum Angriffsablauf aus Abbildung 6 passten. Schon das bloße Nachzeichnen des Angriffsablaufs mit Pfeilen zeigt, was beobachtet werden sollte.

16. Eine Checkliste für praktische API-Reviews

Eine Checkliste, um diese Aufgabe in konkrete Design- und Code-Reviews zu übertragen.

JWT-Prüfung

  • Der erlaubte Signaturalgorithmus ist in der Serverkonfiguration festgelegt
  • none und unerwartete Algorithmen werden abgelehnt
  • Signatur, iss, aud, exp und nbf werden je nach Verwendungszweck geprüft
  • ID-Token, Access-Token und Refresh-Token werden nicht verwechselt
  • Es gibt ein Verfahren für Schlüsselrotation und Widerruf
  • Geheim zu haltende Informationen stehen nicht in der JWT-Payload

Autorisierung auf Objektebene

  • Ändert man die ID in einer Anfrage, lassen sich fremde Daten nicht erreichen
  • Autorisierung erfolgt bei Liste, Detail, Aktualisierung, Löschung und Download gleichermaßen
  • Autorisierung erfolgt in einer gemeinsamen Schicht, die die Daten erreicht, nicht im Bildschirm
  • Bei rein eigenen APIs wurde erwogen, die Ziel-ID aus dem Token abzuleiten
  • Administratoroperationen nutzen eine andere Richtlinie als die allgemeine Nutzer-API

Autorisierung auf Eigenschaftsebene

  • Externer Eingabetyp und Datenbankentität sind getrennt
  • Aktualisierbare Felder sind als Positivliste aufgeführt
  • Nicht spezifizierte Eigenschaften werden abgelehnt oder protokolliert
  • Zustände wie Berechtigung, Abrechnung, Genehmigung oder Eigentümerschaft lassen sich nicht über Nutzereingaben ändern
  • Auch Antworten enthalten keine unnötigen vertraulichen Eigenschaften

Authentifizierungsversuche

  • Es gibt eine kontobezogene Begrenzung der Fehlerzahl
  • Es gibt gestaffelte Verzögerung und quellbezogene Kontrolle
  • Die Fehlerzahl wird bei einer erneuten Codeausstellung nicht zurückgesetzt
  • Der Authentifizierungscode ist nur einmal verwendbar
  • Authentifizierungscodes und Passwörter werden nicht protokolliert
  • Entsperrung/Wiederherstellung ist kein separater, schwächerer Authentifizierungsweg

Kritische Schwachstellen in abhängigen Bibliotheken

  • Laufende Dienste lassen sich ihren Abhängigkeitsversionen zuordnen
  • Es gibt ein Verfahren, die Auswirkung auf harmlose Weise zu prüfen
  • Vorläufige Maßnahmen wie WAF-Regeln und Einschränkung ausgehenden Verkehrs lassen sich anwenden
  • Es gibt einen Betriebsprozess, in dem eine zuständige Person Erkennungsalarme prüft
  • Es gibt einen Notfall-Release-Weg für die Aktualisierung auf eine korrigierte Version
  • Vor dem Update wird in den Protokollen nach möglichem Missbrauch gesucht

17. Die zwei Gesichter gemeinsamer Bausteine, wie diese Aufgabe sie zeigt

In dieser Aufgabe treten zwei gemeinsame Bausteine auf: die JWT-Verwaltungsbibliothek Q und das gemeinsame Modul P.

Gemeinsame Bausteine bringen große Vorteile.

  • Eine Korrektur an einer Stelle wirkt auf alle nutzenden APIs
  • Autorisierungs- und Prüflogik muss nicht in jeder Funktion dupliziert werden
  • Testziele lassen sich bündeln
  • Protokoll- und Audit-Formate lassen sich vereinheitlichen

Andererseits breiten sich auch Fehler auf das Ganze aus.

  • Akzeptiert Bibliothek Q alg=none, wird jede JWT nutzende API gefährdet
  • Akzeptiert das gemeinsame Modul P eine beliebige mid oder ein beliebiges status, werden sowohl GET als auch PUT gefährdet
  • Wird die verwundbare Bibliothek H im Fundament genutzt, wird jeder Weg, der HTTP-Header protokolliert, Teil der Angriffsfläche

Zu vereinheitlichen ist deshalb nicht bloß der Datenzugriff. Nötig ist, die Sicherheitsinvarianten selbst zu vereinheitlichen und diesen gemeinsamen Baustein isoliert streng zu prüfen.

Gestalten Sie zum Beispiel den Vertrag von P so.

P.getOwnUser(authenticatedSubject)
P.updateOwnProfile(authenticatedSubject, ProfileUpdate{name, age})

Eine niedrigschwellige API wie die folgende sollte gewöhnlichen Aufrufern besser nicht direkt offenstehen.

P.getUser(arbitraryMid)
P.updateUser(arbitraryMap)

Letztere wird nur an wenigen, begrenzten Stellen benötigt, etwa in der Verwaltung. Gibt man diese niedrigschwellige Freiheit an alle APIs weiter, entsteht ein Entwurf, der darauf angewiesen ist, dass jeder Aufrufer sie jedes Mal korrekt verwendet.

18. Zusammenfassung

Aufgabe 1 der Nachmittagsprüfung Frühjahr 2024 (Reiwa 6) liest die Themen der API-Sicherheit einzeln auseinander.

Ein JWT zu verwenden bedeutet nicht, dass die Authentifizierung sicher ist. Lässt man den Angreifer den Signaturalgorithmus wählen, lässt sich die Benutzer-ID umschreiben.

Eine gültige JWT-Signatur bedeutet nicht, dass die Autorisierung korrekt ist. Vertraut man der mid der Anfrage, kann ein rechtmäßiger Nutzer auf die Informationen eines anderen zugreifen.

Das eigene Objekt aktualisieren zu können bedeutet nicht, dass jede Eigenschaft geändert werden darf. Wird ein interner Zustand wie status automatisch gebunden, lassen sich Berechtigungen oder Zahlungsstatus umschreiben.

Dass der Authentifizierungscode eine Gültigkeitsdauer hat, bedeutet nicht, dass er gegen Brute-Force resistent ist. Kandidatenraum und Versuchsgeschwindigkeit müssen berechnet und die Fehlerzahl begrenzt werden.

Eine Regel in die WAF einzutragen bedeutet nicht, dass die Schwachstelle behoben ist. Erkennung und Sperrung erkaufen nur Zeit; die Auswirkung wird geprüft und die Bibliothek letztlich aktualisiert.

Zuordnung von Schwachstellen zu GegenmaßnahmenOrdnet jede Schwachstelle ihrer Vertrauensgrenze und ihrer Gegenmaßnahme zuTokenprüfungAutorisierung auf ObjektebeneAutorisierung auf EigenschaftsebeneKontrolle der AuthentifizierungsversucheVon Eingabe zu AusführungJWT-ManipulationErlaubten Algorithmus festlegenmid-AustauschGegen JWT-subject abgleichen/mid unnötigstatus=paidAktualisierungs-DTO als PositivlisteBrute-Force des 4-stelligen CodesFehlerzahlbegrenzung/VerzögerungSchwachstelle vom Typ Log4ShellBibliotheksupdate/WAF

Abbildung 15: Zuordnung von Schwachstellen zu Gegenmaßnahmen. Die Abhilfe je nach gebrochener Grenze trennen.

Ein Prinzip zieht sich durch diese gesamte Aufgabe.

Den Erfolg der vorherigen Prüfung niemals zum Grund machen, die nächste Vertrauensgrenze zu überspringen.

Frühere Artikel dieser Serie behandeln den gespeicherten XSS-Angriff aus Aufgabe 1 der Nachmittagsprüfung Herbst 2023 (Reiwa 5) und das Hinausbringen von Daten über das Gäste-WLAN aus Aufgabe 2 der Nachmittagsprüfung Herbst 2023 (Reiwa 5). Für einen Blick auf die Prüfpunkte einer Website als Ganzes siehe auch IPAs „Anleitung zum sicheren Website-Bau“ als Checkliste nutzen.

Abschließende ZusammenfassungZeigt, dass der Erfolg der vorherigen Prüfung nie ein Grund ist, die nächste Vertrauensgrenze zu überspringenAuthentifizierung erfolgreichJWT-SignaturprüfungAutorisierung auf ObjektebeneAutorisierung auf EigenschaftsebeneBegrenzung der VersuchszahlGrenze von Eingabe zu AusführungWAF/Bibliotheksupdate

Abbildung 16: Abschließende Zusammenfassung. Vertrauensgrenzen werden stufenweise geprüft, keine darf übersprungen werden.

Quellen

  1. IPA, Prüfung zum Registered Information Security Specialist, Frühjahr 2024 (Reiwa 6), Nachmittags-Aufgabenheft. Der Aufgabentext, auf dem dieser Artikel beruht. 

  2. IPA, Prüfung zum Registered Information Security Specialist, Frühjahr 2024 (Reiwa 6), Nachmittags-Musterlösungen. Die offizielle Musterlösung zu jeder Teilaufgabe. 

  3. IPA, Prüfung zum Registered Information Security Specialist, Frühjahr 2024 (Reiwa 6), Nachmittags-Auswertungskommentar. Eine Erklärung der Erfolgsquoten und typischen Fehler. 

  4. NIST, SP 800-63B: Authentication and Authenticator Management. Legt unter anderem Stellenzahlen für kurzfristige Geheimnisse, Begrenzungen der Versuchszahl, die Fehlerzahl bei Neuausstellung und den Verzicht auf E-Mail für die Out-of-Band-Authentifizierung fest.  2

  5. RFC Editor, RFC 7519: JSON Web Token (JWT). Die Spezifikation von JWTs einschließlich des Unsecured JWT und alg=none

  6. RFC Editor, RFC 8725: JSON Web Token Best Current Practices. Das BCP-Dokument, das unter anderem das Festlegen erlaubter Algorithmen sowie die Prüfung von Aussteller, Subjekt und Audience beschreibt. 

  7. OWASP, API1:2023 Broken Object Level Authorization. Erklärt die Notwendigkeit, die Autorisierung für jede vom Nutzer angegebene Objekt-ID zu prüfen. 

  8. OWASP, API3:2023 Broken Object Property Level Authorization. Erklärt Autorisierungslücken auf Eigenschaftsebene einschließlich Mass Assignment sowie Gegenmaßnahmen. 

  9. Apache Logging Services, Security. Erklärt die Auswirkung von CVE-2021-44228, die Codeausführung über JNDI und LDAP sowie die korrigierten Versionen. 

Aktuelle Artikel mit denselben Schlagwörtern führen zu verwandten Themen weiter.

Diese Seiten ordnen den Artikel in einen größeren Leistungs- und Entscheidungskontext ein.

Dieser Artikel ist direkt mit den folgenden Leistungen verbunden.

Website-Entwicklung

Bei Mitglieder-APIs und der Anbindung von Smartphone-Apps wirken sich JWT-Prüfung, Autorisierung auf Objektebene und die Beschränkung aktualisierbarer Eigenschaften unmittelbar auf die Sicherheit des Websystems aus.

Häufige Fragen

Fragen, die in Beratungen zu diesem Artikelthema häufig gestellt werden.

Warum lässt sich jemand anderes vortäuschen, obwohl die JWT-Signaturprüfung erfolgreich war?
In dieser Aufgabe akzeptierte die JWT-Verwaltungsbibliothek das alg im JWT-Header genau so, wie der Angreifer es vorgab, und behandelte ein JWT mit alg=none als gültig – ohne Signatur. Deshalb gelingt die Prüfung auch dann, wenn die Benutzer-ID in der Payload umgeschrieben wird. Die Prüfungsantwort lautet, als zu prüfenden Wert das alg im JWT-Header heranzuziehen und zu bestätigen, dass sein Wert nicht NONE ist. In der Praxis reicht es allerdings nicht, nur NONE abzulehnen. Legen Sie in der serverseitigen Konfiguration die erlaubten Algorithmen fest, etwa auf RS256, und verwenden Sie den vom Token selbst angegebenen Algorithmus nie unmittelbar für die Auswahl. Prüfen Sie außerdem je nach Verwendungszweck issuer, audience, Gültigkeitsdauer und subject.
Reicht es als Autorisierungsmaßnahme, die Benutzer-ID im JWT mit der mid der Anfrage zu vergleichen?
Für die Zwecke dieser Prüfungsantwort ist das ausreichend. Prüft das gemeinsame Modul P, ob die im JWT enthaltene Benutzer-ID mit mid übereinstimmt, lässt sich ein Angriff stoppen, der eine fremde mid angibt. Bei einer API, die aber nur die eigenen Informationen des Aufrufenden behandelt, ist es in der Praxis sicherer, mid gar nicht erst vom Client entgegenzunehmen und die Benutzer-ID stattdessen aus dem subject des geprüften JWT zu bestimmen. Mit Endpunkten wie GET /users/me oder PUT /users/me lässt sich vermeiden, dass die Vergleichsprüfung überhaupt vergessen werden kann. Eine API, über die ein Administrator andere Nutzer bearbeitet, gehört auf einen eigenen Endpunkt mit eigener Autorisierungsrichtlinie.
Warum lässt sich der Angriff, der status hinzufügt, nicht allein durch gewöhnliche Eingabevalidierung verhindern?
Weil es nichts nützt, die Länge von name oder den Wertebereich von age zu prüfen, wenn status – ein Feld, das gar nicht erst angenommen werden sollte – ohnehin automatisch gebunden und an das interne Objekt weitergereicht wird. Das Problem ist nicht das Format eines Werts, sondern die Autorisierung auf Eigenschaftsebene: ob der Nutzer diese Eigenschaft überhaupt ändern darf. Definieren Sie im Eingabetyp für Aktualisierungen ausschließlich name und age, und lehnen Sie unbekannte Eigenschaften ab. Der Zahlungsstatus darf nur durch serverseitig vertrauenswürdige Ereignisse geändert werden, etwa das Erfolgsergebnis des Zahlungsdiensts.
Der vierstellige Authentifizierungscode verfällt nach 10 Minuten – warum ist das trotzdem gefährlich?
Weil es von 0000 bis 9999 nur 10.000 mögliche Werte gibt und bei 10 Versuchen pro Sekunde im Durchschnitt nach 5.000 Versuchen, also 500 Sekunden, der Treffer gelingt. Die Gültigkeitsdauer von 10 Minuten entspricht 600 Sekunden – probiert man sich also der Reihe nach ohne Wiederholung durch, lassen sich innerhalb der Gültigkeitsdauer 6.000 Kombinationen prüfen. Die Ablaufzeit allein stoppt ein Brute-Force nicht. Kandidatenzahl, Versuchsgeschwindigkeit und Obergrenze der Versuche müssen gemeinsam entworfen werden.
Die Prüfungsantwort ist eine Kontosperre – reicht in der Praxis auch eine sofortige Sperre allein?
Nein. In die Lücke der Aufgabe gehört zwar eine Verarbeitung, die das Konto sperrt, sobald die Anzahl aufeinanderfolgender Fehlversuche einen Schwellenwert überschreitet, doch eine feste, dauerhafte Sperre allein erlaubt einem Angreifer, absichtlich das Konto einer anderen Person zu sperren und so einen Denial-of-Service auszulösen. In der Praxis kombiniert man eine kontobezogene Fehlerzahl, gestaffelte Wartezeiten, eine Risikobewertung nach Quelle oder Gerät, Benachrichtigungen und ein Wiederherstellungsverfahren. Wichtig ist auch, dass die Fehlerzahl beim Ausstellen eines neuen Codes nicht auf null zurückgesetzt wird.
Was bringt es, die WAF auf Erkennung statt auf Sperrung zu stellen?
Dass geschäftlicher Datenverkehr auch dann nicht gestoppt wird, wenn eine normale Zeichenfolge fälschlich als Angriff eingestuft wird. Die Musterlösung nennt als Vorteil, dass sich eine Sperrung durch Fehlalarme vermeiden lässt, und als durchzuführende Maßnahme, bei Eingang eines Alarms zu prüfen, ob es sich um einen Angriff handelt. Der Erkennungsmodus ist keine Einstellung zum Sich-selbst-Überlassen. Er dient als Beobachtungszeitraum, in dem Sie Protokolle prüfen, Fehlalarme aussortieren, die Regel anpassen und dann zur Sperrung übergehen. In einem Notfall, in dem eine bekannte, kritische Schwachstelle bereits aktiv ausgenutzt wird, kann es angemessen sein, das Verfügbarkeitsrisiko gegen den Schaden abzuwägen und von Anfang an zu sperren.
Ist die Bibliothek H in dieser Aufgabe Log4j?
Der Aufgabentext verschweigt zwar den Produktnamen, doch der Ablauf des Angriffs – JNDI Lookup, ein LDAP-Server, das Nachladen einer Klasse von einem HTTP-Server, eine in einen HTTP-Header eingebettete Zeichenfolge und ein hoher CVSS-v3.1-Basiswert – lässt sich naheliegend als Abstraktion von CVE-2021-44228 lesen, bekannt als Log4Shell. Dieser Artikel erklärt diese Entsprechung, doch in der Prüfung muss der konkrete Produktname nicht genannt werden. Sie lässt sich allein aus dem vorgegebenen Angriffsablauf und der WAF-Spezifikation beantworten.
Was sollte man aus dieser Aufgabe für die eigene Praxis mitnehmen?
Dass eine erfolgreiche Authentifizierung, ein unverändertes JWT, der erlaubte Zugriff auf das Zielobjekt und die erlaubte Änderung der Zieleigenschaft allesamt getrennte Prüfungen sind. Hinzu kommt, dass ein kurzer Authentifizierungscode eine Begrenzung der Versuchszahl braucht und dass bei einer schwerwiegenden Bibliotheksschwachstelle Wirkungsprüfung, vorläufige Verteidigung und grundlegende Korrektur parallel vorangetrieben werden. Im Kern der Praxis stehen: Autorisierung in gemeinsamen Bausteinen bündeln, das Eingabeschema als Positivliste gestalten, die Prüfbedingungen des JWT serverseitig festlegen und abhängige Bibliotheken im Blick behalten, damit sie aktualisiert werden können.

Autorenprofil

Profilseite des Artikelautors.

Go Komura

Geschäftsführer von KomuraSoft LLC

Spezialisiert auf Windows-Softwareentwicklung, technische Beratung und Fehleranalyse, insbesondere bei bestehenden Systemen und schwer reproduzierbaren Störungen.

Zurück zum Blog