Unsachgemäße Kontrolle der Modifikation dynamisch bestimmter Objektattribute
Beschreibung
Unsachgemäße Kontrolle der Modifikation dynamisch bestimmter Objektattribute (auch bekannt als Mass Assignment, Autobinding oder Object Injection) tritt auf, wenn Software Eingaben erhält, die mehrere Objektattribute für die Initialisierung oder Aktualisierung angeben, aber nicht ordnungsgemäß einschränkt, welche Attribute modifiziert werden können. Wenn eine Anwendung blindlings alle eingehenden Parameter den Attributen eines Objekts zuweist, können Angreifer interne oder sensible Attribute modifizieren, die nicht extern zugänglich sein sollten, wie Berechtigungsstufen, Kontostände oder administrative Flags.
Risiko
Diese Schwachstelle kann zu schwerwiegenden Sicherheitsverletzungen führen. Angreifer können Privilegien eskalieren, indem sie Rollen- oder Berechtigungsattribute modifizieren. Finanzsysteme können kompromittiert werden, indem Saldo- oder Transaktionsfelder manipuliert werden. Die Authentifizierung kann umgangen werden, indem Verifizierungs-Flags geändert werden. In JavaScript-Umgebungen können Prototype-Pollution-Angriffe alle Objekte in der Anwendung beeinflussen. Das Risiko wird verstärkt, weil Entwickler oft davon ausgehen, dass bestimmte Attribute "intern" und geschützt sind, während der Binding-Mechanismus sie in Wirklichkeit für Modifikationen freigibt. Diese Schwachstelle ist besonders häufig in Web-Frameworks, die automatisches Parameter-Binding bereitstellen.
Lösung
Implementieren Sie explizites Attribut-Whitelisting, das genau angibt, welche Attribute durch externe Eingaben modifiziert werden können. Verwenden Sie vom Framework bereitgestellte Schutzmechanismen (Strong Parameters in Rails, @JsonIgnore in Java, etc.). Trennen Sie DTOs (Data Transfer Objects) von Domain-Modellen, um zu kontrollieren, was gebunden werden kann. Binden Sie niemals direkt Request-Parameter an interne Domain-Objekte. Für JavaScript filtern Sie gefährliche Schlüssel wie __proto__, constructor und prototype heraus. Implementieren Sie Eingabevalidierung, die unerwartete Attribute ablehnt. Erwägen Sie die Verwendung unveränderlicher Objekte, wo es angemessen ist.
Häufige Auswirkungen
| Auswirkung | Details |
|---|---|
| Integrität | Bereich: Integrität Anwendungsdaten modifizieren - Angreifer können sensible Daten oder Programmvariablen ändern, die geschützt sein sollten. |
| Integrität | Bereich: Integrität Unautorisierten Code oder Befehle ausführen - Durch Prototype Pollution oder Objektmanipulation können Angreifer die Ausführungslogik ändern. |
| Zugriffskontrolle | Bereich: Zugriffskontrolle Privilegien erlangen oder Identität annehmen - Das Modifizieren von Rollen- oder Berechtigungsattributen ermöglicht Privilegieneskalation. |
Beispielcode
Anfälliger Code
# Anfällig: Mass Assignment in Rails (Pre-Rails 4 Stil)
class User < ActiveRecord::Base
# Alle Attribute sind standardmäßig mass-assignable
end
class UsersController < ApplicationController
def create
# Anfällig: Alle params direkt zugewiesen
@user = User.new(params[:user])
@user.save
end
def update
@user = User.find(params[:id])
# Anfällig: Kann jedes Attribut inkl. :admin, :role modifizieren
@user.update(params[:user])
end
end
# Angriff: POST /users mit params: user[name]=hacker&user[admin]=true
# Anfällig: Django Mass Assignment
from django.db import models
class User(models.Model):
username = models.CharField(max_length=100)
email = models.EmailField()
is_admin = models.BooleanField(default=False)
balance = models.DecimalField(max_digits=10, decimal_places=2)
def vulnerable_update_user(request, user_id):
user = User.objects.get(id=user_id)
# Anfällig: Alle POST-Daten dem Modell zugewiesen
for key, value in request.POST.items():
setattr(user, key, value)
user.save()
# Angriff: POST mit is_admin=True oder balance=999999
// Anfällig: Spring Autobinding
@Controller
public class VulnerableUserController {
@PostMapping("/register")
public String register(User user) {
// Anfällig: Alle Request-Parameter an User gebunden
// Angreifer kann user.role=ADMIN setzen
userRepository.save(user);
return "success";
}
}
public class User {
private String username;
private String password;
private String role = "USER"; // Kann überschrieben werden!
private boolean verified = false; // Kann überschrieben werden!
// Getter und Setter für alle Felder
}
// Anfällig: Prototype Pollution via Mass Assignment
function vulnerableUpdateConfig(config, updates) {
// Anfällig: Kopiert alle Eigenschaften ohne Filterung
Object.assign(config, updates);
}
// Angriff:
const config = { theme: 'dark' };
const maliciousUpdates = JSON.parse(
'{"__proto__": {"polluted": true}}'
);
vulnerableUpdateConfig(config, maliciousUpdates);
// Jetzt haben ALLE Objekte die .polluted Eigenschaft!
console.log({}.polluted); // true
// Anfällig: PHP Object Injection via unserialize
<?php
class User {
public $username;
public $role = 'user';
public $isAdmin = false;
}
// Anfällig: Deserialisierung von Benutzereingaben
$userData = unserialize($_COOKIE['user_data']);
// Angreifer erstellt serialisierten String:
// O:4:"User":3:{s:8:"username";s:6:"hacker";s:4:"role";s:5:"admin";s:7:"isAdmin";b:1;}
// Dies erstellt User mit role=admin, isAdmin=true
?>
Korrigierter Code
# Korrigiert: Strong Parameters in Rails 4+
class UsersController < ApplicationController
def create
@user = User.new(user_params)
@user.save
end
def update
@user = User.find(params[:id])
@user.update(user_params)
end
private
# Korrigiert: Explizite Whitelist erlaubter Parameter
def user_params
params.require(:user).permit(:name, :email, :password)
# :admin, :role, :balance sind NICHT erlaubt
end
end
# Korrigiert: Explizite Attribut-Whitelist
from django.db import models
class User(models.Model):
username = models.CharField(max_length=100)
email = models.EmailField()
is_admin = models.BooleanField(default=False)
balance = models.DecimalField(max_digits=10, decimal_places=2)
# Definieren, welche Felder über API aktualisiert werden können
ALLOWED_UPDATE_FIELDS = {'username', 'email'}
def fixed_update_user(request, user_id):
user = User.objects.get(id=user_id)
# Korrigiert: Nur erlaubte Felder aktualisieren
for key, value in request.POST.items():
if key in User.ALLOWED_UPDATE_FIELDS:
setattr(user, key, value)
else:
# Versuchte Modifikation eines verbotenen Felds loggen
logger.warning(f"Abgelehnte Feldaktualisierung: {key}")
user.save()
# Besser: Serializers verwenden (Django REST Framework)
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ['username', 'email'] # Nur diese sind zuweisbar
read_only_fields = ['is_admin', 'balance'] # Niemals zuweisbar
// Korrigiert: DTO mit expliziten Feldern verwenden
@Controller
public class FixedUserController {
@PostMapping("/register")
public String register(@Valid UserRegistrationDTO dto) {
// Korrigiert: Nur DTO-Felder werden gebunden
User user = new User();
user.setUsername(dto.getUsername());
user.setPassword(passwordEncoder.encode(dto.getPassword()));
user.setRole("USER"); // Intern gesetzt, nicht aus Eingabe
user.setVerified(false); // Intern gesetzt
userRepository.save(user);
return "success";
}
}
// DTO enthält nur erlaubte Felder
public class UserRegistrationDTO {
@NotBlank
private String username;
@NotBlank
private String password;
// Keine role, kein verified - diese können nicht vom Benutzer gesetzt werden
}
// Oder @JsonIgnore auf sensiblen Feldern verwenden
public class User {
private String username;
private String password;
@JsonIgnore // Wird niemals aus JSON gebunden
private String role = "USER";
@JsonIgnore
private boolean verified = false;
}
// Korrigiert: Gefährliche Eigenschaften filtern
function fixedUpdateConfig(config, updates) {
const FORBIDDEN_KEYS = ['__proto__', 'constructor', 'prototype'];
// Korrigiert: Gefährliche Schlüssel herausfiltern
const safeUpdates = Object.fromEntries(
Object.entries(updates).filter(([key]) =>
!FORBIDDEN_KEYS.includes(key)
)
);
Object.assign(config, safeUpdates);
}
// Besser: Whitelist erlaubter Schlüssel
function saferUpdateConfig(config, updates) {
const ALLOWED_KEYS = ['theme', 'language', 'timezone'];
const safeUpdates = Object.fromEntries(
Object.entries(updates).filter(([key]) =>
ALLOWED_KEYS.includes(key)
)
);
Object.assign(config, safeUpdates);
}
// Korrigiert: Explizite Eigenschaftszuweisung
<?php
class User {
public $username;
private $role = 'user';
private $isAdmin = false;
public function setUsername($username) {
$this->username = $username;
}
// Kein öffentlicher Setter für role oder isAdmin
// Diese können nur durch vertrauenswürdige interne Methoden gesetzt werden
public function promoteToAdmin() {
// Dies würde eine ordnungsgemäße Autorisierungsprüfung erfordern
$this->role = 'admin';
$this->isAdmin = true;
}
}
// Korrigiert: Niemals Benutzereingaben deserialisieren
// JSON stattdessen verwenden
$userData = json_decode($_COOKIE['user_data'], true);
$user = new User();
// Nur erlaubte Felder setzen
if (isset($userData['username'])) {
$user->setUsername($userData['username']);
}
// role und isAdmin können nicht aus externen Eingaben gesetzt werden
?>
CVE-Beispiele
- CVE-2024-3283: LLM-Anwendung ermöglichte Modifikation sensibler Variablen durch Mass Assignment.
- CVE-2012-2054: Mass-Assignment-Schwachstelle in Webanwendung ermöglichte Privilegieneskalation.
- CVE-2012-2055: Mass Assignment über URL-Parameter in Versionskontrollsystem.
- CVE-2008-7310: E-Commerce-Anwendung ermöglichte Zahlungsumgehung durch Mass Assignment.
- CVE-2013-1465: PHP-unserialize-Schwachstelle ermöglichte Object-Injection-Angriff.
Verwandte CWEs
- CWE-913: Improper Control of Dynamically-Managed Code Resources (Eltern)
- CWE-1321: Improperly Controlled Modification of Object Prototype Attributes ('Prototype Pollution') (Kind)
- CWE-502: Deserialization of Untrusted Data (verwandt)
Referenzen
- MITRE Corporation. "CWE-915: Improperly Controlled Modification of Dynamically-Determined Object Attributes." https://cwe.mitre.org/data/definitions/915.html
- OWASP. "Mass Assignment Cheat Sheet." https://cheatsheetseries.owasp.org/cheatsheets/Mass_Assignment_Cheat_Sheet.html
- Ruby on Rails Security Guide. "Mass Assignment."