Mass Assignment on Azure
How Mass Assignment Manifests in Azure
Mass assignment vulnerabilities in Azure applications typically emerge through model binding and serialization mechanisms that automatically map HTTP request data to C# objects. In Azure Functions, ASP.NET Core Web APIs, and Azure App Service applications, developers often rely on framework features that bind request payloads directly to data models without explicit property whitelisting.
The most common pattern occurs when Azure developers use [FromBody] or [FromForm] attributes in controller actions. Consider this Azure Function that processes user profile updates:
[HttpTrigger(AuthorizationLevel.Anonymous, "put", Route = "users/{id}")]
public static async Task<User> UpdateUser(
[FromBody] UserUpdateModel model,
string id,
ILogger log,
ExecutionContext context)
{
var user = await _userRepository.GetById(id);
user.UpdateFromModel(model); // Mass assignment vulnerability
await _userRepository.Save(user);
return user;
}The vulnerability appears in the UpdateFromModel method, which might look like this:
public void UpdateFromModel(UserUpdateModel model)
{
// Dangerous: copies ALL properties without validation
FirstName = model.FirstName;
LastName = model.LastName;
Email = model.Email;
Role = model.Role; // Admin role can be escalated
IsActive = model.IsActive;
LastLogin = model.LastLogin; // Timestamp manipulation
}In Azure environments, this becomes particularly dangerous because many applications use Azure Active Directory for authentication and authorization. An attacker could exploit mass assignment to escalate privileges by setting the Role property to "Admin" or modify audit trails by manipulating LastLogin timestamps.
Azure Storage integration introduces another attack vector. When using Azure Table Storage or Cosmos DB with automatic serialization, developers might bind request data directly to entity models:
public class UserEntity : TableEntity
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string Role { get; set; }
public bool IsActive { get; set; }
public DateTime LastLogin { get; set; }
}
[HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = "users")]
public static async Task<IActionResult> CreateUser(
[FromBody] UserEntity user,
[Table("Users")] CloudTable table)
{
await table.ExecuteAsync(TableOperation.Insert(user));
return new OkObjectResult(user);
}Here, an attacker can submit any properties they want, including PartitionKey or RowKey, potentially overwriting existing records or accessing other users' data.
Azure Logic Apps and Azure Functions with Durable Entities present unique mass assignment risks. Durable Entities use stateful objects that persist across function invocations:
[FunctionName("UserEntity")]
public static Task<UserState> UserEntityOperation([
EntityTrigger] IDurableEntityContext context)
{
return context.DispatchAsync<UserState>();
}
[JsonObject(MemberSerialization.OptIn)]
public class UserState
{
[JsonProperty("firstName")]
public string FirstName { get; set; }
[JsonProperty("lastName")]
public string LastName { get; set; }
[JsonProperty("role")]
public string Role { get; set; }
}The DispatchAsync method automatically maps JSON properties to object properties, creating a mass assignment vulnerability where attackers can modify any exposed property without validation.
Azure-Specific Detection
Detecting mass assignment vulnerabilities in Azure applications requires both static code analysis and dynamic runtime testing. middleBrick's Azure-specific scanning identifies these issues through several techniques.
Static analysis in middleBrick examines C# source code for common mass assignment patterns in Azure Functions and Web APIs. The scanner looks for:
// Patterns that trigger alerts:
[FromBody] T model // Generic binding without validation
[FromForm] T model // Form data binding
context.DispatchAsync() // Durable Entities mass assignment
JsonSerializer.Deserialize() // Unvalidated deserialization
// Dangerous method patterns:
public void UpdateFromModel(T model) // No property filtering
public void ApplyChanges(T changes) // Blind property copying
Dynamic scanning tests Azure endpoints by submitting crafted payloads that attempt to modify sensitive properties. For a User model, middleBrick sends requests with additional fields like "Role=admin", "IsActive=false", or "LastLogin=2020-01-01" and observes whether the server accepts these modifications.
middleBrick's Azure-specific checks include:
| Check Type | Azure-Specific Pattern | Detection Method |
|---|---|---|
| Model Binding | [FromBody] User model | Payload injection testing |
| Durable Entities | context.DispatchAsync<User>() | State manipulation attempts |
| Table Storage | CloudTable.ExecuteAsync | PartitionKey/RowKey injection |
| Cosmos DB | Container.CreateItemAsync | Property escalation testing |
For Azure Logic Apps, middleBrick analyzes workflow definitions (JSON) to identify operations that bind external inputs to internal models without validation. The scanner flags patterns like:
// Logic App workflow analysis:
{
"type": "ApiConnection",
"inputs": {
"body": {
"@triggerBody()" // Direct binding without filtering
}
}
}middleBrick also tests Azure API Management APIs by examining backend configurations and policy definitions. It identifies cases where API Management policies automatically transform or bind request data to backend services without proper validation.
The scanner generates Azure-specific findings with severity levels based on the sensitivity of properties that can be manipulated. Modifying a "Role" property receives a higher severity than modifying a "DisplayName" property. Each finding includes the exact code location and recommended remediation steps.
Azure-Specific Remediation
Remediating mass assignment vulnerabilities in Azure applications requires a defense-in-depth approach using Azure's native security features and C# best practices. The most effective strategy combines explicit property whitelisting with Azure's built-in validation mechanisms.
For Azure Functions and Web APIs, implement explicit model binding with property filtering:
public class UserUpdateModel
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string Email { get; set; }
// Exclude sensitive properties from binding
[BindNever]
public string Role { get; set; }
[BindNever]
public bool IsActive { get; set; }
[BindNever]
public DateTime LastLogin { get; set; }
}
[HttpTrigger(AuthorizationLevel.Anonymous, "put", Route = "users/{id}")]
public static async Task<User> UpdateUser(
[FromBody] UserUpdateModel model,
string id,
ILogger log)
{
var user = await _userRepository.GetById(id);
// Explicit property assignment with validation
user.FirstName = SanitizeInput(model.FirstName);
user.LastName = SanitizeInput(model.LastName);
user.Email = ValidateEmail(model.Email);
// Sensitive properties remain unchanged
await _userRepository.Save(user);
return user;
}
private static string SanitizeInput(string input)
{
return WebUtility.HtmlEncode(input?.Trim());
}
private static string ValidateEmail(string email)
{
if (!Regex.IsMatch(email, @"^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$"))
throw new ArgumentException("Invalid email format");
return email;
}For Azure Durable Entities, implement explicit state management instead of relying on automatic dispatch:
[FunctionName("UserEntity")]
public static Task<UserState> UserEntityOperation([
EntityTrigger] IDurableEntityContext context)
{
switch (context.OperationName)
{
case "UpdateProfile":
var update = context.GetInput<UserUpdateModel>();
return UpdateProfile(context, update);
default:
throw new InvalidOperationException($"Unknown operation: {context.OperationName}");
}
}
private static Task<UserState> UpdateProfile(
IDurableEntityContext context,
UserUpdateModel update)
{
var state = context.GetState<UserState>();
// Explicit property assignment with validation
state.FirstName = SanitizeInput(update.FirstName);
state.LastName = SanitizeInput(update.LastName);
state.Email = ValidateEmail(update.Email);
context.SetState(state);
return Task.FromResult(state);
}For Azure Table Storage and Cosmos DB operations, use strongly-typed entities with explicit property mapping:
public class UserEntity : TableEntity
{
public UserEntity() { }
public UserEntity(string userId)
{
PartitionKey = "Users";
RowKey = userId;
}
public string FirstName { get; set; }
public string LastName { get; set; }
public string Email { get; set; }
// Sensitive properties not exposed in entity
[IgnoreProperty]
public string Role { get; set; }
[IgnoreProperty]
public bool IsActive { get; set; }
}
[HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = "users")]
public static async Task<IActionResult> CreateUser(
[FromBody] UserCreateRequest request,
[Table("Users")] CloudTable table)
{
var user = new UserEntity(request.UserId)
{
FirstName = SanitizeInput(request.FirstName),
LastName = SanitizeInput(request.LastName),
Email = ValidateEmail(request.Email)
};
await table.ExecuteAsync(TableOperation.Insert(user));
return new OkObjectResult(user);
}Azure API Management policies can add an additional layer of protection by validating and filtering request bodies before they reach backend services:
<policies>
<validate-content
contentType="application/json"
schema="@/schemes/user-update.json" />
<set-backend-service
base-url="https://your-backend.azurewebsites.net" />
</policies>The schema file (user-update.json) defines exactly which properties are allowed, preventing mass assignment at the API Management layer.
Related CWEs: propertyAuthorization
| CWE ID | Name | Severity |
|---|---|---|
| CWE-915 | Mass Assignment | HIGH |