Zum Inhalt springen

Mass Assignment

CWE-915OWASP A01:2021Aktualisiert 4. September 20266 Min. Lesezeit

Mass Assignment ist eine Schwachstelle, bei der ein Angreifer Felder überschreibt, die nie für ihn gedacht waren, weil die Anwendung einen ganzen Anfragekörper ungefiltert auf ein Modell bindet. Ein zusätzliches Feld wie isAdmin oder guthaben im JSON genügt, um Administrator zu werden. Was hilft: eine ausdrückliche Positivliste der beschreibbaren Felder, am besten als eigenes Übertragungsobjekt.

Nahezu jede Anwendung kopiert Felder aus einem Formular oder einem JSON-Körper in ein Objekt, das anschließend in die Datenbank geht. Frameworks machen das verlockend kurz: Eine einzige Zeile setzt sämtliche Felder aus der Anfrage auf einmal in das Modell. Genau dort entsteht Mass Assignment, denn von diesem Moment an bestimmt der Absender der Anfrage, welche Felder mitkommen, und nicht Sie.

Was ist Mass Assignment?

Von Mass Assignment spricht man, wenn ein Angreifer Felder eines Objekts überschreiben kann, die nie für ihn gedacht waren, weil die Anwendung einen vollständigen Anfragekörper ungefiltert auf ein Modell bindet. In anderen Ökosystemen heißt dasselbe Problem Autobinding, Object Injection oder Over-Posting; die OWASP API Security Top 10 führen es als Broken Object Property Level Authorization.

Ein Vergleich aus dem Alltag: Sie treten einem Sportverein bei und füllen ein Formular mit Namen, Anschrift und Geburtsdatum aus. Unten auf diesem Formular stehen zwei Felder, die sonst nur die Mitgliederverwaltung ausfüllt, nämlich “Beitrag bezahlt” und “Vorstandsmitglied”. Überträgt die Sachbearbeitung das ausgefüllte Formular ungelesen in das Mitgliedersystem, entscheidet der Antragsteller selbst, ob er bezahlt hat und ob er im Vorstand sitzt. Mass Assignment ist genau diese ungeprüfte Übernahme.

Der Kern des Problems ist, dass ein Modell fast immer mehr Felder hat, als das Formular zeigt. Ein Nutzerdatensatz enthält nicht nur Namen und E-Mail-Adresse, sondern auch Felder wie isAdmin, rolle, emailBestaetigt, organisationId, guthaben oder passwortHash. Das Formular zeigt drei davon, die Bindung nimmt alle an. Ein Angreifer muss nichts umgehen: Er ergänzt schlicht ein Feld in dem JSON, das er ohnehin schon sendet.

Damit gehört Mass Assignment zu Broken Access Control. Nicht die Authentifizierung versagt hier, sondern die Autorisierung auf Feldebene. Der Nutzer darf sein eigenes Profil bearbeiten, und das ist richtig so. Er darf nur nicht jedes Feld dieses Profils bearbeiten, und genau diese Unterscheidung fehlt.

Wie funktioniert ein Mass-Assignment-Angriff?

Nehmen Sie einen Endpunkt, an dem ein angemeldeter Nutzer sein eigenes Profil aktualisiert. Der Entwickler lädt das Nutzerobjekt, kopiert den Anfragekörper darauf und speichert es wieder.

Verwundbar:

app.patch("/api/nutzer/ich", erfordertAnmeldung, async (req, res) => {
  const nutzer = await Nutzer.findById(req.session.nutzerId);

  // Der gesamte Anfragekörper wird in das Modell geschrieben
  Object.assign(nutzer, req.body);
  await nutzer.save();

  res.json({ id: nutzer.id, email: nutzer.email });
});

Auf den ersten Blick wirkt die Autorisierung in Ordnung: erfordertAnmeldung hält anonyme Besucher fern, und der Nutzer bearbeitet stets nur seinen eigenen Datensatz. Dennoch entscheidet req.body vollständig darüber, welche Spalten geschrieben werden. Der Angreifer öffnet seine Profilseite, fängt die Anfrage ab und ergänzt zwei Felder:

PATCH /api/nutzer/ich HTTP/1.1
Host: app.beispiel.de
Content-Type: application/json
Cookie: sid=b1c9f4a2

{"anzeigename":"Mo","isAdmin":true,"guthaben":100000}

Diese Feldnamen muss er nicht raten. Sie stehen meist unmittelbar in der Antwort des lesenden Endpunkts für dasselbe Objekt, in einem OpenAPI-Schema, in einer GraphQL-Introspektion oder im JavaScript-Bündel, das die Verwaltungsoberfläche ausliefert. Nach dieser einen Anfrage ist der Angreifer Administrator. Es gibt keine Injektion, keinen Exploit und kein ungewöhnliches Verkehrsmuster: Es ist eine gültige Anfrage an einen gültigen Endpunkt mit zwei zusätzlichen Feldern.

Die Abhilfe besteht darin, dem Anfragekörper nicht als Ganzes zu vertrauen, sondern ausdrücklich herauszugreifen, was ein Nutzer an sich selbst ändern darf. Dafür nutzen Sie ein DTO, ausgeschrieben ein Data Transfer Object: einen eigenen Typ, der genau die zulässigen Felder beschreibt und den Rest ablehnt.

Sicher:

import { z } from "zod";

// DTO: genau die Felder, die ein Nutzer an sich selbst ändern darf
const ProfilAenderung = z
  .object({
    anzeigename: z.string().min(1).max(80),
    sprache: z.enum(["nl", "en", "de"])
  })
  .partial()
  .strict(); // ein unbekanntes Feld erzeugt einen Fehler, statt still durchzugehen

app.patch("/api/nutzer/ich", erfordertAnmeldung, async (req, res) => {
  const geprueft = ProfilAenderung.safeParse(req.body);
  if (!geprueft.success) {
    return res.status(400).json({ fehler: "Unzulässiges Feld im Anfragekörper" });
  }

  const nutzer = await Nutzer.findById(req.session.nutzerId);

  // geprueft.data enthält ausschließlich freigegebene Felder
  Object.assign(nutzer, geprueft.data);
  await nutzer.save();

  res.json({ id: nutzer.id, email: nutzer.email });
});

Zwei Dinge haben sich geändert. geprueft.data enthält ausschließlich freigegebene Felder, sodass isAdmin das Modell nie erreicht, gleich was im Anfragekörper stand. Und dank strict wird ein unbekanntes Feld aktiv abgewiesen statt stillschweigend verworfen. Das ist der Unterschied zwischen einem Angreifer, der ungestört weiter probieren kann, und einer Protokollzeile, die Ihnen sagt, dass es jemand versucht.

Dieselbe Idee gibt es in jedem Framework unter eigenem Namen: Strong Parameters in Rails, die Eigenschaft fillable in Laravel, eine eigene DTO-Klasse in Spring und eine ausdrückliche Bindungsliste in ASP.NET Core.

Eine Positivliste gilt je Endpunkt und je Rolle. Wer ein DTO für die Verwaltungsoberfläche und für die Selbstverwaltung wiederverwendet, holt sich das gefährliche Feld durch die Seitentür zurück. Achten Sie auch auf die harmlos wirkenden Felder: organisationId, teamId oder rolleId verschieben ein Konto in einen anderen Mandanten oder eine andere Rolle, ohne dass isAdmin je im Anfragekörper auftaucht.

Welche Auswirkungen hat Mass Assignment?

Die Auswirkung hängt vollständig davon ab, welches Feld beschreibbar ist, weshalb der Schweregrad von mittel bis hoch reicht. Lässt sich nur ein kosmetisches Feld überschreiben, ein Zeitstempel oder eine interne Notiz, bleibt es ein Integritätsproblem ohne unmittelbare Folge. Führt das Modell ein Rechtefeld, ist das Ergebnis eine vollständige Privilege Escalation: Der Angreifer macht sich mit einer Anfrage zum Administrator und erbt alles, was diese Rolle darf.

Dazwischen liegt die Kategorie, die wirtschaftlich weh tut. Ein beschreibbares guthaben, preis oder rabatt macht aus einem Bestellvorgang eine Selbstbedienung, bei der der Angreifer sein eigenes Guthaben oder seinen eigenen Preis festlegt. Eine beschreibbare organisationId oder mandantId hebt die Trennung zwischen Kunden in einer mandantenfähigen Umgebung auf, was in der Praxis Zugriff auf die Daten eines anderen Kunden bedeutet. Und ein beschreibbares emailBestaetigt, email oder passwortToken öffnet einen Weg zur Kontoübernahme.

Geschäftlich summiert sich das zu manipulierten Umsätzen, einem Datenleck mit Meldepflicht nach der DSGVO und einem Integritätsproblem, das sich im Nachhinein schwer rekonstruieren lässt. Letzteres wird unterschätzt: Weil die Anfrage vollkommen gewöhnlich aussieht, halten viele Protokolldateien nicht fest, wann und wie ein Konto zum Administrator wurde.

Wie spürt man Mass Assignment auf?

Beginnen Sie beim lesenden Modell. Rufen Sie ein Objekt über die Schnittstelle ab und legen Sie die Felder der Antwort neben die Felder, die der zugehörige schreibende Endpunkt offiziell annimmt. Jedes Feld, das Sie zurückbekommen, aber nicht senden sollen, ist ein Kandidat. Weitere Quellen sind eine OpenAPI-Beschreibung, eine GraphQL-Introspektion, verborgene Formularfelder, das JavaScript-Bündel der Verwaltungsoberfläche und die Modelldefinitionen im Quellcode.

Danach wird geprüft, indem einer ansonsten gewöhnlichen Anfrage ein zusätzliches Feld beigefügt und das Objekt erneut abgerufen wird, um zu sehen, ob der Wert wirklich bestehen blieb. Achten Sie auf den Unterschied zwischen ignoriert und übernommen: Eine Antwort mit 200 sagt nichts, der zweite Abruf schon. Ändert sich der Wert in der Antwort nicht sichtbar, wohl aber das Verhalten, etwa weil plötzlich ein Verwaltungsmenü erscheint, ist die Wirkung dennoch vorhanden.

Automatische Scanner schneiden hier schlecht ab, denn sie wissen nicht, was ein Feldname bedeutet, und können die Folgen einer gelungenen Bindung nicht beurteilen. Das Auffinden ist Handarbeit und setzt Kenntnis des Datenmodells und der Rollen in der Anwendung voraus. AssistSec deckt Mass Assignment bei Penetrationstests von Schnittstellen und Webanwendungen ab und zeigt je Befund, welches Feld geschrieben wurde und was ein Angreifer damit erreicht.

Wie verhindert man Mass Assignment?

  • Binden Sie nie unmittelbar auf Ihr Datenbankmodell. Lassen Sie eine Anfrage in einem DTO landen, das nur die Felder dieses einen Endpunkts kennt, und übertragen Sie danach Feld für Feld in das Modell.
  • Arbeiten Sie mit einer Positivliste, nicht mit einer Sperrliste. Eine Liste verbotener Felder vergisst das Feld, das nächsten Monat jemand zum Modell hinzufügt; eine Liste erlaubter Felder nicht.
  • Weisen Sie unbekannte Felder ausdrücklich ab. Antworten Sie mit einer 400 und halten Sie den Versuch fest. Sie stillschweigend zu verwerfen ist sicherer als sie anzunehmen, liefert Ihnen aber kein Signal.
  • Halten Sie Rechtefelder aus jedem nutzerseitigen Schreibweg heraus. Rollen, Guthaben, Preise und Bestätigungskennzeichen werden serverseitig gesetzt, über eine eigene Aktion mit eigener Autorisierungsprüfung.
  • Machen Sie die Positivliste rollenabhängig. Was ein Administrator schreiben darf, ist eine andere Menge als das, was der Eigentümer eines Datensatzes schreiben darf; nehmen Sie zwei Schemata statt eines Schemas mit einer Ausnahme darin.
  • Schalten Sie den Schutz Ihres Frameworks ein und halten Sie ihn aktuell. Strong Parameters in Rails, fillable in Laravel und ausdrückliche Bindungslisten in Spring und ASP.NET Core wirken nur, wenn jedes neue Modell darin aufgenommen wird.
  • Protokollieren Sie Änderungen an heiklen Feldern. Ein Prüfeintrag für jede Änderung einer Rolle, eines Guthabens oder eines Bestätigungskennzeichens macht Missbrauch nachträglich sichtbar und ist oft die einzige Spur, die bleibt.

Quellen

Häufige Fragen

Worin unterscheiden sich Mass Assignment und IDOR?

Bei IDOR erreicht ein Angreifer ein ganzes Objekt, das ihm nicht gehört, meist indem er eine Kennung in der URL ändert. Bei Mass Assignment bleibt er auf seinem eigenen Objekt, beschreibt darin aber ein Feld, das er nicht ändern darf. IDOR ist ein Autorisierungsfehler auf Objektebene, Mass Assignment einer auf Feldebene. Beide gehören zu Broken Access Control und werden nebeneinander geprüft.

Schützen moderne Frameworks standardmäßig davor?

Teilweise, und nur wenn Sie den Schutz auch verwenden. Rails hat Strong Parameters, Laravel die Eigenschaft fillable, Spring und ASP.NET Core unterstützen ausdrückliche Bindungslisten und eigene Übertragungsklassen. Diese Mechanismen greifen je Modell oder je Endpunkt und müssen ausdrücklich gesetzt werden, sodass ein neues Modell oder ein rasch ergänzter Endpunkt zunächst außen vor bleibt. Der Schutz ist nur so aktuell wie der letzte Entwickler, der daran gedacht hat.

Genügt es, ein heikles Feld nicht zu dokumentieren?

Nein. Ein nicht dokumentiertes Feld wird gebunden, sobald sein Name bekannt ist, und dieser Name steht meist in der Antwort des lesenden Endpunkts, in einer GraphQL-Introspektion oder im JavaScript-Bündel der Verwaltungsoberfläche. Es aus der Dokumentation zu entfernen ändert nichts am Verhalten des Servers. Nur eine serverseitige Positivliste ist eine echte Maßnahme.

Wie prüfe ich, ob ein Endpunkt für Mass Assignment anfällig ist?

Rufen Sie das Objekt zuerst mit einem GET ab und notieren Sie jedes Feld in der Antwort. Ergänzen Sie dann eine ansonsten gewöhnliche Schreibanfrage um ein heikel wirkendes Feld, etwa isAdmin oder rolle, und rufen Sie das Objekt erneut ab. Eine Antwort mit 200 beweist nichts; erst der zweite Abruf zeigt, ob der Wert wirklich gespeichert wurde. Achten Sie auch auf mittelbare Wirkungen, etwa ein Verwaltungsmenü, das nach der Anfrage auftaucht.

Verwandte Artikel

Zum Suchen / drücken · Esc