CA2330: Ensure that JsonSerializer has a secure configuration when deserializing
Property | Value |
---|---|
Rule ID | CA2330 |
Title | Ensure that JsonSerializer has a secure configuration when deserializing |
Category | Security |
Fix is breaking or non-breaking | Non-breaking |
Enabled by default in .NET 8 | No |
Cause
This rule fires when both of the following conditions might be true for a Newtonsoft.Json.JsonSerializer instance that's passed to a deserialization method or initialized as a field or property:
- The TypeNameHandling property is a value other than
None
. - The SerializationBinder property is null.
This rule is similar to CA2329, but in this case, analysis can't definitively determine if the serializer is configured insecurely.
By default, this rule analyzes the entire codebase, but this is configurable.
Rule description
Insecure deserializers are vulnerable when deserializing untrusted data. An attacker could modify the serialized data to include unexpected types to inject objects with malicious side effects. An attack against an insecure deserializer could, for example, execute commands on the underlying operating system, communicate over the network, or delete files.
This rule finds Newtonsoft.Json.JsonSerializer instances that might be configured to deserialize types specified from input, but may not be configured to restrict deserialized types with a Newtonsoft.Json.Serialization.ISerializationBinder. If you want to disallow deserialization of types specified from input completely, disable rules CA2327, CA2328, CA2329, and CA2330, and enable rule CA2326 instead.
How to fix violations
- Use TypeNameHandling's
None
value, if possible. - Make the serialized data tamper-proof. After serialization, cryptographically sign the serialized data. Before deserialization, validate the cryptographic signature. Protect the cryptographic key from being disclosed and design for key rotations.
- Restrict deserialized types. Implement a custom Newtonsoft.Json.Serialization.ISerializationBinder. Before deserializing with Json.NET, ensure your custom ISerializationBinder is specified in the Newtonsoft.Json.JsonSerializer.SerializationBinder property. In the overridden Newtonsoft.Json.Serialization.ISerializationBinder.BindToType method, if the type is unexpected, return
null
or throw an exception to stop deserialization.
When to suppress warnings
It's safe to suppress a warning from this rule if:
- You know the input is trusted. Consider that your application's trust boundary and data flows may change over time.
- You've taken one of the precautions in How to fix violations.
- You know that the Newtonsoft.Json.JsonSerializer.SerializationBinder property is always set when TypeNameHandling property is a value other than
None
.
Suppress a warning
If you just want to suppress a single violation, add preprocessor directives to your source file to disable and then re-enable the rule.
#pragma warning disable CA2330
// The code that's violating the rule is on this line.
#pragma warning restore CA2330
To disable the rule for a file, folder, or project, set its severity to none
in the configuration file.
[*.{cs,vb}]
dotnet_diagnostic.CA2330.severity = none
For more information, see How to suppress code analysis warnings.
Configure code to analyze
Use the following options to configure which parts of your codebase to run this rule on.
You can configure these options for just this rule, for all rules it applies to, or for all rules in this category (Security) that it applies to. For more information, see Code quality rule configuration options.
Exclude specific symbols
You can exclude specific symbols, such as types and methods, from analysis. For example, to specify that the rule should not run on any code within types named MyType
, add the following key-value pair to an .editorconfig file in your project:
dotnet_code_quality.CAXXXX.excluded_symbol_names = MyType
Allowed symbol name formats in the option value (separated by |
):
- Symbol name only (includes all symbols with the name, regardless of the containing type or namespace).
- Fully qualified names in the symbol's documentation ID format. Each symbol name requires a symbol-kind prefix, such as
M:
for methods,T:
for types, andN:
for namespaces. .ctor
for constructors and.cctor
for static constructors.
Examples:
Option Value | Summary |
---|---|
dotnet_code_quality.CAXXXX.excluded_symbol_names = MyType |
Matches all symbols named MyType . |
dotnet_code_quality.CAXXXX.excluded_symbol_names = MyType1|MyType2 |
Matches all symbols named either MyType1 or MyType2 . |
dotnet_code_quality.CAXXXX.excluded_symbol_names = M:NS.MyType.MyMethod(ParamType) |
Matches specific method MyMethod with the specified fully qualified signature. |
dotnet_code_quality.CAXXXX.excluded_symbol_names = M:NS1.MyType1.MyMethod1(ParamType)|M:NS2.MyType2.MyMethod2(ParamType) |
Matches specific methods MyMethod1 and MyMethod2 with the respective fully qualified signatures. |
Exclude specific types and their derived types
You can exclude specific types and their derived types from analysis. For example, to specify that the rule should not run on any methods within types named MyType
and their derived types, add the following key-value pair to an .editorconfig file in your project:
dotnet_code_quality.CAXXXX.excluded_type_names_with_derived_types = MyType
Allowed symbol name formats in the option value (separated by |
):
- Type name only (includes all types with the name, regardless of the containing type or namespace).
- Fully qualified names in the symbol's documentation ID format, with an optional
T:
prefix.
Examples:
Option Value | Summary |
---|---|
dotnet_code_quality.CAXXXX.excluded_type_names_with_derived_types = MyType |
Matches all types named MyType and all of their derived types. |
dotnet_code_quality.CAXXXX.excluded_type_names_with_derived_types = MyType1|MyType2 |
Matches all types named either MyType1 or MyType2 and all of their derived types. |
dotnet_code_quality.CAXXXX.excluded_type_names_with_derived_types = M:NS.MyType |
Matches specific type MyType with given fully qualified name and all of its derived types. |
dotnet_code_quality.CAXXXX.excluded_type_names_with_derived_types = M:NS1.MyType1|M:NS2.MyType2 |
Matches specific types MyType1 and MyType2 with the respective fully qualified names, and all of their derived types. |
Pseudo-code examples
Violation
using System;
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
public class BookRecordSerializationBinder : ISerializationBinder
{
// To maintain backwards compatibility with serialized data before using an ISerializationBinder.
private static readonly DefaultSerializationBinder Binder = new DefaultSerializationBinder();
public void BindToName(Type serializedType, out string assemblyName, out string typeName)
{
Binder.BindToName(serializedType, out assemblyName, out typeName);
}
public Type BindToType(string assemblyName, string typeName)
{
// If the type isn't expected, then stop deserialization.
if (typeName != "BookRecord" && typeName != "AisleLocation" && typeName != "WarehouseLocation")
{
return null;
}
return Binder.BindToType(assemblyName, typeName);
}
}
public class BookRecord
{
public string Title { get; set; }
public object Location { get; set; }
}
public abstract class Location
{
public string StoreId { get; set; }
}
public class AisleLocation : Location
{
public char Aisle { get; set; }
public byte Shelf { get; set; }
}
public class WarehouseLocation : Location
{
public string Bay { get; set; }
public byte Shelf { get; set; }
}
public static class Binders
{
public static ISerializationBinder BookRecord = new BookRecordSerializationBinder();
}
public class ExampleClass
{
public BookRecord DeserializeBookRecord(JsonReader reader)
{
JsonSerializer jsonSerializer = new JsonSerializer();
jsonSerializer.TypeNameHandling = TypeNameHandling.Auto;
jsonSerializer.SerializationBinder = Binders.BookRecord;
return jsonSerializer.Deserialize<BookRecord>(reader); // CA2330 -- SerializationBinder might be null
}
}
Imports System
Imports Newtonsoft.Json
Imports Newtonsoft.Json.Serialization
Public Class BookRecordSerializationBinder
Implements ISerializationBinder
' To maintain backwards compatibility with serialized data before using an ISerializationBinder.
Private Shared ReadOnly Property Binder As New DefaultSerializationBinder()
Public Sub BindToName(serializedType As Type, ByRef assemblyName As String, ByRef typeName As String) Implements ISerializationBinder.BindToName
Binder.BindToName(serializedType, assemblyName, typeName)
End Sub
Public Function BindToType(assemblyName As String, typeName As String) As Type Implements ISerializationBinder.BindToType
' If the type isn't expected, then stop deserialization.
If typeName <> "BookRecord" AndAlso typeName <> "AisleLocation" AndAlso typeName <> "WarehouseLocation" Then
Return Nothing
End If
Return Binder.BindToType(assemblyName, typeName)
End Function
End Class
Public Class BookRecord
Public Property Title As String
Public Property Location As Location
End Class
Public MustInherit Class Location
Public Property StoreId As String
End Class
Public Class AisleLocation
Inherits Location
Public Property Aisle As Char
Public Property Shelf As Byte
End Class
Public Class WarehouseLocation
Inherits Location
Public Property Bay As String
Public Property Shelf As Byte
End Class
Public Class Binders
Public Shared Property BookRecord As ISerializationBinder = New BookRecordSerializationBinder()
End Class
Public Class ExampleClass
Public Function DeserializeBookRecord(reader As JsonReader) As BookRecord
Dim jsonSerializer As JsonSerializer = New JsonSerializer()
jsonSerializer.TypeNameHandling = TypeNameHandling.Auto
jsonSerializer.SerializationBinder = Binders.BookRecord
Return jsonSerializer.Deserialize(Of BookRecord)(reader) ' CA2330 -- SerializationBinder might be null
End Function
End Class
Solution
using System;
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
public class BookRecordSerializationBinder : ISerializationBinder
{
// To maintain backwards compatibility with serialized data before using an ISerializationBinder.
private static readonly DefaultSerializationBinder Binder = new DefaultSerializationBinder();
public void BindToName(Type serializedType, out string assemblyName, out string typeName)
{
Binder.BindToName(serializedType, out assemblyName, out typeName);
}
public Type BindToType(string assemblyName, string typeName)
{
// If the type isn't expected, then stop deserialization.
if (typeName != "BookRecord" && typeName != "AisleLocation" && typeName != "WarehouseLocation")
{
return null;
}
return Binder.BindToType(assemblyName, typeName);
}
}
public class BookRecord
{
public string Title { get; set; }
public object Location { get; set; }
}
public abstract class Location
{
public string StoreId { get; set; }
}
public class AisleLocation : Location
{
public char Aisle { get; set; }
public byte Shelf { get; set; }
}
public class WarehouseLocation : Location
{
public string Bay { get; set; }
public byte Shelf { get; set; }
}
public static class Binders
{
public static ISerializationBinder BookRecord = new BookRecordSerializationBinder();
}
public class ExampleClass
{
public BookRecord DeserializeBookRecord(JsonReader reader)
{
JsonSerializer jsonSerializer = new JsonSerializer();
jsonSerializer.TypeNameHandling = TypeNameHandling.Auto;
// Ensure that SerializationBinder is assigned non-null before deserializing
jsonSerializer.SerializationBinder = Binders.BookRecord ?? throw new Exception("Expected non-null");
return jsonSerializer.Deserialize<BookRecord>(reader);
}
}
Imports System
Imports Newtonsoft.Json
Imports Newtonsoft.Json.Serialization
Public Class BookRecordSerializationBinder
Implements ISerializationBinder
' To maintain backwards compatibility with serialized data before using an ISerializationBinder.
Private Shared ReadOnly Property Binder As New DefaultSerializationBinder()
Public Sub BindToName(serializedType As Type, ByRef assemblyName As String, ByRef typeName As String) Implements ISerializationBinder.BindToName
Binder.BindToName(serializedType, assemblyName, typeName)
End Sub
Public Function BindToType(assemblyName As String, typeName As String) As Type Implements ISerializationBinder.BindToType
' If the type isn't expected, then stop deserialization.
If typeName <> "BookRecord" AndAlso typeName <> "AisleLocation" AndAlso typeName <> "WarehouseLocation" Then
Return Nothing
End If
Return Binder.BindToType(assemblyName, typeName)
End Function
End Class
Public Class BookRecord
Public Property Title As String
Public Property Location As Location
End Class
Public MustInherit Class Location
Public Property StoreId As String
End Class
Public Class AisleLocation
Inherits Location
Public Property Aisle As Char
Public Property Shelf As Byte
End Class
Public Class WarehouseLocation
Inherits Location
Public Property Bay As String
Public Property Shelf As Byte
End Class
Public Class Binders
Public Shared Property BookRecord As ISerializationBinder = New BookRecordSerializationBinder()
End Class
Public Class ExampleClass
Public Function DeserializeBookRecord(reader As JsonReader) As BookRecord
Dim jsonSerializer As JsonSerializer = New JsonSerializer()
jsonSerializer.TypeNameHandling = TypeNameHandling.Auto
' Ensure SerializationBinder is non-null before deserializing
jsonSerializer.SerializationBinder = If(Binders.BookRecord, New Exception("Expected non-null"))
Return jsonSerializer.Deserialize(Of BookRecord)(reader)
End Function
End Class
Related rules
CA2326: Do not use TypeNameHandling values other than None
CA2327: Do not use insecure JsonSerializerSettings
CA2328: Ensure that JsonSerializerSettings are secure
CA2329: Do not deserialize with JsonSerializer using an insecure configuration