Udostępnij za pośrednictwem


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 łamiąca lub nienaruszająca Niezgodność
Domyślnie włączone na platformie .NET 10 Nie.
Zastosowane języki C# i Visual Basic

Przyczyna

Metoda deserializacji System.Runtime.Serialization.Formatters.Binary.BinaryFormatter została wywołana lub przywołana, a właściwość Binder może być 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 podczas 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 może być wartością null. Jeśli chcesz uniemożliwić deserializację BinaryFormatter niezależnie od właściwości Binder, 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.
  • Spraw, aby zserializowane dane były odporne na manipulacje. Po serializacji kryptograficznie podpisz serializowane dane. Przed deserializacji zweryfikuj podpis kryptograficzny. Chroń klucz kryptograficzny przed ujawnieniem i zaprojektuj rotacje 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 element System.Runtime.Serialization.SerializationBinder. Przed deserializacją ustaw Binder właściwość na niestandardowe wystąpienie SerializationBinder we wszystkich ścieżkach kodu. W metodzie przesłoniętej BindToType, jeśli typ jest nieoczekiwany, należy rzucić wyjątek, aby zatrzymać deserializację.

Kiedy pomijać ostrzeżenia

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

Konfigurowanie kodu do analizowania

Użyj poniższych opcji, aby skonfigurować, na które części bazy kodu ma być stosowana ta reguła.

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

Wyklucz określone symbole

Z analizy można wykluczyć określone symbole, takie jak typy i metody, ustawiając opcję excluded_symbol_names. 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

Notatka

Zastąp część XXXXCAXXXX identyfikatorem odpowiedniej reguły.

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 symbolu wymaga prefiksu określającego rodzaj symbolu, takiego jak M: dla metody, T: dla typów i N: dla przestrzeni nazw.
  • .ctor dla konstruktorów i .cctor dla 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) Dopasowuje określoną metodę MyMethod do określonej, w pełni kwalifikowanej sygnatury.
dotnet_code_quality.CAXXXX.excluded_symbol_names = M:NS1.MyType1.MyMethod1(ParamType)|M:NS2.MyType2.MyMethod2(ParamType) Dopasowuje określone metody MyMethod1 i MyMethod2 z odpowiednimi w pełni kwalifikowanymi sygnaturami.

Wyklucz określone typy i ich pochodne typy

Określone typy i ich typy pochodne można wykluczyć z analizy, ustawiając opcję excluded_type_names_with_derived_types. 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

Notatka

Zastąp część XXXXCAXXXX identyfikatorem odpowiedniej reguły.

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

  • Podaj tylko nazwę typu (obejmuje wszystkie typy o tej nazwie, bez względu na typ zawierający lub przestrzeń nazw).
  • W pełni kwalifikowane nazwy w formacie dokumentacyjnego identyfikatora symbolu, z opcjonalnym prefiksem T:.

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 Dopasowuje określony typ MyType do danej w pełni kwalifikowanej nazwy i do wszystkich jego pochodnych typów.
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 pseudokodu

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