Zagadnienia związane z zabezpieczeniami danych

Podczas pracy z danymi w programie Windows Communication Foundation (WCF) należy wziąć pod uwagę wiele kategorii zagrożeń. Na poniższej liście przedstawiono najważniejsze klasy zagrożeń związane z przetwarzaniem danych. Program WCF udostępnia narzędzia do eliminowania tych zagrożeń.

  • Denial of service (odmowa usługi)

    Podczas odbierania niezaufanych danych dane mogą spowodować, że strona odbierającego uzyskuje dostęp do nieproporcjonalnej ilości różnych zasobów, takich jak pamięć, wątki, dostępne połączenia lub cykle procesora, powodując długie obliczenia. Atak typu "odmowa usługi" na serwer może spowodować awarię i nie może przetworzyć komunikatów z innych, uzasadnionych klientów.

  • Złośliwe wykonywanie kodu

    Przychodzące niezaufane dane powodują, że po stronie odbierającej zostanie uruchomiony kod, który nie zamierzał.

  • Information disclosure (ujawnienie informacji)

    Zdalny atakujący zmusza osobę odbieraną do odpowiadania na żądania w taki sposób, aby ujawnić więcej informacji niż zamierza.

Kod dostarczony przez użytkownika i zabezpieczenia dostępu kodu

Wiele miejsc w infrastruktury programu Windows Communication Foundation (WCF) uruchamia kod, który jest dostarczany przez użytkownika. Na przykład DataContractSerializer aparat serializacji może wywołać metody dostępu i get akcesoriów właściwości set dostarczonych przez użytkownika. Infrastruktura kanału WCF może również wywoływać klasy pochodne dostarczone przez użytkownika.Message

Autor kodu odpowiada za zapewnienie, że nie istnieją żadne luki w zabezpieczeniach. Jeśli na przykład utworzysz typ kontraktu danych z właściwością składową danych typu liczba całkowita, a w set implementacji metody dostępu przydzielisz tablicę na podstawie wartości właściwości, uwidaczniasz możliwość ataku typu "odmowa usługi", jeśli złośliwy komunikat zawiera bardzo dużą wartość dla tego elementu członkowskiego danych. Ogólnie rzecz biorąc, należy unikać alokacji na podstawie danych przychodzących lub długotrwałego przetwarzania w kodzie dostarczonym przez użytkownika (zwłaszcza jeśli długotrwałe przetwarzanie może być spowodowane niewielką ilością danych przychodzących). Podczas przeprowadzania analizy zabezpieczeń kodu dostarczonego przez użytkownika należy również wziąć pod uwagę wszystkie przypadki awarii (czyli wszystkie gałęzie kodu, w których są zgłaszane wyjątki).

Ostatecznym przykładem kodu dostarczonego przez użytkownika jest kod wewnątrz implementacji usługi dla każdej operacji. Bezpieczeństwo implementacji usługi jest Twoim zadaniem. Łatwo jest przypadkowo utworzyć niezabezpieczone implementacje operacji, które mogą powodować luki w zabezpieczeniach typu "odmowa usługi". Na przykład operacja, która pobiera ciąg i zwraca listę klientów z bazy danych, której nazwa zaczyna się od tego ciągu. Jeśli pracujesz z dużą bazą danych, a przekazywany ciąg jest tylko jedną literą, kod może próbować utworzyć komunikat większy niż cała dostępna pamięć, powodując niepowodzenie całej usługi. (Element OutOfMemoryException nie jest możliwy do odzyskania w programie .NET Framework i zawsze powoduje zakończenie aplikacji).

Upewnij się, że żaden złośliwy kod nie jest podłączony do różnych punktów rozszerzalności. Jest to szczególnie istotne w przypadku uruchamiania w ramach częściowego zaufania, obsługi typów z częściowo zaufanych zestawów lub tworzenia składników używanych przez częściowo zaufany kod. Aby uzyskać więcej informacji, zobacz sekcję "Częściowe zagrożenia zaufania" w dalszej sekcji.

Należy pamiętać, że w przypadku uruchamiania częściowego zaufania infrastruktura serializacji kontraktu danych obsługuje tylko ograniczony podzestaw modelu programowania kontraktów danych — na przykład prywatne elementy członkowskie danych lub typy używające atrybutu SerializableAttribute nie są obsługiwane. Aby uzyskać więcej informacji, zobacz Częściowe zaufanie.

Uwaga

Zabezpieczenia dostępu kodu (CAS) zostały wycofane we wszystkich wersjach programu .NET Framework i .NET. Najnowsze wersje platformy .NET nie honorują adnotacji CAS i generują błędy, jeśli są używane interfejsy API związane z usługą CAS. Deweloperzy powinni szukać alternatywnych sposobów wykonywania zadań zabezpieczeń.

Unikanie niezamierzonego ujawnienia informacji

Podczas projektowania typów z możliwością serializacji z uwzględnieniem zabezpieczeń ujawnienie informacji jest możliwe.

Rozważ następujące punkty:

  • Model DataContractSerializer programowania umożliwia ujawnienie prywatnych i wewnętrznych danych poza typem lub zestawem podczas serializacji. Ponadto podczas eksportowania schematu można uwidocznić kształt typu. Pamiętaj, aby zrozumieć projekcję serializacji typu. Jeśli nie chcesz uwidocznić niczego, wyłącz jego serializowanie (na przykład nie stosując atrybutu DataMemberAttribute w przypadku kontraktu danych).

  • Należy pamiętać, że ten sam typ może mieć wiele projekcji serializacji, w zależności od serializatora w użyciu. Ten sam typ może uwidaczniać jeden zestaw danych w przypadku użycia z DataContractSerializer i innym zestawem danych w przypadku użycia z elementem XmlSerializer. Przypadkowe użycie niewłaściwego serializatora może prowadzić do ujawnienia informacji.

  • Użycie w starszej XmlSerializer wersji zdalnego wywołania procedury (RPC)/trybu zakodowanego może przypadkowo uwidocznić kształt grafu obiektu po stronie wysyłania do strony odbierającego.

Zapobieganie atakom typu "odmowa usługi"

Normy sprzedaży

Przyczyną przydzielenia znacznej ilości pamięci po stronie odbieranej jest potencjalny atak typu "odmowa usługi". Chociaż ta sekcja koncentruje się na problemach z zużyciem pamięci wynikających z dużych komunikatów, mogą wystąpić inne ataki. Na przykład komunikaty mogą używać nieproporcjonalnego czasu przetwarzania.

Ataki typu "odmowa usługi" są zwykle ograniczane przy użyciu limitów przydziału. W przypadku przekroczenia limitu przydziału jest zwykle zgłaszany QuotaExceededException wyjątek. Bez limitu przydziału złośliwy komunikat może spowodować uzyskanie dostępu do całej dostępnej pamięci, co spowoduje OutOfMemoryException wyjątek lub dostęp do wszystkich dostępnych stosów, co spowoduje StackOverflowExceptionuzyskanie dostępu do elementu .

Przekroczony limit przydziału jest możliwy do odzyskania; W przypadku napotkania w uruchomionej usłudze komunikat obecnie przetwarzany jest odrzucany, a usługa nadal działa i przetwarza dalsze komunikaty. Scenariusze braku pamięci i przepełnienia stosu nie są jednak możliwe do odzyskania w dowolnym miejscu w programie .NET Framework; usługa kończy działanie, jeśli wystąpią takie wyjątki.

Przydziały w programie WCF nie obejmują żadnej wstępnej alokacji. Jeśli na przykład limit przydziału MaxReceivedMessageSize (znaleziony w różnych klasach) jest ustawiony na 128 KB, nie oznacza to, że 128 KB jest automatycznie przydzielane dla każdego komunikatu. Przydzielona kwota rzeczywista zależy od rzeczywistego rozmiaru komunikatu przychodzącego.

Wiele przydziałów jest dostępnych w warstwie transportu. Są to limity przydziału wymuszane przez określony używany kanał transportu (HTTP, TCP itd.). W tym temacie omówiono niektóre z tych limitów przydziału, ale te limity przydziału zostały szczegółowo opisane w temacie Limity przydziału transportu.

Luka w zabezpieczeniach w formie tabeli skrótu

Luka w zabezpieczeniach występuje, gdy kontrakty danych zawierają tabele skrótów lub kolekcje. Problem występuje, jeśli duża liczba wartości jest wstawiana do tabeli skrótów, w której duża liczba tych wartości generuje tę samą wartość skrótu. Może to być używane jako atak DOS. Tę lukę w zabezpieczeniach można rozwiązać, ustawiając limit przydziału powiązania MaxReceivedMessageSize. Należy zachować ostrożność podczas ustawiania tego limitu przydziału, aby zapobiec takim atakom. Ten limit przydziału nakłada górny limit rozmiaru komunikatu WCF. Ponadto unikaj używania tabel skrótów lub kolekcji w kontraktach danych.

Ograniczanie zużycia pamięci bez przesyłania strumieniowego

Model zabezpieczeń wokół dużych komunikatów zależy od tego, czy przesyłanie strumieniowe jest używane. W podstawowym przypadku bez strumienia komunikaty są buforowane w pamięci. W takim przypadku należy użyć limitu przydziału MaxReceivedMessageSize dla TransportBindingElement powiązań dostarczonych przez system lub, aby chronić przed dużymi komunikatami, ograniczając maksymalny rozmiar komunikatu do uzyskania dostępu. Należy pamiętać, że usługa może jednocześnie przetwarzać wiele komunikatów, w takim przypadku wszystkie są w pamięci. Użyj funkcji ograniczania przepustowości, aby wyeliminować to zagrożenie.

Należy również pamiętać, że MaxReceivedMessageSize nie umieszcza górnej granicy na zużycie pamięci dla komunikatów, ale ogranicza je do stałego współczynnika. Jeśli na przykład MaxReceivedMessageSize zostanie odebrany komunikat o rozmiarze 1 MB, a następnie zostanie zdeserializowany, wymagana jest dodatkowa pamięć zawierająca deserializowany graf obiektu, co spowoduje całkowite zużycie pamięci ponad 1 MB. Z tego powodu należy unikać tworzenia typów możliwych do serializacji, które mogłyby spowodować znaczne zużycie pamięci bez dużej ilości danych przychodzących. Na przykład kontrakt danych "MyContract" z 50 opcjonalnymi polami składowymi danych i dodatkowymi 100 polami prywatnymi można utworzyć przy użyciu konstrukcji XML "<MyContract/>". Ten kod XML powoduje uzyskanie dostępu do pamięci dla 150 pól. Należy pamiętać, że domyślnie elementy członkowskie danych są opcjonalne. Problem jest złożony, gdy taki typ jest częścią tablicy.

MaxReceivedMessageSize sam nie wystarczy, aby zapobiec atakom typu "odmowa usługi". Na przykład deserializator może być zmuszony do deserializacji głęboko zagnieżdżonego grafu obiektu (obiektu zawierającego inny obiekt, który zawiera jeszcze jeden i tak dalej) przez komunikat przychodzący. DataContractSerializer Metody i XmlSerializer wywołania w sposób zagnieżdżony w celu deserializacji takich grafów. Głębokie zagnieżdżanie wywołań metod może spowodować nieodwracalne StackOverflowException. To zagrożenie jest ograniczane przez ustawienie MaxDepth limitu przydziału w celu ograniczenia poziomu zagnieżdżania kodu XML, zgodnie z opisem w sekcji "Używanie Sejf XML" w dalszej części tematu.

Ustawienie dodatkowych limitów przydziału na MaxReceivedMessageSize wartość jest szczególnie ważne podczas korzystania z kodowania binarnego XML. Użycie kodowania binarnego jest nieco równoważne kompresji: mała grupa bajtów w komunikacie przychodzącym może reprezentować dużo danych. W związku z tym nawet komunikat pasujący do limitu MaxReceivedMessageSize może zająć znacznie więcej pamięci w w pełni rozwiniętym formularzu. Aby wyeliminować takie zagrożenia specyficzne dla kodu XML, wszystkie limity przydziału czytnika XML muszą być ustawione poprawnie, zgodnie z opisem w sekcji "Używanie Sejf XML" w dalszej części tego tematu.

Ograniczanie zużycia pamięci przy użyciu przesyłania strumieniowego

Podczas przesyłania strumieniowego można użyć małego MaxReceivedMessageSize ustawienia, aby chronić przed atakami typu "odmowa usługi". Jednak bardziej skomplikowane scenariusze są możliwe w przypadku przesyłania strumieniowego. Na przykład usługa przekazywania plików akceptuje pliki większe niż cała dostępna pamięć. W takim przypadku ustaw MaxReceivedMessageSize wartość na bardzo dużą wartość, oczekując, że prawie żadne dane nie są buforowane w pamięci, a strumienie komunikatów bezpośrednio na dysku. Jeśli złośliwy komunikat może w jakiś sposób wymusić buforowanie danych przez usługę WCF zamiast przesyłania strumieniowego w tym przypadku, MaxReceivedMessageSize nie będzie już chronić przed komunikatem, który uzyskuje dostęp do całej dostępnej pamięci.

Aby wyeliminować to zagrożenie, istnieją określone ustawienia limitu przydziału dla różnych składników przetwarzania danych WCF, które ograniczają buforowanie. Najważniejszą MaxBufferSize z nich jest właściwość różnych elementów wiązania transportu i standardowych powiązań. Podczas przesyłania strumieniowego ten limit przydziału należy ustawić z uwzględnieniem maksymalnej ilości pamięci, którą chcesz przydzielić na komunikat. Podobnie jak w przypadku MaxReceivedMessageSizeparametru , ustawienie nie nakłada bezwzględnej maksymalnej ilości pamięci, ale ogranicza je tylko do stałego współczynnika. Ponadto, podobnie jak w przypadku MaxReceivedMessageSizeprogramu , należy pamiętać o możliwości jednoczesnego przetwarzania wielu komunikatów.

MaxBufferSize Details

Właściwość MaxBufferSize ogranicza wszystkie operacje buforowania zbiorczego w programie WCF. Na przykład WCF zawsze buforuje nagłówki PROTOKOŁU SOAP i błędy PROTOKOŁU SOAP, a także wszystkie elementy MIME, które nie są w naturalnej kolejności odczytu w komunikacie mechanizmu optymalizacji transmisji komunikatów (MTOM). To ustawienie ogranicza ilość buforowania we wszystkich tych przypadkach.

Program WCF umożliwia przekazanie MaxBufferSize wartości do różnych składników, które mogą buforować. Na przykład niektóre CreateMessage przeciążenia Message klasy przyjmują maxSizeOfHeaders parametr . Program WCF przekazuje wartość do tego parametru MaxBufferSize , aby ograniczyć ilość buforowania nagłówka PROTOKOŁU SOAP. Ważne jest, aby ustawić ten parametr bezpośrednio podczas korzystania z Message klasy. Ogólnie rzecz biorąc, w przypadku używania składnika w programie WCF, który przyjmuje parametry limitu przydziału, ważne jest, aby zrozumieć implikacje zabezpieczeń tych parametrów i ustawić je poprawnie.

Koder komunikatów MTOM ma MaxBufferSize również ustawienie. W przypadku używania powiązań standardowych jest ona ustawiana automatycznie na wartość na poziomie MaxBufferSize transportu. Jednak w przypadku używania elementu powiązania kodera komunikatów MTOM do konstruowania powiązania niestandardowego należy ustawić MaxBufferSize właściwość na bezpieczną wartość podczas przesyłania strumieniowego.

Ataki przesyłane strumieniowo oparte na formacie XML

MaxBufferSize samodzielnie nie wystarczy, aby upewnić się, że nie można wymusić buforowania w przypadku przesyłania strumieniowego. Na przykład czytniki XML programu WCF zawsze buforować cały tag początkowy elementu XML podczas rozpoczynania odczytywania nowego elementu. Odbywa się to tak, aby przestrzenie nazw i atrybuty były prawidłowo przetwarzane. Jeśli MaxReceivedMessageSize skonfigurowano duże rozmiary (na przykład w celu włączenia scenariusza przesyłania strumieniowego dużych plików bezpośrednio na dysku), może zostać skonstruowany złośliwy komunikat, w którym cała treść komunikatu jest dużym tagiem początkowym elementu XML. Próba odczytania go powoduje wyświetlenie elementu OutOfMemoryException. Jest to jeden z wielu możliwych ataków typu "odmowa usługi" opartych na kodzie XML, które mogą zostać złagodzone przy użyciu przydziałów czytnika XML omówionego w sekcji "Korzystanie z kodu XML Sejf" w dalszej części tego tematu. Podczas przesyłania strumieniowego szczególnie ważne jest ustawienie wszystkich tych limitów przydziału.

Mieszanie modeli programowania przesyłania strumieniowego i buforowania

Wiele możliwych ataków wynika z mieszania modeli programowania przesyłania strumieniowego i przesyłania strumieniowego w tej samej usłudze. Załóżmy, że istnieje kontrakt usługi z dwoma operacjami: jeden przyjmuje Stream tablicę pewnego typu niestandardowego. Załóżmy również, że MaxReceivedMessageSize jest ona ustawiona na dużą wartość, aby umożliwić pierwszą operację przetwarzania dużych strumieni. Niestety, oznacza to, że duże komunikaty mogą być teraz wysyłane do drugiej operacji, a deserializator buforuje dane w pamięci jako tablicę przed wywołaniem operacji. Jest to potencjalny atak typu "odmowa usługi": MaxBufferSize limit przydziału nie ogranicza rozmiaru treści komunikatu, z którym działa deserializator.

Z tego powodu należy unikać mieszania operacji opartych na strumieniu i bez strumienia w tym samym kontrakcie. Jeśli absolutnie musisz mieszać dwa modele programowania, należy użyć następujących środków ostrożności:

  • Wyłącz funkcję, IExtensibleDataObject ustawiając IgnoreExtensionDataObject właściwość właściwości na ServiceBehaviorAttributetrue. Dzięki temu tylko członkowie, którzy są częścią kontraktu, są deserializowane.

  • MaxItemsInObjectGraph Ustaw właściwość elementu DataContractSerializer na bezpieczną wartość. Ten limit przydziału jest również dostępny dla atrybutu ServiceBehaviorAttribute lub za pośrednictwem konfiguracji. Ten limit przydziału ogranicza liczbę obiektów, które są deserializowane w jednym odcinku deserializacji. Zwykle każdy parametr operacji lub część treści komunikatu w kontrakcie komunikatów jest deserializowany w jednym odcinku. Podczas deserializacji tablic każdy wpis tablicy jest liowany jako oddzielny obiekt.

  • Ustaw wszystkie limity przydziału czytnika XML na bezpieczne wartości. Zwróć uwagę na MaxDepthciągi , MaxStringContentLengthi MaxArrayLength i unikaj ciągów w operacjach bez przesyłania strumieniowego.

  • Przejrzyj listę znanych typów, mając na uwadze, że w dowolnym momencie można utworzyć wystąpienie dowolnego z nich (zobacz sekcję "Zapobieganie niezamierzonym typom przed załadowaniem" w dalszej części tego tematu).

  • Nie należy używać żadnych typów, które implementują IXmlSerializable interfejs buforujący dużo danych. Nie należy dodawać takich typów do listy znanych typów.

  • Nie używaj XmlElementtablic, XmlNode tablic, Byte tablic ani typów implementujących ISerializable kontrakt.

  • Nie należy używać XmlElementtablic, XmlNode tablic, Byte tablic ani typów, które implementują ISerializable na liście znanych typów.

Powyższe środki ostrożności mają zastosowanie, gdy operacja bez przesyłania strumieniowego używa elementu DataContractSerializer. Nigdy nie mieszaj modeli programowania przesyłania strumieniowego i przesyłania strumieniowego w tej samej usłudze, jeśli używasz XmlSerializerelementu , ponieważ nie ma ochrony przydziału MaxItemsInObjectGraph .

Powolne ataki strumieniowe

Klasa ataków typu "odmowa usługi" przesyłania strumieniowego nie obejmuje zużycia pamięci. Zamiast tego atak obejmuje powolnego nadawcę lub odbiorcę danych. Podczas oczekiwania na wysłanie lub odebranie danych zasoby, takie jak wątki i dostępne połączenia, zostaną wyczerpane. Taka sytuacja może wystąpić w wyniku złośliwego ataku lub z uzasadnionego nadawcy/odbiornika w wolnym połączeniu sieciowym.

Aby wyeliminować te ataki, należy prawidłowo ustawić limity czasu transportu. Aby uzyskać więcej informacji, zobacz Limity przydziału transportu. Po drugie, nigdy nie używaj synchronicznych Read ani Write operacji podczas pracy ze strumieniami w programie WCF.

Używanie Sejf XML

Uwaga

Chociaż ta sekcja dotyczy kodu XML, informacje dotyczą również dokumentów JavaScript Object Notation (JSON). Przydziały działają podobnie przy użyciu mapowania między formatami JSON i XML.

Zabezpieczanie czytników XML

Zestaw informacji XML stanowi podstawę całego przetwarzania komunikatów w programie WCF. W przypadku akceptowania danych XML z niezaufanego źródła istnieje wiele możliwości ataku typu "odmowa usługi", które muszą zostać złagodzone. Program WCF zapewnia specjalne, bezpieczne czytniki XML. Te czytniki są tworzone automatycznie podczas korzystania z jednego ze standardowych kodowań w programie WCF (tekst, binarny lub MTOM).

Niektóre funkcje zabezpieczeń tych czytelników są zawsze aktywne. Na przykład czytelnicy nigdy nie przetwarzają definicji typów dokumentów (DTD), które są potencjalnym źródłem ataków typu "odmowa usługi" i nigdy nie powinny pojawiać się w wiarygodnych komunikatach PROTOKOŁU SOAP. Inne funkcje zabezpieczeń obejmują limity przydziału czytelnika, które należy skonfigurować, które opisano w poniższej sekcji.

Podczas bezpośredniej pracy z czytnikami XML (na przykład podczas pisania własnego niestandardowego kodera lub pracy bezpośrednio z Message klasą) zawsze używaj bezpiecznych czytników WCF, gdy istnieje szansa na pracę z niezaufanymi danymi. Utwórz bezpieczne czytniki, wywołując jedno z przeciążeń CreateTextReadermetody fabryki statycznej , CreateBinaryReaderlub CreateMtomReader w XmlDictionaryReader klasie . Podczas tworzenia czytnika należy przekazać wartości bezpiecznego limitu przydziału. Nie należy wywoływać Create przeciążeń metody. Nie tworzą one czytnika WCF. Zamiast tego tworzony jest czytelnik, który nie jest chroniony przez funkcje zabezpieczeń opisane w tej sekcji.

Limity przydziału czytelnika

Bezpieczne czytniki XML mają pięć konfigurowalnych przydziałów. Są one zwykle konfigurowane przy użyciu ReaderQuotas właściwości w elementach powiązania kodowania lub powiązaniach standardowych lub przy użyciu obiektu przekazanego XmlDictionaryReaderQuotas podczas tworzenia czytnika.

MaxBytesPerRead

Ten limit przydziału ogranicza liczbę bajtów odczytywanych w ramach jednej Read operacji podczas odczytywania tagu startowego elementu i jego atrybutów. (W przypadku nieprzesyłania strumieniowego sama nazwa elementu nie jest liczone względem limitu przydziału). MaxBytesPerRead jest ważne z następujących powodów:

  • Nazwa elementu i jego atrybuty są zawsze buforowane w pamięci podczas ich odczytywania. Dlatego ważne jest prawidłowe ustawienie tego limitu przydziału w trybie przesyłania strumieniowego, aby zapobiec nadmiernemu buforowaniu podczas przesyłania strumieniowego. Zobacz sekcję MaxDepth limitu przydziału, aby uzyskać informacje o rzeczywistej ilości buforowania, która ma miejsce.

  • Zbyt wiele atrybutów XML może używać nieproporcjonalnego czasu przetwarzania, ponieważ nazwy atrybutów muszą być sprawdzane pod kątem unikatowości. MaxBytesPerRead ogranicza to zagrożenie.

Maxdepth

Ten limit przydziału ogranicza maksymalną głębokość zagnieżdżania elementów XML. Na przykład dokument "<A><B><C/></B></A>" ma głębokość zagnieżdżania trzech. MaxDepth jest ważne z następujących powodów:

  • MaxDepth współdziała z MaxBytesPerRead: czytelnik zawsze przechowuje dane w pamięci dla bieżącego elementu i wszystkich jego elementów nadrzędnych, więc maksymalne zużycie pamięci czytnika jest proporcjonalne do produktu tych dwóch ustawień.

  • Podczas deserializacji głęboko zagnieżdżonego grafu obiektu deserializator jest zmuszony do uzyskania dostępu do całego stosu i zgłosić StackOverflowExceptionnieodwracalny element . Istnieje bezpośrednia korelacja między zagnieżdżaniem XML a zagnieżdżaniem obiektów zarówno dla obiektu , jak DataContractSerializer i XmlSerializer. Użyj polecenia MaxDepth , aby wyeliminować to zagrożenie.

MaxNameTableCharCount

Ten limit przydziału ogranicza rozmiar tabeli nazw czytelnika. Tabela nazw zawiera pewne ciągi (takie jak przestrzenie nazw i prefiksy), które są napotykane podczas przetwarzania dokumentu XML. Ponieważ te ciągi są buforowane w pamięci, ustaw ten limit przydziału, aby zapobiec nadmiernemu buforowaniu podczas przesyłania strumieniowego.

MaxStringContentLength

Ten limit przydziału ogranicza maksymalny rozmiar ciągu zwracany przez czytnik XML. Ten limit przydziału nie ogranicza zużycia pamięci w samym czytniku XML, ale w składniku korzystającym z czytnika. Na przykład gdy DataContractSerializer używa czytnika zabezpieczonego za pomocą MaxStringContentLengthprogramu , nie deserializuje ciągów większych niż ten limit przydziału. Jeśli używasz XmlDictionaryReader klasy bezpośrednio, nie wszystkie metody przestrzegają tego limitu przydziału, ale tylko metody, które zostały specjalnie zaprojektowane do odczytywania ciągów, takich jak ReadContentAsString metoda. Na Value właściwość czytelnika nie ma wpływu ten limit przydziału, dlatego nie należy jej używać, gdy ochrona zapewnia ten limit przydziału.

MaxArrayLength

Ten limit przydziału ogranicza maksymalny rozmiar tablicy elementów pierwotnych zwracanych przez czytnik XML, w tym tablic bajtów. Ten limit przydziału nie ogranicza zużycia pamięci w samym czytniku XML, ale w każdym składniku korzystającym z czytnika. Na przykład gdy DataContractSerializer używa czytnika zabezpieczonego za pomocą MaxArrayLengthprogramu , nie deserializuje tablic bajtów większych niż ten limit przydziału. Ważne jest, aby ustawić ten limit przydziału podczas próby połączenia modeli programowania przesyłania strumieniowego i buforowanego w ramach pojedynczego kontraktu. Należy pamiętać, że w przypadku bezpośredniego używania XmlDictionaryReader klasy tylko metody, które zostały specjalnie zaprojektowane do odczytywania tablic o dowolnym rozmiarze niektórych typów pierwotnych, takich jak ReadInt32Array, przestrzegają tego limitu przydziału.

Zagrożenia specyficzne dla kodowania binarnego

Kodowanie binarne XML WCF obsługuje funkcję ciągów słownika . Duży ciąg może być zakodowany przy użyciu tylko kilku bajtów. Umożliwia to znaczne zwiększenie wydajności, ale wprowadza nowe zagrożenia typu "odmowa usługi", które muszą zostać złagodzone.

Istnieją dwa rodzaje słowników: statyczne i dynamiczne. Słownik statyczny to wbudowana lista długich ciągów, które mogą być reprezentowane przy użyciu krótkiego kodu w kodowaniu binarnym. Ta lista ciągów jest stała po utworzeniu czytnika i nie można jej modyfikować. Żaden z ciągów w słowniku statycznym używanym domyślnie przez program WCF nie jest wystarczająco duży, aby stanowić poważne zagrożenie typu "odmowa usługi", chociaż nadal mogą być używane w ataku rozszerzenia słownika. W zaawansowanych scenariuszach, w których podajesz własny słownik statyczny, należy zachować ostrożność podczas wprowadzania dużych ciągów słownika.

Funkcja słowników dynamicznych umożliwia definiowanie własnych ciągów i kojarzenie ich z krótkimi kodami. Te mapowania ciąg-kod są przechowywane w pamięci podczas całej sesji komunikacji, tak aby kolejne komunikaty nie musiały ponownie wysłać ciągów i mogą korzystać z kodów, które są już zdefiniowane. Te ciągi mogą mieć dowolną długość i w związku z tym stanowią poważniejsze zagrożenie niż te w słowniku statycznym.

Pierwszym zagrożeniem, które należy złagodzić, jest możliwość, że słownik dynamiczny (tabela mapowania ciąg-kod) staje się zbyt duży. Ten słownik może zostać rozszerzony w ciągu kilku komunikatów, dlatego MaxReceivedMessageSize limit przydziału nie zapewnia ochrony, ponieważ dotyczy tylko każdego komunikatu oddzielnie. W związku z tym istnieje oddzielna MaxSessionSize właściwość BinaryMessageEncodingBindingElement , która ogranicza rozmiar słownika.

W przeciwieństwie do większości innych przydziałów ten limit przydziału ma zastosowanie również podczas pisania komunikatów. Jeśli podczas odczytywania komunikatu zostanie przekroczony, QuotaExceededException zostanie on zgłoszony jak zwykle. Jeśli podczas pisania komunikatu zostanie przekroczony, wszystkie ciągi, które powodują przekroczenie limitu przydziału, są zapisywane zgodnie z rzeczywistym użyciem funkcji słowników dynamicznych.

Zagrożenia rozszerzenia słownika

Znaczna klasa ataków specyficznych dla danych binarnych wynika z rozszerzenia słownika. Mała wiadomość w postaci binarnej może przekształcić się w bardzo duży komunikat w w pełni rozwiniętym formularzu tekstowym, jeśli korzysta z funkcji słowników ciągów. Współczynnik rozszerzania ciągów słownika dynamicznego jest ograniczony limitem przydziału MaxSessionSize , ponieważ żaden ciąg słownika dynamicznego nie przekracza maksymalnego rozmiaru całego słownika.

Właściwości MaxNameTableCharCount, MaxStringContentLengthi MaxArrayLength ograniczają tylko zużycie pamięci. Zwykle nie są one wymagane do wyeliminowania żadnych zagrożeń w użyciu bez strumienia, ponieważ użycie pamięci jest już ograniczone przez MaxReceivedMessageSizeprogram . Jednak MaxReceivedMessageSize zlicza wstępnie rozszerzane bajty. Gdy kodowanie binarne jest używane, użycie pamięci może potencjalnie wykraczać poza MaxReceivedMessageSizewartość , ograniczone tylko przez współczynnik MaxSessionSize. Z tego powodu ważne jest, aby zawsze ustawiać wszystkie limity przydziału czytelnika (zwłaszcza MaxStringContentLength) podczas korzystania z kodowania binarnego.

W przypadku używania kodowania binarnego razem z interfejsem DataContractSerializerIExtensibleDataObject interfejs może być niewłaściwie używany do instalowania ataku rozszerzenia słownika. Ten interfejs zasadniczo zapewnia nieograniczony magazyn dla dowolnych danych, które nie są częścią kontraktu. Jeśli limity przydziału nie mogą być ustawione na tyle niskie, że MaxSessionSize pomnożone przez MaxReceivedMessageSize nie stanowią problemu, wyłącz IExtensibleDataObject tę funkcję podczas korzystania z kodowania binarnego. IgnoreExtensionDataObject Ustaw właściwość na true wartość w atrybucie ServiceBehaviorAttribute . Alternatywnie nie implementuj interfejsu IExtensibleDataObject . Aby uzyskać więcej informacji, zobacz Kontrakty danych zgodne z przekazywaniem.

Podsumowanie limitów przydziału

Poniższa tabela zawiera podsumowanie wskazówek dotyczących limitów przydziałów.

Stan Ważne przydziały do ustawienia
Brak przesyłania strumieniowego ani przesyłania strumieniowego małych komunikatów, tekstu ani kodowania MTOM MaxReceivedMessageSize, MaxBytesPerReadi MaxDepth
Brak przesyłania strumieniowego ani przesyłania strumieniowego małych komunikatów, kodowanie binarne MaxReceivedMessageSize, MaxSessionSizei wszystkie ReaderQuotas
Przesyłanie strumieniowe dużych komunikatów, tekstu lub kodowania MTOM MaxBufferSize i wszystkie ReaderQuotas
Przesyłanie strumieniowe dużych komunikatów, kodowanie binarne MaxBufferSize, MaxSessionSizei wszystkie ReaderQuotas
  • Limity czasu na poziomie transportu należy zawsze ustawiać i nigdy nie używać synchronicznych odczytów/zapisów, gdy przesyłanie strumieniowe jest używane, niezależnie od tego, czy przesyłasz strumieniowo duże lub małe komunikaty.

  • W razie wątpliwości co do limitu przydziału ustaw ją na bezpieczną wartość, a nie pozostawiając ją otwartą.

Zapobieganie złośliwemu wykonaniu kodu

Następujące ogólne klasy zagrożeń mogą wykonywać kod i mieć niezamierzone skutki:

  • Deserializator ładuje złośliwy, niebezpieczny lub poufny typ.

  • Komunikat przychodzący powoduje, że deserializator tworzy wystąpienie zwykle bezpiecznego typu w taki sposób, że ma niezamierzone konsekwencje.

W poniższych sekcjach omówiono te klasy zagrożeń.

DataContractSerializer

(Aby uzyskać informacje o zabezpieczeniach w witrynie XmlSerializer, zobacz odpowiednią dokumentację). Model zabezpieczeń dla elementu XmlSerializer jest podobny do DataContractSerializermodelu , i różni się głównie w szczegółach. Na przykład XmlIncludeAttribute atrybut jest używany do dołączania typów zamiast atrybutu KnownTypeAttribute . Jednak niektóre zagrożenia unikatowe dla elementu zostały omówione w dalszej części tego tematu XmlSerializer .

Zapobieganie ładowaniu niezamierzonych typów

Ładowanie niezamierzonych typów może mieć znaczące konsekwencje, niezależnie od tego, czy typ jest złośliwy, czy po prostu ma skutki uboczne wrażliwe na zabezpieczenia. Typ może zawierać lukę w zabezpieczeniach umożliwiającą wykorzystanie, wykonywać akcje wrażliwe na zabezpieczenia w konstruktorze lub konstruktorze klasy, mieć duży ślad pamięci, który ułatwia ataki typu "odmowa usługi" lub może zgłaszać wyjątki, które nie można odzyskać. Typy mogą mieć konstruktory klas uruchamiane natychmiast po załadowaniu typu i przed utworzeniem jakichkolwiek wystąpień. Z tych powodów ważne jest, aby kontrolować zestaw typów, które może załadować deserializator.

DataContractSerializer Deserializuje się w luźno sprzężony sposób. Nigdy nie odczytuje typów i nazw zestawów środowiska uruchomieniowego języka wspólnego (CLR) z danych przychodzących. Jest to podobne do zachowania XmlSerializerelementu , ale różni się od zachowania NetDataContractSerializerelementu , BinaryFormatteri SoapFormatter. Luźne sprzężenie wprowadza stopień bezpieczeństwa, ponieważ zdalny atakujący nie może wskazać dowolnego typu do załadowania tylko przez nadanie tego typu nazwy w komunikacie.

Parametr DataContractSerializer jest zawsze dozwolony do załadowania typu, który jest obecnie oczekiwany zgodnie z umową. Jeśli na przykład kontrakt danych zawiera element członkowski danych typu Customer, DataContractSerializer może załadować Customer typ podczas deserializacji tego elementu członkowskiego danych.

DataContractSerializer Ponadto funkcja obsługuje polimorfizm. Element członkowski danych może być zadeklarowany jako Object, ale dane przychodzące mogą zawierać Customer wystąpienie. Jest to możliwe tylko wtedy, gdy Customer typ został "znany" deserializatorowi za pomocą jednego z następujących mechanizmów:

  • KnownTypeAttribute atrybut zastosowany do typu.

  • KnownTypeAttribute atrybut określający metodę zwracającą listę typów.

  • Atrybut ServiceKnownTypeAttribute.

  • Sekcja KnownTypes konfiguracji.

  • Lista znanych typów jawnie przekazana do DataContractSerializer konstrukcji, jeśli używasz serializatora bezpośrednio.

Każdy z tych mechanizmów zwiększa obszar powierzchni, wprowadzając więcej typów, które deserializator może załadować. Kontroluj każdy z tych mechanizmów, aby upewnić się, że do listy znanych typów nie są dodawane żadne złośliwe lub niezamierzone typy.

Gdy znany typ znajduje się w zakresie, można go załadować w dowolnym momencie, a wystąpienia typu można utworzyć, nawet jeśli kontrakt faktycznie go używa. Załóżmy na przykład, że typ "MyDangerousType" jest dodawany do znanej listy typów przy użyciu jednego z powyższych mechanizmów. To oznacza, że:

  • MyDangerousType jest ładowany, a jego konstruktor klasy jest uruchamiany.

  • Nawet w przypadku deserializacji kontraktu danych z elementem członkowskim danych ciągu złośliwy komunikat może nadal powodować utworzenie wystąpienia MyDangerousType . Kod w programie MyDangerousType, taki jak moduły ustawiania właściwości, mogą być uruchamiane. Po wykonaniu tej czynności deserializator próbuje przypisać to wystąpienie do elementu członkowskiego danych ciągu i zakończyć się niepowodzeniem z wyjątkiem.

Podczas pisania metody zwracającej listę znanych typów lub przekazując listę bezpośrednio do DataContractSerializer konstruktora, upewnij się, że kod, który przygotowuje listę, jest bezpieczny i działa tylko na zaufanych danych.

Jeśli określono znane typy w konfiguracji, upewnij się, że plik konfiguracji jest bezpieczny. Zawsze używaj silnych nazw w konfiguracji (określając klucz publiczny podpisanego zestawu, w którym znajduje się typ), ale nie określaj wersji typu do załadowania. Moduł ładujący typu automatycznie wybiera najnowszą wersję, jeśli jest to możliwe. Jeśli określisz określoną wersję w konfiguracji, uruchomisz następujące ryzyko: Typ może mieć lukę w zabezpieczeniach, która może zostać naprawiona w przyszłej wersji, ale wersja podatna na zagrożenia nadal jest ładowana, ponieważ jest jawnie określona w konfiguracji.

Posiadanie zbyt wielu znanych typów ma kolejną konsekwencję: DataContractSerializer Tworzy pamięć podręczną kodu serializacji/deserializacji w domenie aplikacji z wpisem dla każdego typu, który musi serializować i deserializować. Ta pamięć podręczna nigdy nie jest czyszczone tak długo, jak domena aplikacji jest uruchomiona. W związku z tym osoba atakująca, która zdaje sobie sprawę, że aplikacja używa wielu znanych typów, może spowodować deserializacji wszystkich tych typów, co powoduje, że pamięć podręczna zużywa nieproporcjonalnie dużą ilość pamięci.

Zapobieganie wystąpieniu typów w stanie niezamierzonym

Typ może mieć ograniczenia spójności wewnętrznej, które muszą być wymuszane. Należy zachować ostrożność, aby uniknąć łamania tych ograniczeń podczas deserializacji.

Poniższy przykład typu reprezentuje stan blokady powietrza na statku kosmicznym i wymusza ograniczenie, że zarówno wewnętrzne, jak i zewnętrzne drzwi nie mogą być otwarte w tym samym czasie.

[DataContract]
public class SpaceStationAirlock
{
    [DataMember]
    private bool innerDoorOpenValue = false;
    [DataMember]
    private bool outerDoorOpenValue = false;

    public bool InnerDoorOpen
    {
        get { return innerDoorOpenValue; }
        set
        {
            if (value & outerDoorOpenValue)
                throw new Exception("Cannot open both doors!");
            else innerDoorOpenValue = value;
        }
    }
    public bool OuterDoorOpen
    {
        get { return outerDoorOpenValue; }
        set
        {
            if (value & innerDoorOpenValue)
                throw new Exception("Cannot open both doors!");
            else outerDoorOpenValue = value;
        }
    }
}
<DataContract()> _
Public Class SpaceStationAirlock
    <DataMember()> Private innerDoorOpenValue As Boolean = False
    <DataMember()> Private outerDoorOpenValue As Boolean = False

    Public Property InnerDoorOpen() As Boolean
        Get

            Return innerDoorOpenValue
        End Get
        Set(ByVal value As Boolean)
            If (value & outerDoorOpenValue) Then
                Throw New Exception("Cannot open both doors!")
            Else
                innerDoorOpenValue = value
            End If
        End Set
    End Property

    Public Property OuterDoorOpen() As Boolean
        Get
            Return outerDoorOpenValue
        End Get
        Set(ByVal value As Boolean)
            If (value & innerDoorOpenValue) Then
                Throw New Exception("Cannot open both doors!")
            Else
                outerDoorOpenValue = value
            End If
        End Set
    End Property
End Class

Osoba atakująca może wysłać w ten sposób złośliwy komunikat, obejście ograniczeń i przejście obiektu do nieprawidłowego stanu, co może mieć niezamierzone i nieprzewidywalne konsekwencje.

<SpaceStationAirlock>
    <innerDoorOpen>true</innerDoorOpen>
    <outerDoorOpen>true</outerDoorOpen>
</SpaceStationAirlock>

Tę sytuację można uniknąć, zdając sobie sprawę z następujących kwestii:

  • DataContractSerializer Gdy deserializuje większość klas, konstruktory nie są uruchamiane. W związku z tym nie należy polegać na żadnym zarządzaniu stanem wykonywanym w konstruktorze.

  • Użyj wywołań zwrotnych, aby upewnić się, że obiekt jest w prawidłowym stanie. Wywołanie zwrotne oznaczone atrybutem OnDeserializedAttribute jest szczególnie przydatne, ponieważ jest uruchamiane po zakończeniu deserializacji i ma szansę sprawdzić i poprawić ogólny stan. Aby uzyskać więcej informacji, zobacz Wywołania zwrotne serializacji odporne na wersje.

  • Nie projektuj typów kontraktów danych, aby polegać na żadnej konkretnej kolejności, w której należy wywołać metody ustawiania właściwości.

  • Zachowaj ostrożność przy użyciu starszych typów oznaczonych atrybutem SerializableAttribute . Wiele z nich zostało zaprojektowanych do pracy z komunikacji wirtualnej programu .NET Framework do użytku tylko z zaufanymi danymi. Istniejące typy oznaczone tym atrybutem mogły nie zostać zaprojektowane z myślą o bezpieczeństwie stanu.

  • Nie należy polegać na IsRequired właściwości atrybutu DataMemberAttribute , aby zagwarantować obecność danych w zakresie bezpieczeństwa państwa. Dane mogą zawsze mieć nullwartość , zerolub invalid.

  • Nigdy nie ufaj grafowi obiektu deserializowanemu z niezaufanego źródła danych bez uprzedniego sprawdzania poprawności. Każdy pojedynczy obiekt może być w stanie spójnym, ale graf obiektu jako całość może nie być. Ponadto nawet jeśli tryb zachowywania grafu obiektów jest wyłączony, deserializowany graf może mieć wiele odwołań do tego samego obiektu lub mieć odwołania cykliczne. Aby uzyskać więcej informacji, zobacz Serializacja i Deserializacja.

Bezpieczne używanie narzędzia NetDataContractSerializer

Jest NetDataContractSerializer to aparat serializacji, który wykorzystuje ścisłe sprzęganie do typów. Jest to podobne do i BinaryFormatterSoapFormatter. Oznacza to, że określa typ do utworzenia wystąpienia, odczytując zestaw .NET Framework i nazwę typu z danych przychodzących. Chociaż jest to część programu WCF, nie ma dostarczonego sposobu podłączania tego aparatu serializacji; kod niestandardowy musi być napisany. Jest NetDataContractSerializer on dostarczany głównie w celu ułatwienia migracji z komunikacji zdalnie programu .NET Framework do programu WCF. Aby uzyskać więcej informacji, zobacz odpowiednią sekcję w temacie Serialization and Deserialization (Serializacja i deserializacja).

Ponieważ sam komunikat może wskazywać, że można załadować dowolny typ, NetDataContractSerializer mechanizm jest z natury niezabezpieczony i powinien być używany tylko z zaufanymi danymi. Aby uzyskać więcej informacji, zobacz Przewodnik po zabezpieczeniach BinaryFormatter.

Nawet w przypadku użycia z zaufanymi danymi dane przychodzące mogą niewystarczająco określać typ do załadowania, zwłaszcza jeśli właściwość jest ustawiona AssemblyFormat na Simplewartość . Każda osoba mająca dostęp do katalogu aplikacji lub globalnej pamięci podręcznej zestawów może zastąpić złośliwy typ zamiast tego, który ma zostać załadowany. Zawsze upewnij się, że bezpieczeństwo katalogu aplikacji i globalnej pamięci podręcznej zestawów jest prawidłowe, ustawiając odpowiednie uprawnienia.

Ogólnie rzecz biorąc, jeśli zezwalasz na częściowo zaufany dostęp kodu do NetDataContractSerializer wystąpienia lub w inny sposób kontrolujesz selektor zastępczy (ISurrogateSelector) lub binder serializacji (SerializationBinder), kod może mieć dużą kontrolę nad procesem serializacji/deserializacji. Może na przykład wstrzyknąć dowolne typy, prowadzić do ujawnienia informacji, naruszenia wynikowego grafu obiektu lub serializacji danych albo przepełnić wynikowy serializowany strumień.

Innym problemem z zabezpieczeniami NetDataContractSerializer jest odmowa usługi, a nie złośliwe zagrożenie związane z wykonywaniem kodu. W przypadku korzystania z elementu NetDataContractSerializernależy zawsze ustawić limit przydziału MaxItemsInObjectGraph na bezpieczną wartość. Łatwo jest utworzyć mały złośliwy komunikat, który przydziela tablicę obiektów, których rozmiar jest ograniczony tylko przez ten limit przydziału.

Zagrożenia specyficzne dla elementu XmlSerializer

Model XmlSerializer zabezpieczeń jest podobny do DataContractSerializermodelu . Jednak kilka zagrożeń jest unikatowych dla elementu XmlSerializer.

Generuje XmlSerializer zestawy serializacji w czasie wykonywania, które zawierają kod, który faktycznie serializuje i deserializuje; te zestawy są tworzone w katalogu plików tymczasowych. Jeśli jakiś inny proces lub użytkownik ma prawa dostępu do tego katalogu, może zastąpić kod serializacji/deserializacji z dowolnym kodem. Następnie XmlSerializer uruchamia ten kod przy użyciu kontekstu zabezpieczeń zamiast kodu serializacji/deserializacji. Upewnij się, że uprawnienia są poprawnie ustawione w katalogu plików tymczasowych, aby temu zapobiec.

Obiekt XmlSerializer ma również tryb, w którym używa wstępnie wygenerowanych zestawów serializacji zamiast generowania ich w czasie wykonywania. Ten tryb jest wyzwalany za każdym razem, gdy XmlSerializer będzie można znaleźć odpowiedni zestaw serializacji. Sprawdza, XmlSerializer czy zestaw serializacji został podpisany przez ten sam klucz, który został użyty do podpisania zestawu zawierającego typy serializowane. Zapewnia to ochronę przed złośliwymi zestawami, które są przebrane za zestawy serializacji. Jeśli jednak zestaw zawierający typy możliwe do serializacji nie jest podpisany, XmlSerializer nie można wykonać tego sprawdzania i używa żadnego zestawu z poprawną nazwą. Dzięki temu można uruchomić złośliwy kod. Zawsze podpisz zestawy zawierające typy możliwe do serializacji lub ściśle kontroluj dostęp do katalogu aplikacji i globalnej pamięci podręcznej zestawów, aby zapobiec wprowadzeniu złośliwych zestawów.

Obiekt XmlSerializer może podlegać atakowi typu "odmowa usługi". Obiekt XmlSerializer nie ma MaxItemsInObjectGraph limitu przydziału (jak jest dostępny w obiekcie DataContractSerializer). W związku z tym deserializuje dowolną ilość obiektów, ograniczoną tylko przez rozmiar komunikatu.

Zagrożenia częściowego zaufania

Zwróć uwagę na następujące obawy dotyczące zagrożeń związanych z kodem uruchomionym z częściowym zaufaniem. Te zagrożenia obejmują złośliwy częściowo zaufany kod, a także złośliwy częściowo zaufany kod w połączeniu z innymi scenariuszami ataku (na przykład częściowo zaufany kod, który tworzy określony ciąg, a następnie deserializuje go).

  • W przypadku używania jakichkolwiek składników serializacji nigdy nie dochodzi do żadnych uprawnień przed takim użyciem, nawet jeśli cały scenariusz serializacji mieści się w zakresie asercyjności i nie masz do czynienia z żadnymi niezaufanymi danymi ani obiektami. Takie użycie może prowadzić do luk w zabezpieczeniach.

  • W przypadkach, gdy częściowo zaufany kod ma kontrolę nad procesem serializacji, za pośrednictwem punktów rozszerzalności (zastępczych), typów serializowanych lub za pośrednictwem innych środków, częściowo zaufany kod może spowodować, że serializator wyprowadzi dużą ilość danych do serializowanego strumienia, co może spowodować odmowę usługi (DoS) odbiorcy tego strumienia. W przypadku serializacji danych przeznaczonych dla elementu docelowego, który jest poufny dla zagrożeń systemu DoS, nie serializuj częściowo zaufanych typów ani w inny sposób nie zezwalaj na serializacji częściowo zaufanej kontroli kodu.

  • Jeśli zezwolisz na częściowo zaufany dostęp kodu do DataContractSerializer wystąpienia lub w inny sposób kontrolujesz zastępcze kontrakty danych, może to mieć dużą kontrolę nad procesem serializacji/deserializacji. Może na przykład wstrzyknąć dowolne typy, prowadzić do ujawnienia informacji, naruszenia wynikowego grafu obiektu lub serializacji danych albo przepełnić wynikowy serializowany strumień. NetDataContractSerializer Równoważne zagrożenie zostało opisane w sekcji "Using the NetDataContractSerializer Securely" (Używanie bezpiecznego narzędzia NetDataContractSerializer).

  • DataContractAttribute Jeśli atrybut jest stosowany do typu (lub typu oznaczonego jako SerializableAttribute , ale nieISerializable), deserializator może utworzyć wystąpienie takiego typu, nawet jeśli wszystkie konstruktory nie są publiczne lub chronione przez żądania.

  • Nigdy nie ufaj wynikowi deserializacji, chyba że dane do deserializacji są zaufane i masz pewność, że wszystkie znane typy są zaufanymi typami. Należy pamiętać, że znane typy nie są ładowane z pliku konfiguracji aplikacji (ale są ładowane z pliku konfiguracji komputera) podczas uruchamiania w częściowym zaufaniu.

  • Jeśli przekażesz DataContractSerializer wystąpienie z zastępczym dodanym do częściowo zaufanego kodu, kod może zmienić wszelkie ustawienia modyfikowalne dla tego zastępczego.

  • W przypadku obiektu deserializacji, jeśli czytnik XML (lub tam dane) pochodzi z częściowo zaufanego kodu, traktuj wynikowy deserializowany obiekt jako niezaufane dane.

  • Fakt, że ExtensionDataObject typ nie ma publicznych elementów członkowskich, nie oznacza, że dane w nim są bezpieczne. Jeśli na przykład deserializujesz z uprzywilejowanego źródła danych do obiektu, w którym znajdują się niektóre dane, przekaż ten obiekt do częściowo zaufanego kodu, częściowo zaufany kod może odczytać dane w ExtensionDataObject obiekcie, serializując obiekt. Rozważ ustawienie wartości IgnoreExtensionDataObject w true przypadku deserializacji z uprzywilejowanego źródła danych do obiektu, który jest później przekazywany do częściowo zaufanego kodu.

  • DataContractSerializer obsługa DataContractJsonSerializer serializacji prywatnych, chronionych, wewnętrznych i publicznych członków w pełnym zaufaniu. Jednak w przypadku częściowego zaufania tylko publiczne elementy członkowskie mogą być serializowane. Obiekt SecurityException jest zgłaszany, jeśli aplikacja próbuje serializować element członkowski inny niż publiczny.

    Aby zezwolić wewnętrznym lub chronionym członkom wewnętrznym na serializacji w częściowym zaufaniu, użyj atrybutu InternalsVisibleToAttribute zestawu. Ten atrybut umożliwia zestawowi zadeklarowanie, że jego wewnętrzne elementy członkowskie są widoczne dla innego zestawu. W takim przypadku zestaw, który chce mieć jego wewnętrzne elementy członkowskie serializowane, deklaruje, że jego wewnętrzne elementy członkowskie są widoczne dla System.Runtime.Serialization.dll.

    Zaletą tego podejścia jest to, że nie wymaga ścieżki generowania kodu z podwyższonym poziomem uprawnień.

    Jednocześnie istnieją dwie główne wady.

    Pierwszą wadą jest to, że właściwość opt-in atrybutu InternalsVisibleToAttribute jest szeroko ustawiona. Oznacza to, że nie można określić, że tylko określona klasa może mieć jego wewnętrzne składowe serializowane. Oczywiście nadal można zdecydować, aby nie serializować określonego elementu członkowskiego wewnętrznego, po prostu nie dodając DataMemberAttribute atrybutu do tego elementu członkowskiego. Podobnie deweloper może również zdecydować się na utworzenie członka wewnętrznego, a nie prywatnego lub chronionego, z niewielkimi obawami dotyczącymi widoczności.

    Druga wadą jest to, że nadal nie obsługuje prywatnych ani chronionych członków.

    Aby zilustrować użycie atrybutu InternalsVisibleToAttribute w częściowym zaufaniu, rozważ następujący program:

        public class Program
        {
            public static void Main(string[] args)
            {
                try
                {
    //              PermissionsHelper.InternetZone corresponds to the PermissionSet for partial trust.
    //              PermissionsHelper.InternetZone.PermitOnly();
                    MemoryStream memoryStream = new MemoryStream();
                    new DataContractSerializer(typeof(DataNode)).
                        WriteObject(memoryStream, new DataNode());
                }
                finally
                {
                    CodeAccessPermission.RevertPermitOnly();
                }
            }
    
            [DataContract]
            public class DataNode
            {
                [DataMember]
                internal string Value = "Default";
            }
        }
    

    W powyższym PermissionsHelper.InternetZone przykładzie odpowiada PermissionSet wartości dla częściowego zaufania. Teraz bez atrybutu InternalsVisibleToAttribute aplikacja zakończy się niepowodzeniem, zgłaszając informację SecurityException , że niepubliczni członkowie nie mogą być serializowane w częściowym zaufaniu.

    Jeśli jednak dodamy następujący wiersz do pliku źródłowego, program zostanie uruchomiony pomyślnie.

    [assembly:System.Runtime.CompilerServices.InternalsVisibleTo("System.Runtime.Serialization, PublicKey = 00000000000000000400000000000000")]
    

Inne problemy związane z zarządzaniem stanem

Warto wspomnieć o kilku innych problemach dotyczących zarządzania stanem obiektu:

  • W przypadku korzystania z modelu programowania opartego na strumieniu z transportem strumieniowym przetwarzanie komunikatu następuje po nadejściu komunikatu. Nadawca wiadomości może przerwać operację wysyłania w środku strumienia, pozostawiając kod w nieprzewidywalnym stanie, jeśli oczekiwano większej ilości zawartości. Ogólnie rzecz biorąc, nie polegaj na zakończeniu przesyłania strumieniowego i nie wykonuj żadnej pracy w operacji opartej na strumieniu, której nie można wycofać w przypadku przerwania strumienia. Dotyczy to również sytuacji, w której komunikat może być źle sformułowany po treści przesyłania strumieniowego (na przykład może brakować tagu końcowego dla koperty protokołu SOAP lub może mieć drugą treść komunikatu).

  • IExtensibleDataObject Użycie tej funkcji może spowodować emitowane poufne dane. Jeśli akceptujesz dane z niezaufanego źródła do kontraktów danych, IExtensibleObjectData a później ponownie emitujesz je w bezpiecznym kanale, w którym są podpisane komunikaty, potencjalnie ręczysz za dane, o których nic nie wiesz. Ponadto ogólny stan, który wysyłasz, może być nieprawidłowy, jeśli uwzględnisz zarówno znane, jak i nieznane fragmenty danych. Unikaj tej sytuacji przez selektywne ustawienie właściwości danych rozszerzenia na null lub przez selektywne wyłączenie IExtensibleObjectData funkcji.

Importowanie schematu

Zwykle proces importowania schematu do generowania typów odbywa się tylko w czasie projektowania, na przykład w przypadku używania narzędzia ServiceModel Metadata Tool (Svcutil.exe) w usłudze sieci Web w celu wygenerowania klasy klienta. Jednak w bardziej zaawansowanych scenariuszach schemat można przetwarzać w czasie wykonywania. Należy pamiętać, że może to narazić Cię na ryzyko odmowy usługi. Importowanie niektórych schematów może zająć dużo czasu. Nigdy nie używaj XmlSerializer składnika importu schematu w takich scenariuszach, jeśli schematy mogą pochodzić z niezaufanego źródła.

Zagrożenia specyficzne dla integracji ASP.NET AJAX

Gdy użytkownik implementuje WebScriptEnablingBehavior program lub WebHttpBehavior, program WCF uwidacznia punkt końcowy, który może akceptować komunikaty XML i JSON. Istnieje jednak tylko jeden zestaw przydziałów czytników, używany zarówno przez czytnik XML, jak i czytnik JSON. Niektóre ustawienia limitu przydziału mogą być odpowiednie dla jednego czytnika, ale zbyt duże dla drugiego.

Podczas implementowania WebScriptEnablingBehaviorużytkownik ma możliwość uwidocznienia serwera proxy języka JavaScript w punkcie końcowym. Należy rozważyć następujące problemy z zabezpieczeniami:

  • Informacje o usłudze (nazwy operacji, nazwy parametrów itd.) można uzyskać, sprawdzając serwer proxy języka JavaScript.

  • W przypadku korzystania z punktu końcowego języka JavaScript poufne i prywatne informacje mogą być przechowywane w pamięci podręcznej przeglądarki internetowej klienta.

Uwaga dotycząca składników

WCF to elastyczny i dostosowywalny system. Większość treści tego tematu koncentruje się na najbardziej typowych scenariuszach użycia WCF. Można jednak tworzyć składniki WCF na wiele różnych sposobów. Ważne jest, aby zrozumieć implikacje zabezpieczeń związane z używaniem poszczególnych składników. W szczególności:

  • Jeśli musisz używać czytników XML, użyj czytników XmlDictionaryReader , które klasa udostępnia, w przeciwieństwie do innych czytników. Sejf czytniki są tworzone przy użyciu CreateTextReadermetod , CreateBinaryReaderlub CreateMtomReader . Nie używaj Create metody . Zawsze konfiguruj czytelników przy użyciu bezpiecznych przydziałów. Aparaty serializacji w programie WCF są bezpieczne tylko wtedy, gdy są używane z bezpiecznymi czytnikami XML z programu WCF.

  • W przypadku używania DataContractSerializer elementu do deserializacji potencjalnie niezaufanych danych zawsze ustawiaj MaxItemsInObjectGraph właściwość .

  • Podczas tworzenia komunikatu ustaw parametr , jeśli MaxReceivedMessageSize nie zapewnia wystarczającej maxSizeOfHeaders ochrony.

  • Podczas tworzenia kodera zawsze należy skonfigurować odpowiednie przydziały, takie jak MaxSessionSize i MaxBufferSize.

  • W przypadku korzystania z filtru komunikatów XPath ustaw wartość NodeQuota , aby ograniczyć liczbę węzłów XML wizyt filtru. Nie używaj wyrażeń XPath, które mogą zająć dużo czasu do obliczenia bez odwiedzania wielu węzłów.

  • Ogólnie rzecz biorąc, w przypadku korzystania z dowolnego składnika, który akceptuje limit przydziału, należy zrozumieć jego wpływ na bezpieczeństwo i ustawić go na bezpieczną wartość.

Zobacz też