Silent Risks in Default System Text JSON Serialization
The System.Text.Json library has become the default for most modern .NET applications. It’s efficient, lightweight, and deeply integrated into ASP.NET Core. But the same defaults that make it simple also introduce silent security risks. By default, System.Text.Json serialization automatically serializes all public properties, even those holding sensitive data like tokens or internal identifiers. Developers often rely on these defaults for speed, overlooking that they might expose confidential information in API responses (jsonserializer) .
⚠️Insecure example, for educational purposes only. Do not use in production.
public class UserProfile
{
public string UserName { get; set; }
public string Email { get; set; }
public string SessionToken { get; set; } // Sensitive field
}
var json = JsonSerializer.Serialize(userProfile);
Console.WriteLine(json);
// Output: {"UserName":"alex","Email":"alex@example.com","SessionToken":"eyJhbGciOi..."}
This jsonserializer behavior exposes SessionToken because all public properties are serialized automatically.
Secure version:
public class UserProfile
{
public string UserName { get; set; }
public string Email { get; set; }
[JsonIgnore] // Prevent serialization of sensitive fields
public string SessionToken { get; set; }
}
Educational note: Always use [JsonIgnore] for sensitive properties or design separate DTOs that only contain fields intended for serialization.
How JsonSerializer Expands Exposure Without Warning
The JsonSerializer in System.Text.Json doesn’t stop at the surface level. It recursively walks through nested objects, collections, and enum values, serializing everything it can access. Without explicit exclusion, this can easily lead to nested data leaks, especially when internal objects contain tokens, identifiers, or session keys.
⚠️Insecure example, for educational purposes only. Do not use in production.
public class AuthData
{
public string AccessToken { get; set; } // Sensitive
}
public class ApiResponse
{
public string Message { get; set; }
public AuthData AuthInfo { get; set; } // Nested object
}
var json = JsonSerializer.Serialize(new ApiResponse
{
Message = "OK",
AuthInfo = new AuthData { AccessToken = "abc123" }
});
Console.WriteLine(json);
// Output: {"Message":"OK","AuthInfo":{"AccessToken":"abc123"}}
The nested object makes system text json serialization include the sensitive AccessToken automatically.
Secure version:
public class AuthData
{
[JsonIgnore] // Prevent exposure of sensitive tokens
public string AccessToken { get; set; }
}
public class ApiResponse
{
public string Message { get; set; }
public AuthData AuthInfo { get; set; }
}
var options = new JsonSerializerOptions
{
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingDefault,
WriteIndented = false
};
var json = JsonSerializer.Serialize(apiResponse, options);
Educational note: Always define JsonSerializerOptions explicitly and review nested types for unintended data exposure.
Real-World Impact in APIs and Microservices
In real-world .NET applications, these serialization risks often go unnoticed until after deployment. The combination of automation, DTO reuse, and default jsonserializer behavior amplifies exposure. Common patterns where unsafe serialization leaks data:
- Reusing EF Core entities directly in API responses.
Logging serialized objects that contain authentication data. - Sharing models between internal and public services in CI/CD pipelines.
⚠️Insecure example, for educational purposes only. Do not use in production.
// Logs the full object, including sensitive fields
_logger.LogInformation("Response: {data}", JsonSerializer.Serialize(result));
If the result contains PasswordHash or SessionToken, these are now stored in CI/CD logs, violating least-privilege principles. Never expose real tokens, credentials, or internal URLs in pipelines.
Secure version:
var options = new JsonSerializerOptions
{
IgnoreReadOnlyProperties = true,
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull
};
// Sanitize before serialization
result.SessionToken = null;
_logger.LogInformation("Response: {data}", JsonSerializer.Serialize(result, options));
Educational note: Sanitize or nullify sensitive fields before logging, and always use safe JsonSerializerOptions defaults for audit output.
Safe Serialization Practices for Developers
Developers should treat system.text.json defaults as a convenience layer, not a security policy. Safe system text JSON serialization means taking control of what leaves your application.
Practical Defenses
- Define explicit contracts: Create dedicated DTOs for API responses, never serialize EF or domain entities directly.
- Configure global options: Use JsonSerializerOptions.DefaultIgnoreCondition and PropertyNamingPolicy.
- Apply [JsonIgnore] or converters: Hide or transform sensitive data.
- Validate outputs: Inspect actual serialized responses in tests.
- Automate checks: Include serialization validation in CI/CD.
Mini Preventive Checklist
- Review all classes using System.Text.Json serialization
- Mark secrets or credentials with [JsonIgnore]
- Define global safe defaults in Program.cs or Startup.cs
- Avoid direct serialization in logs
- Include serialization exposure tests in your CI/CD pipeline
Educational note: Safe serialization is about limiting exposure, not adding complexity. Make serialization explicit and testable.
Detecting Insecure Serialization with Automated Scanning – System.Text.Json
Automated scanning in CI/CD helps detect risky use of System.Text.Json and JsonSerializer. Static analysis tools can identify:
- Models missing [JsonIgnore].
- Sensitive fields exposed in DTOs.
- Unsafe global defaults (e.g., lack of ignore conditions).
Example Pipeline Check
# Never expose real tokens, credentials, or internal URLs in pipelines
- name: Run serialization security checks
run: dotnet xygeni validate --rules serialization --fail-on-risk
Educational note: Integrating static checks into your CI/CD ensures consistent control over system text JSON serialization across all builds.
How Xygeni Code Security Strengthens .NET Serialization Security
Xygeni Code Security provides deep detection for unsafe System.Text.Json defaults and misuse of JsonSerializer in NET.
It identifies patterns such as:
- Sensitive fields are serialized by default.
- Unsafe logging of JSON data.
- Missing [JsonIgnore] attributes.
- Misconfigured system text JSON serialization options.
By integrating Xygeni into pre-commit hooks, CI/CD pipelines, or PR gates, you can block vulnerable serialization patterns before they merge. It complements your existing DevSecOps process by turning serialization misconfigurations into enforceable security policies.
Educational note: Security tools like Xygeni enforce serialization hygiene automatically, freeing developers to focus on code, not audits.
Conclusion – About System.Text.Json
System.Text.Json makes serialization fast, but convenience often hides exposure. Default system.text.json serialization includes every public property, potentially leaking internal data, tokens, or identifiers.
Developers should:
- Audit their models.
- Use [JsonIgnore] and safe JsonSerializerOptions.
- Avoid serializing internal objects.
- Automate scanning using tools like Xygeni Code Security.
Default serialization isn’t just a developer shortcut; it’s a potential data leak. Make serialization explicit, verify outputs, and automate checks, because safe defaults aren’t always safe.