영웅 배경, 구분선 없음
지침

Massenzuweisung

Im Moment gehen wir auf Sicherheitslücken bei Massenzuweisungen ein und erläutern, wie sie aussehen, zusammen mit einigen Möglichkeiten, sie zu vermeiden. Zunächst eine kurze Zusammenfassung:

Massenzuweisungen sind eine Sicherheitslücke, bei der API-Endpunkte nicht einschränken, welche Eigenschaften des zugehörigen Objekts von einem Benutzer geändert werden können.

Diese Sicherheitsanfälligkeit kann auftreten, wenn eine Bibliothek/ein Framework verwendet wird, das die automatische Bindung von HTTP-Parametern an ein Modell ermöglicht, das dann ohne Validierung verwendet wird.

Die Verwendung der automatischen Bindung von einer Anfrage an ein Objekt kann manchmal äußerst hilfreich sein, kann aber auch zu Sicherheitsproblemen führen, wenn das Modell Eigenschaften hat, auf die der Benutzer nicht zugreifen soll.

Beispiel

Wir werden das Beispiel einer Webseite verwenden, auf der ein Benutzer Details wie seinen Namen, seine E-Mail-Adresse und andere ähnliche Dinge ändern kann. Wir haben ein Benutzermodell definiert als:

öffentliche Klasse UserModel {

öffentliche lange ID {get; set;}
öffentliche Zeichenfolge Name {get; set;}
öffentliche Zeichenfolge passwordHash {get; set;}
öffentliche Zeichenfolge emailAddress {get; set;}
public bool isAdmin {get; set;}

}

Der Frontend-Teil definiert ein Formular wie folgt. Beachten Sie das Fehlen des Werts `isAdmin`:

<form method="POST">
<input name="Id" type="hidden">
<input name="Name" type="text">
<input name="EmailAddress" type="text">
<input type="submit">
</form>

Der Controller definiert einen Endpunkt wie folgt. Mit dem `UserModel` als Parameter ordnet unser Framework automatisch die jeweiligen Eigenschaften für uns diesem Modell zu:

[Http-Beitrag]
public bool updateUser (UserModel-Modell)
{
//Stellen Sie sicher, dass der Benutzer nur sich selbst aktualisiert
model.ID = request.user.userId;

var success = UserService.updateUser (Modell);

Erfolg zurückgeben;
}

Von hier aus können wir davon ausgehen, dass die Methode 'UserService.updateUser' keine weitere Validierung in Bezug auf die Autorisierung durchführt und einfach das bereitgestellte Benutzerobjekt speichert.

(Wenn für eine Eigenschaft kein Wert angegeben wird, behält sie einfach den vorhandenen Wert bei)

Das bedeutet, dass ein Benutzer eine Anfrage mit dem 'isAdmin' einreichen könnte, was den aktuellen Wert überschreiben und den Benutzer wie folgt zu einem Admin machen würde:

<form method="POST">
<input name="Id" type="hidden" value="666">
<input name="Name" type="text" value="Bad guy">
<input name="EmailAddress" type="text" value="hacker@attacker.com">
<input name="IsAdmin" type="hidden" value="true">
<input type="submit">
</form>

Strategien zur Schadensbegrenzung

Im Folgenden finden Sie einige Strategien zur Risikominderung, die Sie berücksichtigen sollten, wenn es darum geht, Sicherheitslücken im Zusammenhang mit Massenzuweisungen zu vermeiden.

Vermeiden Sie die Wiederverwendung von Datenmodellen für Anforderungsmodelle

Es ist wichtig, dass Sie Ihre Datenmodelle (die möglicherweise in einer Datenbank gespeichert sind) von den Modellen trennen, die bei der Kommunikation mit einem Kunden verwendet werden. Die Bearbeitung eines Formulars an einen Controller ist ein ganz anderes Anliegen als die persistente Speicherung von Daten in einer Datenbank und deren Darstellung in der Datenbank. Dadurch entsteht ein weitaus höheres Maß an Kopplung zwischen dem Frontend und der Persistenzschicht, als es gut wäre.

Seien Sie in Ihren Zuordnungen explizit

Das Problem mit der automatischen Bindung (oder Zuordnung) besteht darin, dass das Fehlen expliziter Zuordnungen es einfach macht, Eigenschaften verfügbar zu machen, auf die im Modell nicht zugegriffen werden soll. Indem Sie bei den Zuordnungen zwischen Anforderungsmodellen und dem Rest Ihres Backends explizit sind, können Sie diese Art von Gefährdung von Anfang an verhindern.

Dies könnte durch die Verwendung verschiedener Modelle für Anfragen und Daten erreicht werden. Dies hindert Sie nicht daran, einen automatischen Mapper zwischen der Anfrage und dem Datenmodell zu verwenden, da Ihr Anforderungsmodell keine Eigenschaften verfügbar machen sollte, die für die spezifische Anfrage nicht zulässig sind.

Weitere Beispiele

Im Folgenden finden Sie einige zusätzliche Beispiele in verschiedenen Sprachen, wie das aussehen kann.

C# - unsicher

Benutzer der öffentlichen Klasse {
öffentliche lange ID {get; set;}
öffentliche Zeichenfolge Vorname {get; set;}
öffentliche Zeichenfolge LastName {get; set;}
öffentliche Zeichenfolge passwordHash {get; set;}
öffentliche Zeichenfolge Country {get; set;}
öffentliche Zeichenfolge Role {get; set;}
}

[Http-Beitrag]
public ViewResult Edit (Benutzerbenutzer)
{
//Speichert den Benutzer einfach wie angegeben
userService.updateUser (Benutzer);
gib Ok () zurück;
}

C# - sicher

Benutzer der öffentlichen Klasse {
öffentliche lange ID {get; set;}
öffentliche Zeichenfolge Vorname {get; set;}
öffentliche Zeichenfolge LastName {get; set;}
öffentliche Zeichenfolge passwordHash {get; set;}
öffentliche Zeichenfolge Country {get; set;}
öffentliche Zeichenfolge Role {get; set;}
}
öffentliche Klasse updateUserViewModel {

öffentliche Zeichenfolge Vorname {get; set;}
öffentliche Zeichenfolge LastName {get; set;}
öffentliche Zeichenfolge Country {get; set;}
}

[Http-Beitrag]
public ViewResult Edit (updateUserViewModel UserModel)
{
var user = Request.User;

user.firstName = userModel.firstName;
user.lastName = userModel.lastName;
user.country = UserModel.COUNTRY;

userService.updateUser (Benutzer);

gib Ok () zurück;
}

C# - Alternative - schließt Parameter aus

Benutzer der öffentlichen Klasse {
öffentliche lange ID {get; set;}
öffentliche Zeichenfolge Vorname {get; set;}
öffentliche Zeichenfolge LastName {get; set;}
öffentliche Zeichenfolge passwordHash {get; set;}
öffentliche Zeichenfolge Country {get; set;}
öffentliche Zeichenfolge Role {get; set;}
}

[Http-Beitrag]
public ViewResult Edit ([Bind (Include = „Vorname, Nachname, Land“)] Benutzerbenutzer)
{
wenn (Request.User.Id! = Benutzer.ID) {
return Forbidden („Änderung eines anderen Benutzers beantragen“);
}
var existingUser = Request.User;
user.passwordHash = existierender Benutzer.passwordHash;
user.role = existierender Benutzer.Role;

userService.updateUser (Benutzer);

gib Ok () zurück;
}

Java - unsicher

Benutzer der öffentlichen Klasse {
öffentliche Münzhilfe;
öffentliche Zeichenfolge Vorname;
öffentliche Zeichenfolge Nachname;
öffentliche Zeichenfolge passwordHash;
öffentliches Zeichenkettenland;
öffentliche String-Rolle;
}

@RequestMapping (Wert = „/updateUser“, Methode = requestMethod.POST)
öffentliche Zeichenfolge updateUser (Benutzerbenutzer) {
userService.update (Benutzer);
gib „UserUpdatedPage“ zurück;
}


Java - sicher

öffentliche Klasse UserViewModel {
öffentliche Zeichenfolge Vorname;
öffentliche Zeichenfolge Nachname;
öffentliches Zeichenkettenland;
}
Benutzer der öffentlichen Klasse {
öffentliche Münzhilfe;
öffentliche Zeichenfolge Vorname;
öffentliche Zeichenfolge Nachname;
öffentliche Zeichenfolge passwordHash;
öffentliches Zeichenkettenland;
öffentliche String-Rolle;
}
@RequestMapping (Wert = „/updateUser“, Methode = requestMethod.POST)
öffentliche Zeichenfolge updateUser (@AuthenticationPrincipal User currentUser, UserViewModel UserViewModel) {
currentUser.firstName = userViewModel.firstName;
currentUser.lastName = userViewModel.lastName;
currentUser.country = UserViewModel.Country;

userService.update (aktueller Benutzer);
gib „UserUpdatedPage“ zurück;
}

Javascript - unsicher

app.get ('/user/update', (erforderlich, res) => {
var user = req.user;

Object.assign (Benutzer, req.body);

userService.update (Benutzer);

gibt „Benutzer wurde aktualisiert“ zurück;
})

Javascript - sicher

app.get ('/user/update', (erforderlich, res) => {
var user = req.user;

user.firstName = req.body.firstName;
user.lastName = req.body.LastName;
user.country = req.body.country;

userService.update (Benutzer);

gibt „Benutzer wurde aktualisiert“ zurück;
})

Python - Unsicher

@app .route („/user/update“, methods= ['POST'])
def update_user ():

user = request.user
form = request.form.to_dict (flat=TRUE)

für Schlüssel, Wert in form.items ():
setattr (Benutzer, Schlüssel, Wert)

userService.updateUser (Benutzer)

Weiterleitung zurückgeben („/user“, Code=201)

Python - Sicher

@app .route („/user/update“, methods= ['POST'])
def update_user ():

user = request.user
form = Anforderungsformular

user.firstName = form.FirstName
user.lastName = form.lastName

userService.updateUser (Benutzer)
Weiterleitung zurückgeben („/user“, Code=201)