Prototype Pollution in Aspnet with Mutual Tls
Prototype Pollution in Aspnet with Mutual Tls — how this specific combination creates or exposes the vulnerability
Prototype pollution in ASP.NET applications occurs when an attacker can modify the prototype of shared objects, typically through insecure deserialization or manipulation of JSON payloads that the server processes. When mutual TLS (mTLS) is used, the assumption is that communication is strongly authenticated because both client and server present certificates. However, mTLS does not inherently prevent prototype pollution; it only authenticates the endpoints. If an authenticated client sends a malicious payload—such as JSON containing __proto__, constructor, or prototype properties—the server-side model binders in ASP.NET can still be tricked into modifying object prototypes before the application logic executes.
In an mTLS scenario, the server validates the client certificate during the TLS handshake and may associate the certificate with a user or role. The application might then deserialize incoming JSON directly, for example using System.Text.Json or Newtonsoft.Json, without sufficient validation. Attackers can craft requests that exploit known gadget chains in the chosen serializer to pollute prototypes. Common patterns include using nested objects with __proto__ or setting properties on Object.prototype via special keys. Because mTLS ensures the request comes from a trusted client, developers may mistakenly believe the input is safe, leading to missing input validation on the deserialized data. This combination—strong transport security but weak runtime validation—means prototype pollution findings in mTLS-protected endpoints often indicate the application incorrectly trusts authenticated client data.
ASP.NET model binding merges query strings, route data, and JSON bodies, which can amplify the risk. For instance, with System.Text.Json, options like PropertyNamingPolicy and custom converters may inadvertently allow prototype-like keys to map to objects. If the application later shares these objects across requests or uses them as templates, modified prototypes can affect behavior, potentially leading to insecure property access or logic bypass. The 12 security checks in middleBrick run in parallel and include Input Validation and BOLA/IDOR, which can detect cases where authenticated mTLS sessions still accept malformed or malicious object graphs that lead to prototype pollution paths.
Real-world examples involve gadgets in JSON payloads that leverage TypeConverter or custom binders. For instance, an attacker might send { "__proto__": { "isAdmin": true } } and, depending on model binder configuration, cause the prototype chain to be altered for subsequent instances. middleByte’s LLM/AI Security checks do not apply here, but the scanner’s Input Validation and Property Authorization checks highlight whether such inputs are sanitized or constrained before deserialization.
Because mTLS prevents unauthenticated access, prototype pollution might be limited to authenticated clients, but the impact can still be severe if the polluted prototype affects authorization checks or data handling. middleBrick’s Authentication and BOLA/IDOR checks help identify whether authenticated sessions inadvertently expose dangerous object construction paths. Remediation focuses on strict input validation and avoiding direct use of user-controlled keys as object property names during deserialization.
Mutual Tls-Specific Remediation in Aspnet — concrete code fixes
To secure ASP.NET applications with mutual TLS while preventing prototype pollution, apply strict input validation and avoid unsafe deserialization patterns. Below are concrete code examples using mTLS in ASP.NET Core and hardened deserialization techniques.
Mutual TLS setup in ASP.NET Core
Configure Kestrel to require client certificates and map them to user identity:
// Program.cs (ASP.NET Core 6+)
var builder = WebApplication.CreateBuilder(args);
// Enforce mTLS by requiring client certificates
builder.WebHost.ConfigureKestrel(serverOptions =>
{
serverOptions.ListenAnyIP(5001, listenOptions =>
{
listenOptions.UseHttps(httpsOptions =>
{
httpsOptions.ServerCertificate = LoadServerCertificate();
httpsOptions.ClientCertificateMode = ClientCertificateMode.RequireCertificate;
// Optionally validate client certificate thumbprint or chain
httpsOptions.ClientCertificateValidation = (cert, chain, errors) =>
{
if (cert is null) return false;
// Example: enforce specific thumbprint or policy
return errors == System.Security.Authentication.SslPolicyErrors.None;
};
});
});
});
// Add authentication based on client certificate
builder.Services.AddAuthentication(CertificateAuthenticationDefaults.AuthenticationScheme)
.AddCertificate(options =>
{
options.AllowedCertificateTypes = CertificateTypes.All;
options.RevocationMode = System.Security.Cryptography.X509Certificates.X509RevocationMode.NoCheck;
options.Events = new CertificateAuthenticationEvents
{
OnCertificateValidated = context =>
{
var cert = context.ClientCertificate;
// Map certificate fields to claims if needed
var claims = new List<Claim>
{
new Claim(ClaimTypes.NameIdentifier, cert.SubjectName.Name),
new Claim("Thumbprint", cert.Thumbprint)
};
context.Principal = new ClaimsPrincipal(new ClaimsIdentity(claims, context.Scheme.Name));
return Task.CompletedTask;
}
};
});
builder.Services.AddAuthorization();
var app = builder.Build();
app.UseAuthentication();
app.UseAuthorization();
app.MapControllers();
app.Run();
Hardened deserialization to prevent prototype pollution
Use custom JSON options that disallow dangerous property mappings. With System.Text.Json:
// In a controller or service
var options = new JsonSerializerOptions
{
PropertyNameCaseInsensitive = true,
// Prevent mapping of __proto__, constructor, and other special keys
PropertyNamingPolicy = JsonNamingPolicy.CamelCase
};
// Use a custom converter or pre-processing to reject suspicious keys
public class SafeObjectInputFilter : IJsonInputFilter
{
public bool TryRead(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options, out object? value)
{
// Example: for dictionaries, block keys that look like prototype pollution attempts
if (reader.TokenType == JsonTokenType.StartObject)
{
using var doc = JsonDocument.ParseValue(ref reader);
var root = doc.RootElement;
foreach (var prop in root.EnumerateObject())
{
if (prop.NameEquals("__proto__") || prop.NameEquals("constructor") || prop.NameEquals("prototype"))
{
value = null;
return false; // reject
}
}
}
// Fallback to default deserialization for allowed structures
value = JsonSerializer.Deserialize(ref reader, typeToConvert, options);
return true;
}
}
// Apply the filter in minimal APIs or controllers
app.MapPost("/secure-endpoint", (JsonDocument input) =>
{
// Process validated data; avoid using input as prototype source
return Results.Ok(new { received = input.RootElement.GetProperty("data").ToString() });
}).AddJsonOptions(opts => opts.JsonSerializerOptions.CustomConverters.Add(new SafeObjectInputFilter()));
For Newtonsoft.Json, configure ReferenceHandler and settings to avoid prototype pollution:
// Using Newtonsoft.Json in a controller or service
var settings = new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.None, // avoid type injection
MetadataPropertyHandling = MetadataPropertyHandling.Ignore,
ObjectCreationHandling = ObjectCreationHandling.Replace,
// Block resolution of special members
ConstructorHandling = ConstructorHandling.Default,
// Ensure no prototype pollution via property names
ContractResolver = new DefaultContractResolver
{
NamingStrategy = new CamelCaseNamingStrategy()
}
};
var safeData = JsonConvert.DeserializeObject<MyModel>(jsonString, settings);
Combine these practices with the principle that mTLS secures the channel but does not sanitize input. Validate and restrict all incoming JSON keys, and avoid merging untrusted keys directly into objects used across requests. middleBrick’s scans can highlight missing validation in mTLS-protected endpoints, focusing on Input Validation and Property Authorization checks.