Deserializzazione JSON sicura in .NET 10: guida completa a JsonSerializerOptions.Strict
#tech
spcnet.it/deserializzazione-js…
@informatica
Deserializzazione JSON sicura in .NET 10: guida completa a JsonSerializerOptions.Strict
Considera questo payload JSON in arrivo alla tua API:{"Amount": 100, "Amount": -999}
Due proprietà con lo stesso nome. La sezione 4 di RFC 8259 dice che i nomi degli oggetti dovrebbero essere univoci, ma non lo impone. System.Text.Json, di default, adotta l’approccio permissivo: vince l’ultima scrittura, nessun avviso, nessun errore. Il valore dell’attaccante passa silenziosamente.Questo non è solo un problema di proprietà duplicate. La deserializzazione di default ignora anche campi extra che un attaccante potrebbe iniettare, lascia scivolare i valori null nelle proprietà non-nullable e salta dati richiesti mancanti. Ogni di queste “comodità” è una potenziale vulnerabilità al confine della tua API.
JsonSerializerOptions.Strict: cinque protezioni in un solo preset
.NET 10 introduceJsonSerializerOptions.Strict, un nuovo preset di sola lettura che si affianca aDefaulteWeb. MentreDefaultdà priorità alla retrocompatibilità eWebottimizza per le API HTTP tipiche,Strictsegue le best practice di sicurezza attivando cinque impostazioni protettive simultaneamente.var strict = JsonSerializerOptions.Strict; // AllowDuplicateProperties: False // UnmappedMemberHandling: Disallow // PropertyNameCaseInsensitive: False // RespectNullableAnnotations: True // RespectRequiredConstructorParameters: TrueConfronto tra i tre preset
Impostazione Default Web Strict AllowDuplicateProperties true true false UnmappedMemberHandling Skip Skip Disallow PropertyNameCaseInsensitive false true false RespectNullableAnnotations false false true RespectRequiredConstructorParameters false false true I dati serializzati con
Defaultpossono essere deserializzati conStrict. La compatibilità va in una sola direzione:Strictè più severo su ciò che accetta, non su ciò che produce.1. Proprietà duplicate vietate
I protocolli che stratificano il parsing JSON (OAuth 2.0, OpenID Connect, firme webhook) possono essere sfruttati se parser diversi gestiscono input duplicati in modo diverso. ConStrict, ogni tentativo di deserializzare JSON con proprietà duplicate genera immediatamente unaJsonException:string duplicateJson = @'{"Amount": 100, "Amount": -999}'; try { JsonSerializer.Deserialize<Payment>(duplicateJson, JsonSerializerOptions.Strict); } catch (JsonException ex) { // JsonException: Duplicate property 'Amount' encountered during deserialization Console.WriteLine(ex.Message); } public record Payment(int Amount);
Questa protezione si estende oltre i POCO (plain-old C# objects): funziona anche conJsonDocument,JsonNodeeDictionary<string, T>.2. Rifiuto dei membri non mappati
La deserializzazione di default scarta silenziosamente le proprietà JSON che non corrispondono al tuo tipo .NET. È comodo durante lo sviluppo, ma è pericoloso a un confine di fiducia perché non sai cosa sta inviando il client.string extraFieldJson = @'{"Name": "Alice", "Role": "user", "IsRoot": true}'; // Default: ignora silenziosamente "IsRoot" var user = JsonSerializer.Deserialize<User>(extraFieldJson); // Name=Alice, Role=user - "IsRoot" scompare senza tracce // Strict: rifiuta la proprieta' non mappata JsonSerializer.Deserialize<User>(extraFieldJson, JsonSerializerOptions.Strict); // throws: The JSON property 'IsRoot' could not be mapped to any .NET member public record User(string Name, string Role);3. Corrispondenza case-sensitive dei nomi di proprietà
In modalitàStrict, la case sensitivity diventa un contratto preciso: i nomi delle proprietà JSON devono corrispondere esattamente ai nomi delle proprietà C#. Se i tuoi client inviano camelCase ma i tuoi tipi usano PascalCase, aggiungi[JsonPropertyName("nomeCamelCase")]per rendere il contratto esplicito nella definizione del tipo.4. Enforcement delle annotazioni nullable
I nullable reference types di C# aiutano a intercettare i problemi di null a compile time, ma System.Text.Json li ignora di default durante la deserializzazione. ConStrict, se hai dichiaratostring Name(nonstring? Name), il serializzatore rifiuterà qualsiasi JSON connullper quella proprietà:string nullNameJson = @'{"Name": null, "Email": "alice@example.com"}'; // Default: null va nella stringa non-nullable senza errori var contact = JsonSerializer.Deserialize<Contact>(nullNameJson); // contact.Name == null (silenzioso!) // Strict: genera eccezione JsonSerializer.Deserialize<Contact>(nullNameJson, JsonSerializerOptions.Strict); // throws: The constructor parameter 'Name' doesn't allow null values public record Contact(string Name, string Email);5. Parametri obbligatori del costruttore
I record type e le classi con costruttori parametrizzati possono avere parametri obbligatori silenziosamente riempiti con valori di default quando il JSON manca dei dati.Strictlo impedisce:string missingParamJson = @'{"FirstName": "Alice"}'; // Default: LastName mancante diventa silenziosamente null var person = JsonSerializer.Deserialize<Person>(missingParamJson); // person.LastName == null // Strict: richiede tutti i parametri JsonSerializer.Deserialize<Person>(missingParamJson, JsonSerializerOptions.Strict); // throws: JSON deserialization was missing required properties: 'LastName' public record Person(string FirstName, string LastName);Integrazione in ASP.NET Core Minimal APIs
Nei demo sopra usiamoJsonSerializerdirettamente. In un’applicazione web, configuri le opzioni JSON una volta e ogni endpoint le eredita. Nota:JsonSerializerOptions.Strictè un singleton frozen, quindi non puoi passarlo direttamente aConfigureHttpJsonOptionsche richiede un’istanza mutabile. Imposta le singole proprietà:builder.Services.ConfigureHttpJsonOptions(options => { options.SerializerOptions.AllowDuplicateProperties = false; options.SerializerOptions.UnmappedMemberHandling = System.Text.Json.Serialization.JsonUnmappedMemberHandling.Disallow; options.SerializerOptions.PropertyNameCaseInsensitive = false; options.SerializerOptions.RespectNullableAnnotations = true; options.SerializerOptions.RespectRequiredConstructorParameters = true; }); app.MapPost("/payments", (Payment payment) => { // Se il body ha proprieta' duplicate, campi non mappati o dati mancanti, // il framework risponde con 400 Bad Request prima che questo codice venga eseguito. return Results.Ok(payment); });
Il framework intercettaJsonExceptiondurante il model binding e restituisce un 400 Bad Request con problem details. Il tuo endpoint vede solo oggetti validi e completamente inizializzati.Configurazione per-endpoint
Se hai bisogno di validazione strict su alcuni endpoint ma parsing più flessibile su altri, puoi deserializzare manualmente dal body della richiesta con le opzioni desiderate:app.MapPost("/api/strict", async (HttpContext context) => { var payment = await context.Request.ReadFromJsonAsync<Payment>( JsonSerializerOptions.Strict); return Results.Ok(payment); });Supporto per i Source Generator
Per scenari AOT o per i benefici prestazionali dei source generator, configura manualmente le impostazioni equivalenti suJsonSourceGenerationOptionsAttribute. Non esiste una scorciatoiaStrictper l’attributo: ogni proprietà va impostata individualmente.[JsonSourceGenerationOptions( AllowDuplicateProperties = false, UnmappedMemberHandling = JsonUnmappedMemberHandling.Disallow, PropertyNameCaseInsensitive = false, RespectNullableAnnotations = true, RespectRequiredConstructorParameters = true )] [JsonSerializable(typeof(Payment))] internal partial class StrictJsonContext : JsonSerializerContext;
Il codice generato include tutta la logica di validazione a compile time, senza overhead di reflection.Quando usare Strict (e quando no)
Usalo ai confini di fiducia: endpoint token, ricevitori di webhook, controller API che accettano JSON da client non controllati completamente. Il costo è unaJsonExceptionquando i payload non corrispondono al contratto. Questo è esattamente lo scopo.Evitalo per l’ingestione flessibile: se consumi JSON da API di terze parti con schemi inconsistenti, la modalità strict rifiuterà payload che potresti voler gestire con più grazia. In questi casi usa
DefaultoWebe valida dopo la deserializzazione.Migra in modo incrementale: non è necessario passare tutto a
Strictsubito. Inizia dagli endpoint ad alto rischio, intercettaJsonException, registra i problemi, correggi i client che inviano payload non conformi, poi espandi.Sappi i limiti:
Strictvalida le violazioni del contratto strutturale ma non protegge da JSON profondamente annidato (usaMaxDepth), payload eccessivi (imposta limiti HTTP) o type confusion polimorfico. È un layer di difesa, non l’unico.Conclusione
Ogni endpoint API che accetta JSON è un confine di fiducia. La deserializzazione permissiva rende quel confine poroso.JsonSerializerOptions.Strictnon aggiunge nuova logica: attiva protezioni già presenti inSystem.Text.Jsonma disattivate di default per retrocompatibilità. Una riga di configurazione le attiva tutte.Questo è particolarmente rilevante ai confini di protocollo come OAuth 2.0 e OpenID Connect, dove una proprietà duplicata o un campo inatteso non è solo un bug — è un potenziale vettore di exploit.
Fonte: Harden Your .NET JSON Deserialization with System.Text.Json and JsonSerializerOptions.Strict — Khalid Abuhakmeh, Duende Software (30 aprile 2026)
Harden Your .NET JSON Deserialization with System.Text.Json and JsonSerializerOptions.Strict
Learn how .NET 10's new JsonSerializerOptions.Strict preset activates five security protections in System.Text.Json to close gaps at your API trust boundaries.Khalid Abuhakmeh (Duende Software)
reshared this
The Pirate Post, The Privacy Post, Cybersecurity & cyberwarfare, Poliversity - Università ricerca e giornalismo, macfranc, Franc Mac, Poliverso & Poliversity, informapirata ⁂ e Informa Pirata reshared this.
Robertof
in reply to macfranc • • •Provare per credere: ponete alla Ai la domanda se trump e netanyau sono criminali in base alle aioni che stanno eseguendo.