Over-binding occurs when model binding assigns client-supplied request values to properties the application did not intend the client to control. An attacker can submit fields omitted from the form, potentially changing sensitive data or causing persistence errors. section 19.5.2 section 28.4.3
Prevent over-binding by controlling which request values reach application objects:
- Use a dedicated binding target containing only accepted input properties, then map it explicitly to the domain model. section 19.5.2
- Apply the Bind attribute to allowlist properties for a specific action, excluding every other submitted value. section 28.4.3
- Apply BindNever to a model property when the same exclusion should apply across binding scenarios, including Razor Pages. section 28.4.3
Protecting product creation
A POST request may include ProductId=100 even though the database assigns identity keys. Binding directly to Product can pass that value to Entity Framework Core and trigger an identity-column error. Binding to ProductBindingTarget instead accepts only Name, Price, CategoryId, and SupplierId, then ToProduct creates the persisted Product. section 19.5.2
Listing 19.16 The ProductBindingTarget.cs file in the WebApp/Models folder
namespace WebApp.Models {
public class ProductBindingTarget {
public required string Name { get; set; }
public decimal Price { get; set; }
public long CategoryId { get; set; }
public long SupplierId { get; set; }
public Product ToProduct() => new Product() {
Name = this.Name, Price = this.Price,
CategoryId = this.CategoryId, SupplierId = this.SupplierId
};
}
}
Inspect the dedicated ProductBindingTarget class and its explicit ToProduct mapping. section 19.5.2
Listing 28.21 Decorating a property in the Product.cs file in the Models folder
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Text.Json.Serialization;
using Microsoft.AspNetCore.Mvc.ModelBinding;
namespace WebApp.Models {
public class Product {
public long ProductId { get; set; }
public required string Name { get; set; }
[Column(TypeName = "decimal(8, 2)")]
[BindNever]
public decimal Price { get; set; }
public long CategoryId { get; set; }
public Category? Category { get; set; }
public long SupplierId { get; set; }
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public Supplier? Supplier { get; set; }
}
}
Inspect a Product model using BindNever to exclude Price from model binding. section 28.4.3
