ASP.NET-Fehlkonfiguration: Unsachgemäße Modellvalidierung
Beschreibung
ASP.NET-Fehlkonfiguration: Unsachgemäße Modellvalidierung tritt auf, wenn eine ASP.NET-Anwendung das vom ASP.NET MVC- oder ASP.NET Core-Framework bereitgestellte Modellvalidierungs-Framework nicht verwendet oder falsch verwendet. ASP.NET bietet robuste integrierte Modellvalidierung durch Datenannotationen und den ModelState-Validierungsmechanismus. Wenn Entwickler diese Validierungsfunktionen nicht implementieren oder sie falsch implementieren, werden Benutzereingaben möglicherweise nicht ordnungsgemäß validiert, was zu verschiedenen Sicherheitsschwachstellen einschließlich Injection-Angriffen führt.
Risiko
Unsachgemäße Modellvalidierung in ASP.NET-Anwendungen hat erhebliche Sicherheitsauswirkungen. Ungeprüfte Eingaben können zu SQL-Injection-Angriffen führen. Cross-Site-Scripting (XSS)-Schwachstellen werden wahrscheinlicher. Geschäftslogik kann durch ungültige Eingaben umgangen werden. Prozesssteuerungsangriffe können möglich sein. Mass-Assignment-Schwachstellen können auftreten. Die Datenintegrität wird kompromittiert. Der Anwendungszustand kann beschädigt werden. Autorisierungsprüfungen können umgangen werden.
Lösung
Verwenden Sie immer das ASP.NET-Modellvalidierungs-Framework. Wenden Sie Datenannotations-Attribute auf Modelleigenschaften an ([Required], [StringLength], [Range], etc.). Prüfen Sie ModelState.IsValid vor der Verarbeitung von Eingaben. Implementieren Sie benutzerdefinierte Validierungsattribute für komplexe Regeln. Verwenden Sie [ValidateAntiForgeryToken] für Formularübermittlungen. Konfigurieren Sie Modellbindung, um erlaubte Eigenschaften einzuschränken. Verwenden Sie das [Bind]-Attribut oder View-Models, um Over-Posting zu verhindern. Aktivieren Sie clientseitige Validierung für bessere UX. Implementieren Sie globale Fehlerbehandlung für Validierungsfehler. Halten Sie das ASP.NET-Framework aktuell.
Häufige Auswirkungen
| Auswirkung | Details |
|---|---|
| Integrität | Bereich: Integrität Unerwarteter Zustand - Ungeprüfte Eingaben führen zu Cross-Site-Scripting, Prozesssteuerung, SQL-Injection-Schwachstellen und anderen Injection-Angriffen. |
Beispielcode
Verwundbarer Code
// Verwundbar: ASP.NET MVC ohne Modellvalidierung
public class UserController : Controller
{
// Verwundbar: Keine Modellvalidierung
[HttpPost]
public ActionResult Register(string username, string email, string password)
{
// Direkte Verwendung von Eingaben ohne Validierung
// Keine Längenprüfungen, Formatvalidierung oder Pflichtfeldprüfungen
var user = new User
{
Username = username, // Könnte null, leer oder zu lang sein
Email = email, // Könnte ungültiges E-Mail-Format sein
Password = password // Könnte schwach oder leer sein
};
// SQL-Injection möglich wenn keine parametrisierten Abfragen verwendet
db.Users.Add(user);
db.SaveChanges();
return RedirectToAction("Success");
}
// Verwundbar: Modellbindung ohne Validierungsprüfung
[HttpPost]
public ActionResult UpdateProfile(UserProfile profile)
{
// ModelState.IsValid wird nicht geprüft!
// Auch wenn das Modell Validierungsattribute hat, werden sie nicht durchgesetzt
db.UserProfiles.Update(profile);
db.SaveChanges();
return RedirectToAction("Profile");
}
// Verwundbar: Over-Posting-Angriff möglich
[HttpPost]
public ActionResult EditUser(User user)
{
// Benutzer kann zusätzliche Felder wie "IsAdmin = true" POSTen
// die an das Modell gebunden werden
db.Entry(user).State = EntityState.Modified;
db.SaveChanges(); // Kann Felder aktualisieren die nicht benutzereditierbar sein sollten
return RedirectToAction("Index");
}
}
// Modell ohne Validierungsattribute
public class User
{
public int Id { get; set; }
public string Username { get; set; } // Keine Validierung
public string Email { get; set; } // Keine Validierung
public string Password { get; set; } // Keine Validierung
public bool IsAdmin { get; set; } // Kann per Mass-Assignment gesetzt werden!
}
// Verwundbar: ASP.NET Core API ohne Validierung
[ApiController]
[Route("api/[controller]")]
public class ProductsController : ControllerBase
{
// Verwundbar: [ApiController] automatische Validierung nicht verwendet
// oder ModelState nicht manuell geprüft
[HttpPost]
public async Task<IActionResult> Create(ProductDto product)
{
// Keine Validierung - product könnte ungültige Daten haben
var entity = new Product
{
Name = product.Name, // Könnte null sein
Price = product.Price, // Könnte negativ sein
Category = product.Category
};
_context.Products.Add(entity);
await _context.SaveChangesAsync();
return Ok(entity);
}
// Verwundbar: Benutzerdefinierter Model-Binder umgeht Validierung
[HttpPut("{id}")]
public async Task<IActionResult> Update(int id, [ModelBinder(typeof(CustomBinder))] Product product)
{
// Benutzerdefinierter Binder löst möglicherweise keine Validierung aus
_context.Entry(product).State = EntityState.Modified;
await _context.SaveChangesAsync();
return NoContent();
}
}
// DTO ohne Validierung
public class ProductDto
{
public string Name { get; set; }
public decimal Price { get; set; }
public string Category { get; set; }
}
Lösung
// Behoben: ASP.NET MVC mit ordnungsgemäßer Modellvalidierung
public class UserController : Controller
{
// Behoben: Validiertes Modell verwenden
[HttpPost]
[ValidateAntiForgeryToken] // CSRF-Schutz
public ActionResult Register(RegisterViewModel model)
{
// Modellvalidierung prüfen
if (!ModelState.IsValid)
{
// View mit Validierungsfehlern zurückgeben
return View(model);
}
var user = new User
{
Username = model.Username,
Email = model.Email,
PasswordHash = HashPassword(model.Password)
};
db.Users.Add(user);
db.SaveChanges();
return RedirectToAction("Success");
}
// Behoben: ModelState-Validierung durchgesetzt
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult UpdateProfile(UserProfileViewModel model)
{
if (!ModelState.IsValid)
{
return View(model);
}
var profile = db.UserProfiles.Find(model.Id);
if (profile == null || profile.UserId != User.GetUserId())
{
return NotFound();
}
// Nur erlaubte Felder aktualisieren
profile.DisplayName = model.DisplayName;
profile.Bio = model.Bio;
db.SaveChanges();
return RedirectToAction("Profile");
}
// Behoben: [Bind] verwenden um Over-Posting zu verhindern
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult EditUser([Bind("Id,Username,Email")] UserEditViewModel model)
{
// Nur Id, Username, Email können gebunden werden - IsAdmin nicht
if (!ModelState.IsValid)
{
return View(model);
}
var user = db.Users.Find(model.Id);
if (user == null)
{
return NotFound();
}
// Nur bestimmte Felder aktualisieren
user.Username = model.Username;
user.Email = model.Email;
db.SaveChanges();
return RedirectToAction("Index");
}
}
// View Model mit Validierungsattributen
public class RegisterViewModel
{
[Required(ErrorMessage = "Benutzername ist erforderlich")]
[StringLength(50, MinimumLength = 3,
ErrorMessage = "Benutzername muss 3-50 Zeichen haben")]
[RegularExpression(@"^[a-zA-Z0-9_]+$",
ErrorMessage = "Benutzername kann nur Buchstaben, Zahlen und Unterstriche enthalten")]
public string Username { get; set; }
[Required(ErrorMessage = "E-Mail ist erforderlich")]
[EmailAddress(ErrorMessage = "Ungültiges E-Mail-Format")]
public string Email { get; set; }
[Required(ErrorMessage = "Passwort ist erforderlich")]
[StringLength(128, MinimumLength = 8,
ErrorMessage = "Passwort muss mindestens 8 Zeichen haben")]
[DataType(DataType.Password)]
public string Password { get; set; }
[DataType(DataType.Password)]
[Compare("Password", ErrorMessage = "Passwörter stimmen nicht überein")]
public string ConfirmPassword { get; set; }
}
// Edit View Model - keine sensiblen Felder exponiert
public class UserEditViewModel
{
public int Id { get; set; }
[Required]
[StringLength(50, MinimumLength = 3)]
public string Username { get; set; }
[Required]
[EmailAddress]
public string Email { get; set; }
// Hinweis: IsAdmin ist NICHT enthalten - kann nicht per Mass-Assignment gesetzt werden
}
// Behoben: ASP.NET Core API mit Validierung
[ApiController] // Aktiviert automatische Modellvalidierung
[Route("api/[controller]")]
public class ProductsController : ControllerBase
{
// Behoben: [ApiController] validiert ModelState automatisch
// Gibt 400 Bad Request zurück wenn Validierung fehlschlägt
[HttpPost]
public async Task<IActionResult> Create([FromBody] CreateProductDto product)
{
// Mit [ApiController] wird ModelState automatisch geprüft
// Ungültige Anfragen erreichen diesen Punkt nie
var entity = new Product
{
Name = product.Name,
Price = product.Price,
Category = product.Category
};
_context.Products.Add(entity);
await _context.SaveChangesAsync();
return CreatedAtAction(nameof(GetById), new { id = entity.Id }, entity);
}
// Behoben: Explizite Validierung für mehr Kontrolle
[HttpPut("{id}")]
public async Task<IActionResult> Update(int id, [FromBody] UpdateProductDto product)
{
// Zusätzliche benutzerdefinierte Validierung
if (id != product.Id)
{
return BadRequest("ID-Abweichung");
}
var entity = await _context.Products.FindAsync(id);
if (entity == null)
{
return NotFound();
}
// Nur erlaubte Felder aus DTO aktualisieren
entity.Name = product.Name;
entity.Price = product.Price;
entity.Category = product.Category;
// Hinweis: andere Felder wie CreatedBy, CreatedDate werden nicht aktualisiert
await _context.SaveChangesAsync();
return NoContent();
}
}
// DTO mit Validierungsattributen
public class CreateProductDto
{
[Required(ErrorMessage = "Name ist erforderlich")]
[StringLength(100, MinimumLength = 1,
ErrorMessage = "Name muss 1-100 Zeichen haben")]
public string Name { get; set; }
[Required]
[Range(0.01, 1000000, ErrorMessage = "Preis muss zwischen 0,01 und 1.000.000 liegen")]
public decimal Price { get; set; }
[Required]
[RegularExpression(@"^(Electronics|Clothing|Food|Other)$",
ErrorMessage = "Ungültige Kategorie")]
public string Category { get; set; }
}
// Benutzerdefiniertes Validierungsattribut für komplexe Regeln
public class ValidCategoryAttribute : ValidationAttribute
{
protected override ValidationResult IsValid(object value, ValidationContext context)
{
var category = value as string;
var dto = context.ObjectInstance as CreateProductDto;
// Beispiel: Lebensmittel müssen Preis unter 100 haben
if (category == "Food" && dto?.Price > 100)
{
return new ValidationResult("Lebensmittel dürfen nicht mehr als 100€ kosten");
}
return ValidationResult.Success;
}
}
CVE-Beispiele
Diese CWE stellt ein Konfigurations-/Implementierungsproblem dar, das eine Grundursache für viele ASP.NET-Schwachstellen ist. Mass-Assignment-Schwachstellen in ASP.NET-Anwendungen resultieren oft aus unsachgemäßer Modellvalidierung.
Verwandte CWEs
- CWE-1173: Unsachgemäße Verwendung eines Validierungs-Frameworks (übergeordnet)
- CWE-20: Unsachgemäße Eingabevalidierung (verwandt)
- CWE-915: Unsachgemäß kontrollierte Modifikation dynamisch bestimmter Objektattribute (Mass-Assignment)
Referenzen
- MITRE Corporation. "CWE-1174: ASP.NET Misconfiguration: Improper Model Validation." https://cwe.mitre.org/data/definitions/1174.html
- Microsoft Docs - Modellvalidierung in ASP.NET Core MVC
- OWASP - Mass Assignment