ASP.NET Core  

Protecting Against JSON Injection and Malformed Payloads in ASP.NET Core

What is JSON injection?

  • JSON injection occurs when an attacker manipulates the structure of a JSON payload to insert unexpected keys, values, or scripts.

  • Attackers exploit

    • Extra fields (e.g., isAdmin: true ).

    • Script injection inside JSON strings (leading to XSS if improperly handled).

    • Malformed JSON to crash parsers or bypass validations.

Example Attack Payload

  
    {
  "username": "attacker",
  "email": "[email protected]",
  "isAdmin": true,        // Sensitive field injection
  "__proto__": { "polluted": "yes" } // Prototype pollution attack
}
  

If your API model-binds this blindly into your entity, attackers can escalate privileges or poison objects.

Risks of Malformed JSON

  • Denial of Service (DoS): Sending enormous JSON payloads.

  • Deserialization flaws: Exploiting weak parsers (e.g., type confusion).

  • Validation bypass: Sending unexpected JSON structures.

How to Protect in ASP.NET Core?

1. Use DTOs Instead of Entities

Never accept raw JSON into entity models. Define Data Transfer Objects (DTOs) with strict fields.

  
    public class RegisterDto
{
    [Required, StringLength(50)]
    public string Username { get; set; }

    [Required, EmailAddress]
    public string Email { get; set; }

    [Required, StringLength(100)]
    public string Password { get; set; }
}
  

Controller

  
    [HttpPost("register")]
public IActionResult Register([FromBody] RegisterDto dto)
{
    if (!ModelState.IsValid)
        return BadRequest(ModelState);

    // Safe mapping
    var user = new User
    {
        Username = dto.Username,
        Email = dto.Email,
        IsAdmin = false // Explicitly controlled
    };

    return Ok(user);
}
  

Prevents attackers from injecting extra JSON properties, such as isAdmin.

2. Reject Unknown Properties

By default, System. Text.Json ignores unknown fields. Force it to fail on unknown JSON properties.

  
    builder.Services.AddControllers()
    .AddJsonOptions(options =>
    {
        options.JsonSerializerOptions.ReadCommentHandling = JsonCommentHandling.Disallow;
        options.JsonSerializerOptions.AllowTrailingCommas = false;
        options.JsonSerializerOptions.UnknownTypeHandling = System.Text.Json.Serialization.JsonUnknownTypeHandling.Fail; 
        options.JsonSerializerOptions.PropertyNameCaseInsensitive = false;
    });
  

Or, in Newtonsoft.Json.

  
    builder.Services.AddControllers()
    .AddNewtonsoftJson(options =>
    {
        options.SerializerSettings.MissingMemberHandling = Newtonsoft.Json.MissingMemberHandling.Error;
    });
  

Any unexpected JSON field will cause a 400 Bad Request.

3. Input Validation

Use Data Annotations or FluentValidation to validate JSON payloads.

  
    if (!ModelState.IsValid)
    return BadRequest(ModelState);
  

Ensures malformed JSON (wrong type, missing field) gets rejected.

4. Limit Request Size (Prevent DoS)

Attackers may send gigantic JSON payloads to crash your server. Set max request body size.

  
    app.Use(async (context, next) =>
{
    context.Request.Headers.ContentLength = Math.Min(context.Request.ContentLength ?? 0, 10_000); // 10 KB
    await next();
});
  

Or in Kestrel

  
    builder.WebHost.ConfigureKestrel(options =>
{
    options.Limits.MaxRequestBodySize = 10 * 1024; // 10 KB
});
  

Protects against payload flooding.

5. Sanitize JSON String Fields

Attackers may inject <script> or SQL-like payloads inside JSON strings. Use HtmlSanitizer or encoding before saving.

  
    var sanitizer = new HtmlSanitizer();
dto.Username = sanitizer.Sanitize(dto.Username);
  

Prevents XSS when storing/displaying JSON fields.

6. Strong Content-Type Validation

Reject requests that are not application/json.

  
    if (!context.Request.ContentType?.Contains("application/json") ?? true)
{
    context.Response.StatusCode = StatusCodes.Status415UnsupportedMediaType;
    return;
}
  

Stops attackers from sneaking payloads with wrong MIME types.

7. Use JsonDocument for Strict Parsing

If you don’t trust the payload at all, parse manually.

  
    using var doc = JsonDocument.Parse(jsonString, new JsonDocumentOptions
{
    AllowTrailingCommas = false,
    CommentHandling = JsonCommentHandling.Disallow
});

if (!doc.RootElement.TryGetProperty("username", out var username))
    return BadRequest("Missing username");
  

Complete control over parsing logic.

8. Audit & Logging

Log rejected payloads with correlation IDs.

  
    _logger.LogWarning("Invalid JSON payload received: {Payload}", jsonString);
  

Helps detect attack attempts.

Best Practices Checklist

  • Use DTOs (never bind entities).

  • Fail on unknown JSON properties.

  • Validate input with Data Annotations/FluentValidation.

  • Set max request body size to prevent DoS.

  • Sanitize string fields for XSS safety.

  • Enforce application/json Content-Type.

  • Parse manually ( JsonDocument ) for untrusted payloads.

  • Log and monitor invalid payload attempts.

Conclusion

ASP.NET Core provides strong defenses, but developers must explicitly enable strict JSON parsing and validation.

By using DTOs, rejecting unknown fields, sanitizing input, and setting request limits, you protect your API from.

  • βœ” JSON Injection

  • βœ” Malformed Payload DoS

  • βœ” Prototype Pollution

  • βœ” XSS inside JSON