Partilhar via


CA2312: Certifique-se de que o NetDataContractSerializer.Binder esteja definido antes de desserializar.

Propriedade valor
ID da regra CA2312
Título Verifique se NetDataContractSerializer.Binder está definido antes de desserializar
Categoria Segurança
A correção causa interrupção ou não Ininterrupto
Habilitado por padrão no .NET 10 Não
Línguas aplicáveis C# e Visual Basic

Motivo

Um método de System.Runtime.Serialization.NetDataContractSerializer desserialização foi chamado ou referenciado, e a propriedade Binder pode ser nula.

Esta regra é semelhante à CA2311, mas a análise não pode determinar se a Binder é definitivamente nula.

Por padrão, essa regra analisa toda a base de código, mas isso é configurável.

Aviso

Restringir tipos com um SerializationBinder não pode impedir todos os ataques. Para obter mais informações, consulte o guia de segurança BinaryFormatter.

Descrição da regra

Desserializadores inseguros são vulneráveis ao desserializar dados não confiáveis. Um invasor pode modificar os dados serializados para incluir tipos inesperados para injetar objetos com efeitos colaterais mal-intencionados. Um ataque contra um desserializador inseguro pode, por exemplo, executar comandos no sistema operacional subjacente, se comunicar pela rede ou excluir arquivos.

Esta regra localiza chamadas de método ou referências de desserialização quando o Binder pode ser nulo. Se pretender não permitir qualquer desserialização com NetDataContractSerializer independentemente da propriedade Binder, desative esta regra e a regra CA2311, e ative a regra CA2310.

NetDataContractSerializer é insegura e não pode ser tornada segura. Para obter mais informações, consulte o guia de segurança BinaryFormatter.

Como corrigir violações

  • Em vez disso, use um serializador seguro e não permita que um invasor especifique um tipo arbitrário para desserializar. Para obter mais informações, consulte as alternativas preferidas.
  • Torne os dados serializados invioláveis. Após a serialização, assine criptograficamente os dados serializados. Antes da desserialização, valide a assinatura criptográfica. Proteja a chave criptográfica de ser divulgada e planeje para girações de chave.
  • Essa opção torna o código vulnerável a ataques de negação de serviço e possíveis ataques de execução remota de código no futuro. Para obter mais informações, consulte o guia de segurança BinaryFormatter. Restrinja tipos de dados desserializados. Implemente um System.Runtime.Serialization.SerializationBinder personalizado. Antes de desserializar, defina a Binder propriedade como uma instância da sua SerializationBinder personalizado em todos os caminhos de código. No método substituído BindToType , se o tipo for inesperado, lance uma exceção para interromper a desserialização.

Quando suprimir avisos

NetDataContractSerializer é insegura e não pode ser tornada segura.

Configurar código para análise

Use as opções a seguir para configurar em quais partes da base de código executar essa regra.

Você pode configurar essas opções apenas para esta regra, para todas as regras às quais elas se aplicam ou para todas as regras nesta categoria (Security) às quais elas se aplicam. Para obter mais informações, consulte Opções de configuração da regra de qualidade de código.

Excluir símbolos específicos

Você pode excluir símbolos específicos, como tipos e métodos, da análise definindo a opção excluded_symbol_names. Por exemplo, para especificar que a regra não deve ser executada em nenhum código dentro de tipos nomeados MyType, adicione o seguinte par chave-valor a um arquivo .editorconfig em seu projeto:

dotnet_code_quality.CAXXXX.excluded_symbol_names = MyType

Observação

Substitua a parte XXXX do CAXXXX pelo ID da regra aplicável.

Formatos de nome de símbolo permitidos no valor da opção (separados por |):

  • Somente nome do símbolo (inclui todos os símbolos com o nome, independentemente do tipo ou namespace que o contém).
  • Nomes totalmente qualificados no formato de ID de documentação do símbolo. Cada nome de símbolo requer um prefixo de tipo de símbolo, como M: para métodos, T: para tipos e N: para namespaces.
  • .ctor para construtores e .cctor para construtores estáticos.

Exemplos:

Valor da opção Resumo
dotnet_code_quality.CAXXXX.excluded_symbol_names = MyType Corresponde a todos os símbolos denominados MyType.
dotnet_code_quality.CAXXXX.excluded_symbol_names = MyType1|MyType2 Corresponde a todos os símbolos denominados MyType1 ou MyType2.
dotnet_code_quality.CAXXXX.excluded_symbol_names = M:NS.MyType.MyMethod(ParamType) Combina o método MyMethod específico com a assinatura totalmente qualificada especificada.
dotnet_code_quality.CAXXXX.excluded_symbol_names = M:NS1.MyType1.MyMethod1(ParamType)|M:NS2.MyType2.MyMethod2(ParamType) Correlaciona métodos MyMethod1 e MyMethod2 específicos com as respetivas assinaturas totalmente qualificadas.

Excluir tipos específicos e seus tipos derivados

Você pode excluir tipos específicos e seus tipos derivados da análise definindo a opção excluded_type_names_with_derived_types. Por exemplo, para especificar que a regra não deve ser executada em nenhum método dentro de tipos nomeados MyType e seus tipos derivados, adicione o seguinte par chave-valor a um arquivo .editorconfig em seu projeto:

dotnet_code_quality.CAXXXX.excluded_type_names_with_derived_types = MyType

Observação

Substitua a parte XXXX do CAXXXX pelo ID da regra aplicável.

Formatos de nome de símbolo permitidos no valor da opção (separados por |):

  • Somente nome do tipo (inclui todos os tipos com o nome, independentemente do tipo ou namespace que o contém).
  • Nomes totalmente qualificados no formato de ID de documentação do símbolo, com um prefixo opcional T:.

Exemplos:

Valor da opção Resumo
dotnet_code_quality.CAXXXX.excluded_type_names_with_derived_types = MyType Corresponde a todos os tipos nomeados MyType e todos os seus tipos derivados.
dotnet_code_quality.CAXXXX.excluded_type_names_with_derived_types = MyType1|MyType2 Corresponde a todos os tipos nomeados como MyType1 ou MyType2, e todos os seus tipos derivados.
dotnet_code_quality.CAXXXX.excluded_type_names_with_derived_types = M:NS.MyType Corresponde a um tipo MyType específico com um determinado nome totalmente qualificado e todos os seus tipos derivados.
dotnet_code_quality.CAXXXX.excluded_type_names_with_derived_types = M:NS1.MyType1|M:NS2.MyType2 Correlaciona tipos MyType1 e MyType2 específicos com os respetivos nomes totalmente qualificados e todos os seus tipos derivados.

Exemplos de pseudocódigo

Violação 1

using System;
using System.IO;
using System.Runtime.Serialization;

public class BookRecordSerializationBinder : SerializationBinder
{
    public override Type BindToType(string assemblyName, string typeName)
    {
        // One way to discover expected types is through testing deserialization
        // of **valid** data and logging the types used.

        ////Console.WriteLine($"BindToType('{assemblyName}', '{typeName}')");

        if (typeName == "BookRecord")
        {
            return typeof(BookRecord);
        }
        else if (typeName == "AisleLocation")
        {
            return typeof(AisleLocation);
        }
        else
        {
            throw new ArgumentException("Unexpected type", nameof(typeName));
        }
    }
}

[DataContract]
public class BookRecord
{
    [DataMember]
    public string Title { get; set; }

    [DataMember]
    public AisleLocation Location { get; set; }
}

[DataContract]
public class AisleLocation
{
    [DataMember]
    public char Aisle { get; set; }

    [DataMember]
    public byte Shelf { get; set; }
}

public class Binders
{
    public static SerializationBinder BookRecord = new BookRecordSerializationBinder();
}

public class ExampleClass
{
    public BookRecord DeserializeBookRecord(byte[] bytes)
    {
        NetDataContractSerializer serializer = new NetDataContractSerializer();
        serializer.Binder = Binders.BookRecord;
        using (MemoryStream ms = new MemoryStream(bytes))
        {
            return (BookRecord) serializer.Deserialize(ms);
        }
    }
}
Imports System
Imports System.IO
Imports System.Runtime.Serialization

Public Class BookRecordSerializationBinder
    Inherits SerializationBinder

    Public Overrides Function BindToType(assemblyName As String, typeName As String) As Type
        ' One way to discover expected types is through testing deserialization
        ' of **valid** data and logging the types used.

        'Console.WriteLine($"BindToType('{assemblyName}', '{typeName}')")

        If typeName = "BinaryFormatterVB.BookRecord" Then
            Return GetType(BookRecord)
        Else If typeName = "BinaryFormatterVB.AisleLocation" Then
            Return GetType(AisleLocation)
        Else
            Throw New ArgumentException("Unexpected type", NameOf(typeName))
        End If
    End Function
End Class

<DataContract()>
Public Class BookRecord
    <DataMember()>
    Public Property Title As String

    <DataMember()>
    Public Property Location As AisleLocation
End Class

<DataContract()>
Public Class AisleLocation
    <DataMember()>
    Public Property Aisle As Char

    <DataMember()>
    Public Property Shelf As Byte
End Class

Public Class Binders
    Public Shared Property BookRecord As SerializationBinder = New BookRecordSerializationBinder()
End Class


Public Class ExampleClass
    Public Function DeserializeBookRecord(bytes As Byte()) As BookRecord
        Dim serializer As NetDataContractSerializer = New NetDataContractSerializer()
        serializer.Binder = Binders.BookRecord
        Using ms As MemoryStream = New MemoryStream(bytes)
            Return CType(serializer.Deserialize(ms), BookRecord)   ' CA2312 violation
        End Using
    End Function
End Class

Solução 1

using System;
using System.IO;
using System.Runtime.Serialization;

public class BookRecordSerializationBinder : SerializationBinder
{
    public override Type BindToType(string assemblyName, string typeName)
    {
        // One way to discover expected types is through testing deserialization
        // of **valid** data and logging the types used.

        ////Console.WriteLine($"BindToType('{assemblyName}', '{typeName}')");

        if (typeName == "BookRecord")
        {
            return typeof(BookRecord);
        }
        else if (typeName == "AisleLocation")
        {
            return typeof(AisleLocation);
        }
        else
        {
            throw new ArgumentException("Unexpected type", nameof(typeName));
        }
    }
}

[DataContract]
public class BookRecord
{
    [DataMember]
    public string Title { get; set; }

    [DataMember]
    public AisleLocation Location { get; set; }
}

[DataContract]
public class AisleLocation
{
    [DataMember]
    public char Aisle { get; set; }

    [DataMember]
    public byte Shelf { get; set; }
}

public class Binders
{
    public static SerializationBinder BookRecord = new BookRecordSerializationBinder();
}

public class ExampleClass
{
    public BookRecord DeserializeBookRecord(byte[] bytes)
    {
        NetDataContractSerializer serializer = new NetDataContractSerializer();

        // Ensure that Binder is always non-null before deserializing
        serializer.Binder = Binders.BookRecord ?? throw new Exception("Expected non-null");

        using (MemoryStream ms = new MemoryStream(bytes))
        {
            return (BookRecord) serializer.Deserialize(ms);
        }
    }
}
Imports System
Imports System.IO
Imports System.Runtime.Serialization

Public Class BookRecordSerializationBinder
    Inherits SerializationBinder

    Public Overrides Function BindToType(assemblyName As String, typeName As String) As Type
        ' One way to discover expected types is through testing deserialization
        ' of **valid** data and logging the types used.

        'Console.WriteLine($"BindToType('{assemblyName}', '{typeName}')")

        If typeName = "BinaryFormatterVB.BookRecord" Then
            Return GetType(BookRecord)
        Else If typeName = "BinaryFormatterVB.AisleLocation" Then
            Return GetType(AisleLocation)
        Else
            Throw New ArgumentException("Unexpected type", NameOf(typeName))
        End If
    End Function
End Class

<DataContract()>
Public Class BookRecord
    <DataMember()>
    Public Property Title As String

    <DataMember()>
    Public Property Location As AisleLocation
End Class

<DataContract()>
Public Class AisleLocation
    <DataMember()>
    Public Property Aisle As Char

    <DataMember()>
    Public Property Shelf As Byte
End Class

Public Class Binders
    Public Shared Property BookRecord As SerializationBinder = New BookRecordSerializationBinder()
End Class


Public Class ExampleClass
    Public Function DeserializeBookRecord(bytes As Byte()) As BookRecord
        Dim serializer As NetDataContractSerializer = New NetDataContractSerializer()

        ' Ensure that Binder is always non-null before deserializing
        serializer.Binder = If(Binders.BookRecord, New Exception("Expected non-null"))

        Using ms As MemoryStream = New MemoryStream(bytes)
            Return CType(serializer.Deserialize(ms), BookRecord)
        End Using
    End Function
End Class

Violação 2

using System;
using System.IO;
using System.Runtime.Serialization;

[DataContract]
public class BookRecord
{
    [DataMember]
    public string Title { get; set; }

    [DataMember]
    public string Author { get; set; }

    [DataMember]
    public int PageCount { get; set; }

    [DataMember]
    public AisleLocation Location { get; set; }
}

[DataContract]
public class AisleLocation
{
    [DataMember]
    public char Aisle { get; set; }

    [DataMember]
    public byte Shelf { get; set; }
}

public class ExampleClass
{
    public NetDataContractSerializer Serializer { get; set; }

    public BookRecord DeserializeBookRecord(byte[] bytes)
    {
        using (MemoryStream ms = new MemoryStream(bytes))
        {
            return (BookRecord) this.Serializer.Deserialize(ms);    // CA2312 violation
        }
    }
}
Imports System
Imports System.IO
Imports System.Runtime.Serialization

<DataContract()>
Public Class BookRecord
    <DataMember()>
    Public Property Title As String

    <DataMember()>
    Public Property Author As String

    <DataMember()>
    Public Property Location As AisleLocation
End Class

<DataContract()>
Public Class AisleLocation
    <DataMember()>
    Public Property Aisle As Char

    <DataMember()>
    Public Property Shelf As Byte
End Class

Public Class ExampleClass
    Public Property Serializer As NetDataContractSerializer

    Public Function DeserializeBookRecord(bytes As Byte()) As BookRecord
        Using ms As MemoryStream = New MemoryStream(bytes)
            Return CType(Me.Serializer.Deserialize(ms), BookRecord)    ' CA2312 violation
        End Using
    End Function
End Class

Solução 2

using System;
using System.IO;
using System.Runtime.Serialization;

public class BookRecordSerializationBinder : SerializationBinder
{
    public override Type BindToType(string assemblyName, string typeName)
    {
        // One way to discover expected types is through testing deserialization
        // of **valid** data and logging the types used.

        ////Console.WriteLine($"BindToType('{assemblyName}', '{typeName}')");

        if (typeName == "BookRecord")
        {
            return typeof(BookRecord);
        }
        else if (typeName == "AisleLocation")
        {
            return typeof(AisleLocation);
        }
        else
        {
            throw new ArgumentException("Unexpected type", nameof(typeName));
        }
    }
}

[DataContract]
public class BookRecord
{
    [DataMember]
    public string Title { get; set; }

    [DataMember]
    public string Author { get; set; }

    [DataMember]
    public int PageCount { get; set; }

    [DataMember]
    public AisleLocation Location { get; set; }
}

[DataContract]
public class AisleLocation
{
    [DataMember]
    public char Aisle { get; set; }

    [DataMember]
    public byte Shelf { get; set; }
}

public class ExampleClass
{
    public BookRecord DeserializeBookRecord(byte[] bytes)
    {
        NetDataContractSerializer serializer = new NetDataContractSerializer();
        serializer.Binder = new BookRecordSerializationBinder();
        using (MemoryStream ms = new MemoryStream(bytes))
        {
            return (BookRecord) serializer.Deserialize(ms);
        }
    }
}
Imports System
Imports System.IO
Imports System.Runtime.Serialization

Public Class BookRecordSerializationBinder
    Inherits SerializationBinder

    Public Overrides Function BindToType(assemblyName As String, typeName As String) As Type
        ' One way to discover expected types is through testing deserialization
        ' of **valid** data and logging the types used.

        'Console.WriteLine($"BindToType('{assemblyName}', '{typeName}')")

        If typeName = "BinaryFormatterVB.BookRecord" Then
            Return GetType(BookRecord)
        Else If typeName = "BinaryFormatterVB.AisleLocation" Then
            Return GetType(AisleLocation)
        Else
            Throw New ArgumentException("Unexpected type", NameOf(typeName))
        End If
    End Function
End Class

<DataContract()>
Public Class BookRecord
    <DataMember()>
    Public Property Title As String

    <DataMember()>
    Public Property Author As String

    <DataMember()>
    Public Property Location As AisleLocation
End Class

<DataContract()>
Public Class AisleLocation
    <DataMember()>
    Public Property Aisle As Char

    <DataMember()>
    Public Property Shelf As Byte
End Class

Public Class ExampleClass
    Public Function DeserializeBookRecord(bytes As Byte()) As BookRecord
        Dim serializer As NetDataContractSerializer = New NetDataContractSerializer()
        serializer.Binder = New BookRecordSerializationBinder()
        Using ms As MemoryStream = New MemoryStream(bytes)
            Return CType(serializer.Deserialize(ms), BookRecord)
        End Using
    End Function
End Class