CA2302: Upewnij się, że parametr BinaryFormatter.Binder został ustawiony przed wywołaniem metody BinaryFormatter.Deserialize

Właściwości Wartość
Identyfikator reguły CA2302
Tytuł Upewnij się, że właściwość BinaryFormatter.Binder jest ustawiona przed wywołaniem metody BinaryFormatter.Deserialize
Kategoria Bezpieczeństwo
Poprawka powodująca niezgodność lub niezgodność Niezgodność
Domyślnie włączone na platformie .NET 8 Nie.

Przyczyna

Metoda System.Runtime.Serialization.Formatters.Binary.BinaryFormatter deserializacji została wywołana lub odwołana, a Binder właściwość może mieć wartość null.

Ta reguła jest podobna do CA2301, ale analiza nie może określić, czy wartość Binder jest zdecydowanie równa null.

Domyślnie ta reguła analizuje całą bazę kodu, ale można to skonfigurować.

Ostrzeżenie

Ograniczanie typów za pomocą klasy SerializationBinder nie może zapobiec wszystkim atakom. Aby uzyskać więcej informacji, zobacz Przewodnik po zabezpieczeniach BinaryFormatter.

Opis reguły

Niezabezpieczone deserializatory są podatne na deserializacji niezaufanych danych. Osoba atakująca może zmodyfikować serializowane dane w celu uwzględnienia nieoczekiwanych typów w celu wstrzyknięcia obiektów ze złośliwymi skutkami ubocznymi. Atak na niezabezpieczonego deserializatora może na przykład wykonywać polecenia w bazowym systemie operacyjnym, komunikować się za pośrednictwem sieci lub usuwać pliki.

Ta reguła znajduje System.Runtime.Serialization.Formatters.Binary.BinaryFormatter wywołania metody deserializacji lub odwołania, gdy Binder wartość może mieć wartość null. Jeśli chcesz uniemożliwić deserializacji BinaryFormatter niezależnie od Binder właściwości, wyłącz tę regułę i CA2301 i włącz regułę CA2300.

Jak naprawić naruszenia

  • Zamiast tego użyj bezpiecznego serializatora i nie zezwalaj osobie atakującej na określenie dowolnego typu do deserializacji. Aby uzyskać więcej informacji, zobacz Preferowane alternatywy.
  • Ustaw serializacji danych na dowód manipulacji. Po serializacji kryptograficznie podpisz serializowane dane. Przed deserializacji zweryfikuj podpis kryptograficzny. Ochrona klucza kryptograficznego przed ujawnieniem i projektowaniem rotacji kluczy.
  • Ta opcja sprawia, że kod jest podatny na ataki typu "odmowa usługi" i możliwe ataki zdalnego wykonywania kodu w przyszłości. Aby uzyskać więcej informacji, zobacz Przewodnik po zabezpieczeniach BinaryFormatter. Ogranicz deserializowane typy. Zaimplementuj niestandardowy System.Runtime.Serialization.SerializationBinderelement . Przed deserializacji ustaw Binder właściwość na wystąpienie niestandardowe SerializationBinder we wszystkich ścieżkach kodu. W metodzie przesłoniętej BindToType , jeśli typ jest nieoczekiwany, należy zgłosić wyjątek, aby zatrzymać deserializacji.

Kiedy pomijać ostrzeżenia

BinaryFormatter jest niezabezpieczony i nie można go zabezpieczyć.

Konfigurowanie kodu do analizowania

Użyj poniższych opcji, aby skonfigurować, które części bazy kodu mają być uruchamiane w tej regule.

Możesz skonfigurować te opcje tylko dla tej reguły, dla wszystkich reguł, do których ma ona zastosowanie, lub dla wszystkich reguł w tej kategorii (Zabezpieczenia), których dotyczy. Aby uzyskać więcej informacji, zobacz Opcje konfiguracji reguły jakości kodu.

Wykluczanie określonych symboli

Z analizy można wykluczyć określone symbole, takie jak typy i metody. Aby na przykład określić, że reguła nie powinna być uruchamiana w żadnym kodzie w typach o nazwie MyType, dodaj następującą parę klucz-wartość do pliku editorconfig w projekcie:

dotnet_code_quality.CAXXXX.excluded_symbol_names = MyType

Dozwolone formaty nazw symboli w wartości opcji (oddzielone przez |):

  • Tylko nazwa symbolu (zawiera wszystkie symbole o nazwie, niezależnie od typu zawierającego lub przestrzeni nazw).
  • W pełni kwalifikowane nazwy w formacie identyfikatora dokumentacji symbolu. Każda nazwa symboli wymaga prefiksu typu symboli, takiego jak M: metody, T: dla typów i N: przestrzeni nazw.
  • .ctor dla konstruktorów i .cctor konstruktorów statycznych.

Przykłady:

Wartość opcji Podsumowanie
dotnet_code_quality.CAXXXX.excluded_symbol_names = MyType Pasuje do wszystkich symboli o nazwie MyType.
dotnet_code_quality.CAXXXX.excluded_symbol_names = MyType1|MyType2 Pasuje do wszystkich symboli o nazwie MyType1 lub MyType2.
dotnet_code_quality.CAXXXX.excluded_symbol_names = M:NS.MyType.MyMethod(ParamType) Pasuje do określonej metody MyMethod z określonym w pełni kwalifikowanym podpisem.
dotnet_code_quality.CAXXXX.excluded_symbol_names = M:NS1.MyType1.MyMethod1(ParamType)|M:NS2.MyType2.MyMethod2(ParamType) Pasuje do określonych metod MyMethod1 i MyMethod2 z odpowiednimi w pełni kwalifikowanymi podpisami.

Wykluczanie określonych typów i ich typów pochodnych

Z analizy można wykluczyć określone typy i ich typy pochodne. Aby na przykład określić, że reguła nie powinna być uruchamiana na żadnych metodach w typach nazwanych MyType i ich typach pochodnych, dodaj następującą parę klucz-wartość do pliku .editorconfig w projekcie:

dotnet_code_quality.CAXXXX.excluded_type_names_with_derived_types = MyType

Dozwolone formaty nazw symboli w wartości opcji (oddzielone przez |):

  • Nazwa typu (zawiera tylko wszystkie typy o nazwie, niezależnie od typu zawierającego lub przestrzeni nazw).
  • W pełni kwalifikowane nazwy w formacie identyfikatora dokumentacji symbolu z opcjonalnym T: prefiksem.

Przykłady:

Wartość opcji Podsumowanie
dotnet_code_quality.CAXXXX.excluded_type_names_with_derived_types = MyType Pasuje do wszystkich typów nazwanych MyType i wszystkich ich typów pochodnych.
dotnet_code_quality.CAXXXX.excluded_type_names_with_derived_types = MyType1|MyType2 Dopasuje wszystkie typy o nazwie MyType1 lub MyType2 i wszystkie ich typy pochodne.
dotnet_code_quality.CAXXXX.excluded_type_names_with_derived_types = M:NS.MyType Pasuje do określonego typu MyType z daną w pełni kwalifikowaną nazwą i wszystkimi jego typami pochodnymi.
dotnet_code_quality.CAXXXX.excluded_type_names_with_derived_types = M:NS1.MyType1|M:NS2.MyType2 Pasuje do określonych typów MyType1 i MyType2 z odpowiednimi w pełni kwalifikowanymi nazwami i wszystkimi ich typami pochodnymi.

Przykłady przykładów kodu przykładowego

Naruszenie 1

using System;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;

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));
        }
    }
}

[Serializable]
public class BookRecord
{
    public string Title { get; set; }
    public AisleLocation Location { get; set; }
}

[Serializable]
public class AisleLocation
{
    public char Aisle { get; set; }
    public byte Shelf { get; set; }
}

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

public class ExampleClass
{
    public BookRecord DeserializeBookRecord(byte[] bytes)
    {
        BinaryFormatter formatter = new BinaryFormatter();
        formatter.Binder = Binders.BookRecord;
        using (MemoryStream ms = new MemoryStream(bytes))
        {
            return (BookRecord)formatter.Deserialize(ms);    // CA2302 violation
        }
    }
}
Imports System
Imports System.IO
Imports System.Runtime.Serialization
Imports System.Runtime.Serialization.Formatters.Binary

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

<Serializable()>
Public Class BookRecord
    Public Property Title As String
    Public Property Location As AisleLocation
End Class

<Serializable()>
Public Class AisleLocation
    Public Property Aisle As Char
    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 formatter As BinaryFormatter = New BinaryFormatter()
        formatter.Binder = Binders.BookRecord
        Using ms As MemoryStream = New MemoryStream(bytes)
            Return CType(formatter.Deserialize(ms), BookRecord)    ' CA2302 violation
        End Using
    End Function
End Class

Rozwiązanie 1

using System;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;

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));
        }
    }
}

[Serializable]
public class BookRecord
{
    public string Title { get; set; }
    public AisleLocation Location { get; set; }
}

[Serializable]
public class AisleLocation
{
    public char Aisle { get; set; }
    public byte Shelf { get; set; }
}

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

public class ExampleClass
{
    public BookRecord DeserializeBookRecord(byte[] bytes)
    {
        BinaryFormatter formatter = new BinaryFormatter();

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

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

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

<Serializable()>
Public Class BookRecord
    Public Property Title As String
    Public Property Location As AisleLocation
End Class

<Serializable()>
Public Class AisleLocation
    Public Property Aisle As Char
    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 formatter As BinaryFormatter = New BinaryFormatter()

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

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

Naruszenie 2

using System;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;

[Serializable]
public class BookRecord
{
    public string Title { get; set; }
    public AisleLocation Location { get; set; }
}

[Serializable]
public class AisleLocation
{
    public char Aisle { get; set; }
    public byte Shelf { get; set; }
}

public class ExampleClass
{
    public BinaryFormatter Formatter { get; set; }

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

<Serializable()>
Public Class BookRecord
    Public Property Title As String
    Public Property Location As AisleLocation
End Class

<Serializable()>
Public Class AisleLocation
    Public Property Aisle As Char
    Public Property Shelf As Byte
End Class

Public Class ExampleClass
    Public Property Formatter As BinaryFormatter

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

Rozwiązanie 2

using System;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;

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));
        }
    }
}

[Serializable]
public class BookRecord
{
    public string Title { get; set; }
    public AisleLocation Location { get; set; }
}

[Serializable]
public class AisleLocation
{
    public char Aisle { get; set; }
    public byte Shelf { get; set; }
}

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

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

<Serializable()>
Public Class BookRecord
    Public Property Title As String
    Public Property Location As AisleLocation
End Class

<Serializable()>
Public Class AisleLocation
    Public Property Aisle As Char
    Public Property Shelf As Byte
End Class

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