Freigeben über


Sicherheitsüberlegungen für Daten

Beim Umgang mit Daten in Windows Communication Foundation (WCF) müssen Sie eine Reihe von Bedrohungskategorien berücksichtigen. In der folgenden Liste sind die wichtigsten Bedrohungsklassen aufgeführt, die sich auf die Datenverarbeitung beziehen. WCF stellt Tools bereit, um diese Bedrohungen zu mindern.

  • Denial of Service

    Wenn sie nicht vertrauenswürdige Daten empfangen, können die empfangenden Daten dazu führen, dass die empfangende Seite auf eine unverhältnismäßige Menge verschiedener Ressourcen zugreift, z. B. Arbeitsspeicher, Threads, verfügbare Verbindungen oder Prozessorzyklen, indem lange Berechnungen verursacht werden. Ein Denial-of-Service-Angriff auf einen Server kann dazu führen, dass er abstürzt und keine Nachrichten von anderen, legitimen Clients verarbeiten kann.

  • Ausführung bösartiger Code

    Eingehende nicht vertrauenswürdige Daten führen dazu, dass der empfangende Code ausgeführt wird, den er nicht beabsichtigt hat.

  • Veröffentlichung von Informationen

    Der Remoteangreifer erzwingt die empfangende Partei, auf seine Anfragen so zu reagieren, dass mehr Informationen offengelegt werden, als er beabsichtigt.

User-Provided Code- und Codezugriffssicherheit

Eine Reihe von Stellen in der Windows Communication Foundation (WCF)-Infrastruktur führen Code aus, der vom Benutzer bereitgestellt wird. Die Serialisierungs-Engine DataContractSerializer z. B. kann vom Benutzer bereitgestellte set - und get -Eigenschaftenaccessoren aufrufen. Die WCF-Kanalinfrastruktur kann auch von Benutzern bereitgestellte abgeleitete Klassen der Message Klasse aufrufen.

Es liegt in der Verantwortung des Codeautors, sicherzustellen, dass keine Sicherheitsrisiken vorhanden sind. Wenn Sie z. B. einen Datenvertragstyp mit einer Data Member-Eigenschaft vom Typ "integer" erstellen und in der set Accessorimplementierung ein Array basierend auf dem Eigenschaftswert zuweisen, machen Sie die Möglichkeit eines Denial-of-Service-Angriffs verfügbar, wenn eine schädliche Nachricht einen extrem großen Wert für dieses Datenmemm enthält. Vermeiden Sie im Allgemeinen alle Zuordnungen basierend auf eingehenden Daten oder einer langen Verarbeitung im vom Benutzer bereitgestellten Code (insbesondere, wenn eine lange Verarbeitung durch eine kleine Menge eingehender Daten verursacht werden kann). Achten Sie bei der Sicherheitsanalyse von vom Benutzer bereitgestellten Code darauf, auch alle Fehlerfälle zu berücksichtigen (d. a. alle Codeverzweigungen, bei denen Ausnahmen ausgelöst werden).

Das ultimative Beispiel für vom Benutzer bereitgestellten Code ist der Code in Ihrer Dienstimplementierung für jeden Vorgang. Die Sicherheit Ihrer Dienstimplementierung liegt in Ihrer Verantwortung. Es ist einfach, versehentlich unsichere Vorgangsimplementierungen zu erstellen, die zu Denial-of-Service-Sicherheitsrisiken führen können. Ein Vorgang, der eine Zeichenfolge verwendet und die Liste der Kunden aus einer Datenbank zurückgibt, deren Name mit dieser Zeichenfolge beginnt. Wenn Sie mit einer großen Datenbank arbeiten und die übergebene Zeichenfolge nur ein einzelner Buchstabe ist, versucht Ihr Code möglicherweise, eine Nachricht zu erstellen, die größer als alle verfügbaren Arbeitsspeicher ist, was dazu führt, dass der gesamte Dienst fehlschlägt. (Ein OutOfMemoryException kann im .NET Framework nicht wiederhergestellt werden und führt immer zur Beendigung Ihrer Anwendung.)

Sie sollten sicherstellen, dass kein bösartiger Code an die verschiedenen Erweiterbarkeitspunkte angeschlossen ist. Dies ist besonders dann relevant, wenn er unter teilweiser Vertrauenswürdigkeit ausgeführt wird, und mit Typen arbeitet, die in nicht voll vertrauenswürdigen Assemblys deklariert sind, oder Komponenten erstellt, die von teilweise vertrauenswürdigem Code verwendet werden. Weitere Informationen finden Sie unter "Partielle Vertrauensbedrohungen" in einem späteren Abschnitt.

Beachten Sie, dass bei Ausführung unter teilweiser Vertrauenswürdigkeit die Infrastruktur der Datenvertragsserialisierung nur eine beschränkte Teilmenge des Datenvertragsprogrammiermodells unterstützt – beispielsweise werden private Datenmember oder Typen, die das SerializableAttribute -Attribut verwenden, nicht unterstützt. Weitere Informationen finden Sie unter Teilweises Vertrauen.

Hinweis

Code Access Security (CAS) ist in allen Versionen von .NET Framework und .NET veraltet. Aktuelle Versionen von .NET berücksichtigen keine CAS-Anmerkungen und erzeugen Fehler, wenn CAS-bezogene APIs verwendet werden. Entwickler sollten alternative Mittel zum Ausführen von Sicherheitsaufgaben suchen.

Vermeiden unbeabsichtigter Offenlegung von Informationen

Beim Entwerfen serialisierbarer Typen unter Berücksichtigung der Sicherheit ist die Offenlegung von Informationen eine mögliche Sorge.

Berücksichtigen Sie die folgenden Punkte:

  • Das DataContractSerializer Programmiermodell ermöglicht die Darstellung privater und interner Daten außerhalb des Typs oder der Assembly während der Serialisierung. Darüber hinaus kann die Form eines Typs während des Schemaexports verfügbar gemacht werden. Achten Sie darauf, die Serialisierungsprojektion Ihres Typs zu verstehen. Wenn Sie nichts verfügbar machen möchten, deaktivieren Sie die Serialisierung (z. B. indem Sie das DataMemberAttribute Attribut im Falle eines Datenvertrags nicht anwenden).

  • Beachten Sie, dass derselbe Typ je nach verwendetem Serializer mehrere Serialisierungsansichten aufweisen kann. Derselbe Typ kann einen Satz von Daten verfügbar machen, wenn er mit dem DataContractSerializer und einem anderen Satz von Daten verwendet wird, wenn er mit dem XmlSerializer. Versehentliches Verwenden des falschen Serialisierers kann zur Offenlegung von Informationen führen.

  • Wenn Sie XmlSerializer im RPC/encoded-Modus des Legacy-Remoteprozeduraufrufs verwenden, legen Sie vielleicht unbeabsichtigt die Form des Objektdiagramms der Absenderseite für die Empfängerseite offen.

Verhindern von Denial-of-Service-Angriffen

Kontingente

Die Ursache dafür, dass die empfangende Seite einen erheblichen Arbeitsspeicher zuweist, ist ein potenzieller Denial-of-Service-Angriff. Dieser Abschnitt konzentriert sich zwar auf Probleme beim Arbeitsspeicherverbrauch, die sich aus großen Nachrichten ergeben, andere Angriffe können jedoch auftreten. Beispielsweise können Nachrichten eine unverhältnismäßige Verarbeitungszeit verwenden.

Denial-of-Service-Angriffe werden in der Regel mithilfe von Kontingenten abgemildert. Wenn ein Kontingent überschritten wird, wird normalerweise eine QuotaExceededException Ausnahme ausgelöst. Ohne das Kontingent kann eine schädliche Nachricht dazu führen, dass auf den gesamten verfügbaren Speicher zugegriffen wird, was eine OutOfMemoryException Ausnahme verursacht, oder dass alle verfügbaren Stapelspeicher genutzt werden, was zu einem StackOverflowException führt.

Das Szenario 'Kontingent überschritten' ist wiederherstellbar; wenn es in einem laufenden Dienst auftritt, wird die gerade verarbeitete Nachricht automatisch verworfen, und der Dienst läuft weiter und verarbeitet weitere Nachrichten. Die Out-of-Memory- und Stack-Overflow-Szenarien können jedoch nicht im gesamten .NET-Framework behoben werden; der Dienst beendet sich, wenn solche Ausnahmen auftreten.

Kontingente in WCF umfassen keine Vorzuweisung. Wenn beispielsweise das MaxReceivedMessageSize Kontingent (in verschiedenen Klassen gefunden) auf 128 KB festgelegt ist, bedeutet dies nicht, dass für jede Nachricht automatisch 128 KB zugewiesen werden. Der tatsächliche zugewiesene Betrag hängt von der tatsächlichen Größe der eingehenden Nachricht ab.

Viele Kontingente sind auf der Transportebene verfügbar. Hierbei handelt es sich um Kontingente, die vom jeweiligen verwendeten Transportkanal erzwungen werden (HTTP, TCP usw.). Während in diesem Thema einige dieser Kontingente erläutert werden, werden diese Kontingente in den Transportkontingenten ausführlich beschrieben.

Hashbare Sicherheitsanfälligkeit

Eine Sicherheitsanfälligkeit ist vorhanden, wenn Datenverträge Hashtables oder Sammlungen enthalten. Das Problem tritt auf, wenn eine große Anzahl von Werten in eine Hashtabelle eingefügt wird, bei der eine große Anzahl dieser Werte denselben Hashwert generiert. Dies kann als DOS-Angriff verwendet werden. Diese Sicherheitsanfälligkeit kann durch Festlegen des Bindungskontingents "MaxReceivedMessageSize" verringert werden. Achten Sie beim Festlegen dieses Kontingents, um solche Angriffe zu verhindern. Dieses Kontingent legt eine Obergrenze für die Größe der WCF-Nachricht fest. Vermeiden Sie außerdem die Verwendung von Hashtables oder Sammlungen in Ihren Datenverträgen.

Einschränken der Speicherauslastung ohne Streaming

Das Sicherheitsmodell für große Nachrichten hängt davon ab, ob Streaming verwendet wird. Im einfachen, nicht gestreamten Fall werden Nachrichten in den Arbeitsspeicher gepuffert. Verwenden Sie hier zur Abwehr großer Nachrichten das MaxReceivedMessageSize -Kontingent für TransportBindingElement oder für die vom System bereitgestellten Bindungen, und schränken Sie damit die maximal verarbeitete Nachrichtengröße ein. Beachten Sie, dass ein Dienst mehrere Nachrichten gleichzeitig verarbeitet, in diesem Fall befinden sie sich alle im Arbeitsspeicher. Verwenden Sie die Drosselungsfunktion, um diese Bedrohung zu verringern.

Beachten Sie außerdem, dass MaxReceivedMessageSize keine obere Grenze für die Speicherauslastung pro Nachricht gesetzt wird, sie jedoch auf einen konstanten Faktor beschränkt. Wenn dies MaxReceivedMessageSize beispielsweise 1 MB ist und eine 1-MB-Nachricht empfangen und dann deserialisiert wird, ist zusätzlicher Speicher erforderlich, um das deserialisierte Objektdiagramm zu enthalten, was zu einer Gesamtspeicherauslastung von gut 1 MB führt. Vermeiden Sie aus diesem Grund das Erstellen serialisierbarer Typen, die zu einer erheblichen Speicherauslastung ohne viele eingehende Daten führen könnten. Beispielsweise könnte ein Datenvertrag "MyContract" mit 50 optionalen Datenmemberfeldern und zusätzlichen 100 privaten Feldern durch die XML-Konstruktion "<MyContract/>" erstellt werden. Dieser XML-Code führt dazu, dass für 150 Felder auf den Speicher zugegriffen wird. Datenmember sind standardmäßig optional. Das Problem wird verschärft, wenn ein solcher Typ Teil eines Arrays ist.

MaxReceivedMessageSize allein reicht nicht aus, um alle Denial-of-Service-Angriffe zu verhindern. Das Deserialisierungsprogramm kann z. B. von einer eingehenden Nachricht gezwungen werden, ein tief geschachteltes Objektdiagramm (ein Objekt, das ein anderes Objekt enthält, das wiederum ein Objekt enthält usw.) zu deserialisieren. Sowohl DataContractSerializer als auch XmlSerializer rufen zum Deserialisieren solcher Diagramme Methoden in einer geschachtelte Weise auf. Eine tiefe Schachtelung von Methodenaufrufen kann zu einer nicht behebbaren StackOverflowExceptionführen. Diese Bedrohung wird verringert, indem das MaxDepth Kontingent festgelegt wird, um die Ebene der XML-Schachtelung einzuschränken, wie weiter unten im Thema im Abschnitt "Verwenden von XML sicher" beschrieben.

MaxReceivedMessageSize festzulegen, ist bei der Verwendung einer binären XML-Codierung besonders wichtig. Die Verwendung der binären Codierung entspricht etwas der Komprimierung: Eine kleine Gruppe von Bytes in der eingehenden Nachricht kann viele Daten darstellen. So kann sogar eine Nachricht, die in die MaxReceivedMessageSize Grenze passt, viel mehr Arbeitsspeicher in vollständig erweiterter Form in Anspruch nehmen. Um solche XML-spezifischen Bedrohungen zu mindern, müssen alle XML-Lesekontingente korrekt festgelegt werden, wie weiter unten in diesem Thema im Abschnitt "Sichere Verwendung von XML" beschrieben.

Einschränken der Speicherauslastung mit Streaming

Beim Streaming können Sie eine kleine MaxReceivedMessageSize Einstellung verwenden, um vor Denial-of-Service-Angriffen zu schützen. Komplexere Szenarien sind jedoch mit Streaming möglich. Beispielsweise akzeptiert ein Dateiuploaddienst Dateien, die größer als alle verfügbaren Arbeitsspeicher sind. Legen Sie in diesem Fall den MaxReceivedMessageSize Wert auf einen extrem großen Wert fest, wobei erwartet wird, dass fast keine Daten im Arbeitsspeicher gepuffert werden und die Nachrichtenströme direkt auf den Datenträger übertragen werden. Wenn eine schädliche Nachricht WCF dazu zwingen kann, Daten zu puffern, anstatt sie zu streamen, schützt MaxReceivedMessageSize nicht mehr davor, dass die Nachricht auf den gesamten verfügbaren Speicher zugreift.

Um diese Bedrohung zu minimieren, sind bestimmte Kontingenteinstellungen für verschiedene WCF-Datenverarbeitungskomponenten vorhanden, die die Pufferung einschränken. Die wichtigste davon ist die MaxBufferSize Eigenschaft für verschiedene Transportbindungselemente und Standardbindungen. Beim Streaming sollte dieses Kontingent unter Berücksichtigung der maximalen Speichermenge festgelegt werden, die Sie pro Nachricht zuweisen möchten. Wie bei MaxReceivedMessageSizewird mit dieser Einstellung kein absolutes Maximum für den Arbeitsspeicherverbrauch festgelegt, sondern der Verbrauch wird auf eine konstante Größe beschränkt. Beachten Sie außerdem, wie bei diesem Verfahren MaxReceivedMessageSize, dass mehrere Nachrichten gleichzeitig verarbeitet werden können.

Näheres zu MaxBufferSize

Die MaxBufferSize Eigenschaft begrenzt die Massenpufferung, die von WCF durchgeführt wird. Beispielsweise werden SOAP-Header und SOAP-Fehler von WCF immer gepuffert, ebenso wie MIME-Bestandteile, die in einer MTOM-Nachricht (Message Transmission Optimization Mechanism) nicht die natürliche Lesereihenfolge aufweisen. Diese Einstellung beschränkt die Puffermenge in all diesen Fällen.

WCF erreicht dies, indem der MaxBufferSize Wert an die verschiedenen Komponenten übergeben wird, die puffern können. Beispielsweise nehmen einige CreateMessage Überladungen der Message Klasse einen maxSizeOfHeaders Parameter an. WCF übergibt den MaxBufferSize Wert an diesen Parameter, um die Menge der SOAP-Headerpuffer zu begrenzen. Es ist wichtig, diesen Parameter bei der direkten Verwendung der Message Klasse festzulegen. Im Allgemeinen ist es wichtig, bei der Verwendung einer Komponente in WCF, die Kontingentparameter verwendet, die Sicherheitsauswirkungen dieser Parameter zu verstehen und richtig festzulegen.

Der MTOM-Nachrichten-Encoder verfügt auch über eine MaxBufferSize Einstellung. Bei Verwendung von Standardbindungen wird dies automatisch auf den Wert der Transportebene MaxBufferSize festgelegt. Wenn Sie jedoch das MTOM-Nachrichtengeberbindungselement zum Erstellen einer benutzerdefinierten Bindung verwenden, ist es wichtig, die MaxBufferSize Eigenschaft auf einen sicheren Wert festzulegen, wenn Streaming verwendet wird.

XML-basierte Streamingangriffe

MaxBufferSize allein reicht nicht aus, um sicherzustellen, dass WCF nicht zum Puffern gezwungen werden kann, wenn streaming erwartet wird. Wenn z. B. die WCF-XML-Reader mit dem Lesen eines neuen Elements beginnen, puffern sie stets das gesamte Starttag des XML-Elements. Dies geschieht, damit Namespaces und Attribute ordnungsgemäß verarbeitet werden. Wenn MaxReceivedMessageSize hoch konfiguriert wird (z. B. um eine umfangreiches Streaming direkt auf die Festplatte zu ermöglichen), könnte eine böswillige Nachricht erstellt werden, in der der gesamte Nachrichtentext aus einem großen Starttag für XML-Elemente besteht. Der Versuch, dieses Starttag zu lesen, führt zu einer OutOfMemoryException. Dies ist einer der vielen möglichen XML-basierten Denial-of-Service-Angriffe, die alle mithilfe von XML-Lesekontingenten abgemildert werden können, die weiter unten in diesem Thema im Abschnitt "Sichere Verwendung von XML" erläutert werden. Beim Streaming ist es besonders wichtig, all diese Kontingente festzulegen.

Mischen von Streaming- und Pufferprogrammierungsmodellen

Viele mögliche Angriffe ergeben sich aus dem Mischen von Streaming- und Nicht-Streaming-Programmiermodellen im selben Dienst. Angenommen, ein Dienst umfasst zwei Vorgänge: ein Vorgang verwendet einen Stream und ein anderer ein Array eines benutzerdefinierten Typs. Angenommen, dass MaxReceivedMessageSize auf einen großen Wert festgelegt ist, um den ersten Vorgang zur Verarbeitung großer Datenströme zu ermöglichen. Leider bedeutet dies, dass große Nachrichten nun auch an den zweiten Vorgang gesendet werden können, und der Deserialisierer puffert Daten im Arbeitsspeicher als Array, bevor der Vorgang aufgerufen wird. Dies ist ein potenzieller Denial-of-Service-Angriff: Das MaxBufferSize Kontingent beschränkt nicht die Größe des Nachrichtentexts, mit dem der Deserialisierer arbeitet.

Vermeiden Sie aus diesem Grund das Mischen von streambasierten und nicht gestreamten Vorgängen im selben Vertrag. Wenn Sie unbedingt die beiden Programmiermodelle kombinieren müssen, sollten Sie die folgenden Vorsichtsmaßnahmen treffen:

  • Deaktivieren Sie die IExtensibleDataObject Funktion, indem Sie die IgnoreExtensionDataObject Eigenschaft von ServiceBehaviorAttribute auf true festlegen. Dadurch stellen Sie sicher, dass nur Member deserialisiert werden, die zum Vertrag gehören.

  • Legen Sie die MaxItemsInObjectGraph-Eigenschaft von DataContractSerializer auf einen sicheren Wert fest. Dieses Kontingent ist auch über das ServiceBehaviorAttribute Attribut oder durch Konfiguration verfügbar. Dieses Kontingent beschränkt die Anzahl der Objekte, die in einer Deserialisierungsserie deserialisiert werden. Normalerweise wird jeder Vorgangsparameter oder Nachrichtentextteil in einem Nachrichtenvertrag in einer Folge deserialisiert. Beim Deserialisieren von Arrays wird jeder Arrayeintrag als separates Objekt gezählt.

  • Legen Sie alle XML-Lesekontingente auf sichere Werte fest. Achten Sie auf MaxDepth, MaxStringContentLengthund MaxArrayLength , und vermeiden Sie Zeichenfolgen in anderen als Streamingvorgängen.

  • Überprüfen Sie die Liste der bekannten Typen, wobei Sie bedenken sollten, dass jeder von ihnen jederzeit instanziiert werden kann (siehe Abschnitt "Verhindern, dass unbeabsichtigte Typen geladen werden" im späteren Verlauf dieses Themas).

  • Verwenden Sie keine Typen, die die IXmlSerializable Schnittstelle implementieren, die viele Daten puffert. Fügen Sie der Liste der bekannten Typen keine solchen Typen hinzu.

  • Verwenden Sie kein XmlElementund keine XmlNode -Arrays, Byte -Arrays oder Typen, die ISerializable in einem Vertrag implementieren.

  • Verwenden Sie kein XmlElementund keine XmlNode -Arrays, Byte -Arrays oder Typen, die ISerializable in der Liste der bekannten Typen implementieren.

Die vorangehenden Vorkehrungen sind relevant, wenn der Vorgang ohne Streaming DataContractSerializerverwendet. Mischen Sie keine Streaming- und Nicht-Streaming-Programmierungsmodelle auf demselben Dienst, wenn Sie das XmlSerializerKontingent verwenden, da es nicht den Schutz des MaxItemsInObjectGraph Kontingents hat.

Langsame Streamangriffe

Eine Klasse von Streaming-Denial-of-Service-Angriffen erfordert keinen Arbeitsspeicherverbrauch. Stattdessen umfasst der Angriff einen langsamen Absender oder Empfänger von Daten. Während sie auf das Senden oder Empfangen der Daten warten, werden Ressourcen wie Threads und verfügbare Verbindungen erschöpft. Diese Situation kann entweder als Folge eines böswilligen Angriffs oder eines legitimen Absenders/Empfängers bei einer langsamen Netzwerkverbindung auftreten.

Legen Sie die Transporttimeouts richtig fest, um diese Angriffe zu verhindern. Weitere Informationen finden Sie unter Transportkontingente. Verwenden Sie außerdem niemals synchrone Read- oder Write-Vorgänge, wenn Sie in WCF mit Streams arbeiten.

Sicheres Verwenden von XML

Hinweis

Obwohl dieser Abschnitt über XML verfügt, gelten die Informationen auch für JavaScript Object Notation (JSON)-Dokumente. Die Kontingente funktionieren ähnlich, wobei die Zuordnung zwischen JSON und XML verwendet wird.

Sichere XML-Reader

Das XML-Infoset bildet die Basis aller Nachrichtenverarbeitungen in WCF. Beim Akzeptieren von XML-Daten aus einer nicht vertrauenswürdigen Quelle gibt es eine Reihe von Denial-of-Service-Angriffsmöglichkeiten, die abgemildert werden müssen. WCF bietet spezielle, sichere XML-Reader. Diese Leser werden automatisch erstellt, wenn sie eine der Standardcodierungen in WCF (Text, Binärdatei oder MTOM) verwenden.

Einige der Sicherheitsfunktionen dieser Reader sind immer aktiv. Beispielsweise verarbeiten die Leser niemals Dokumenttypdefinitionen (DTDs), die eine potenzielle Quelle für Denial-of-Service-Angriffe sind und niemals in legitimen SOAP-Nachrichten erscheinen sollten. Weitere Sicherheitsfeatures sind Lesekontingente, die konfiguriert werden müssen, die im folgenden Abschnitt beschrieben werden.

Wenn Sie direkt mit XML-Readern arbeiten (z. B. beim Schreiben eines eigenen benutzerdefinierten Encoders oder beim direkten Arbeiten mit der Message Klasse), verwenden Sie immer die sicheren WCF-Leser, wenn die Möglichkeit besteht, mit nicht vertrauenswürdigen Daten zu arbeiten. Erstellen Sie die sicheren Reader, indem Sie eine der Überladungen der statischen Factorymethode von CreateTextReader, CreateBinaryReaderoder CreateMtomReader für die XmlDictionaryReader -Klasse aufrufen. Übergeben Sie beim Erstellen eines Readers sichere Kontingentwerte. Rufen Sie nicht die Überladungen der Create -Methode auf. Diese erstellen keinen WCF-Reader. Stattdessen wird ein Leser erstellt, der nicht durch die in diesem Abschnitt beschriebenen Sicherheitsfeatures geschützt ist.

Readerkontingente

Die sicheren XML-Reader verfügen über fünf konfigurierbare Kontingente. Diese werden in der Regel mithilfe der ReaderQuotas -Eigenschaft für das Codierungsbindungselement oder die Standardbindungen konfiguriert oder mithilfe eines XmlDictionaryReaderQuotas -Objekts, das beim Erstellen eines Readers übergeben wird.

MaxBytesPerRead

Diese Grenze beschränkt die Anzahl der Bytes, die in einem einzelnen Read Vorgang gelesen werden, wenn der Start-Tag des Elements und seine Attribute gelesen werden. (In nicht gestreamten Fällen wird der Elementname selbst nicht für das Kontingent gezählt.) MaxBytesPerRead ist aus folgenden Gründen wichtig:

  • Der Elementname und die zugehörigen Attribute werden immer im Arbeitsspeicher gepuffert, wenn sie gelesen werden. Daher ist es wichtig, dieses Kontingent im Streamingmodus korrekt festzulegen, um übermäßige Pufferung zu verhindern, wenn streaming erwartet wird. Informationen zur tatsächlichen Puffermenge, die stattfindet, finden Sie im MaxDepth Kontingentabschnitt.

  • Wenn zu viele XML-Attribute vorhanden sind, kann die Verarbeitungszeit unverhältnismäßig hoch sein, da Attributnamen auf Eindeutigkeit überprüft werden müssen. MaxBytesPerRead mindert dieses Risiko.

Maximale Tiefe

Diese Kontingent schränkt die maximale Schachtelungstiefe von XML-Elementen ein. Das Dokument „<A><B><C/></B></A>“ beispielsweise hat eine Schachtelungstiefe von drei Ebenen. MaxDepth ist aus folgenden Gründen wichtig:

  • MaxDepth interagiert mit MaxBytesPerRead: Der Leser behält immer Daten im Arbeitsspeicher für das aktuelle Element und alle seine Vorgänger, sodass der maximale Speicherverbrauch des Readers proportional zum Produkt dieser beiden Einstellungen ist.

  • Wenn Sie ein tief geschachteltes Objektdiagramm deserialisieren, ist das Deserialisierungsprogramm gezwungen, auf den gesamten Stapel zuzugreifen und eine nicht behebbare StackOverflowExceptionauszulösen. Eine direkte Korrelation besteht zwischen XML-Schachtelung und Objektschachtelung für die DataContractSerializer und die XmlSerializer. Verwenden Sie MaxDepth, um diese Bedrohung zu mindern.

MaxNameTableCharCount

Dieses Kontingent beschränkt die Größe der Namenstabelle des Lesers. Die Nametable enthält bestimmte Zeichenfolgen (z. B. Namespaces und Präfixe), die beim Verarbeiten eines XML-Dokuments auftreten. Wenn diese Zeichenfolgen im Arbeitsspeicher gepuffert werden, legen Sie dieses Kontingent fest, um übermäßige Pufferung zu verhindern, wenn streaming erwartet wird.

MaxStringContentLength

Dieses Kontingent beschränkt die maximale Zeichenfolgengröße, die der XML-Reader zurückgibt. Dieses Kontingent beschränkt den Arbeitsspeicherverbrauch im XML-Reader selbst nicht, sondern in der Komponente, die den Reader verwendet. Wenn DataContractSerializer z. B. einen Reader verwendet, der mit MaxStringContentLengthgesichert ist, werden keine Zeichenfolgen deserialisiert, deren Größe dieses Kontingent überschreitet. Bei der direkten Verwendung der XmlDictionaryReader Klasse berücksichtigen nicht alle Methoden dieses Kontingents, sondern nur die Methoden, die speziell für das Lesen von Zeichenfolgen konzipiert sind, z. B. die ReadContentAsString Methode. Die Value -Eigenschaft für den Reader wird von diesem Kontingent nicht beeinflusst. Sie sollte deshalb nicht verwendet werden, wenn der Schutz benötigt wird, den dieses Kontingent bietet.

MaxArrayLength

Dieses Kontingent beschränkt die maximale Größe eines Arrays von Grundtypen, die der XML-Reader zurückgibt, einschließlich Bytearrays. Dieses Kontingent schränkt den Arbeitsspeicherverbrauch im XML-Reader selbst nicht ein, sondern in jeder Komponente, die den Reader verwendet. Wenn DataContractSerializer z. B. einen Reader verwendet, der mit MaxArrayLengthgesichert ist, werden keine Bytearrays deserialisiert, deren Größe dieses Kontingent überschreitet. Es ist wichtig, dieses Kontingent festzulegen, wenn Versucht wird, Streaming- und gepufferte Programmiermodelle in einem einzigen Vertrag zu kombinieren. Denken Sie daran, dass bei der direkten Verwendung der XmlDictionaryReader Klasse nur die Methoden, die speziell für das Lesen von Arrays mit beliebiger Größe bestimmter primitiver Typen, wie ReadInt32Array, konzipiert sind, dieses Kontingent beachten.

Bedrohungen, die für die binäre Codierung spezifisch sind

Die binäre XML-Codierung von WCF unterstützt eine Funktion für Wörterbuchzeichenfolgen. Eine große Zeichenfolge kann mit nur wenigen Bytes codiert werden. Dies ermöglicht erhebliche Leistungssteigerungen, führt jedoch neue Denial-of-Service-Bedrohungen ein, die abgemildert werden müssen.

Es gibt zwei Arten von Wörterbüchern: statisch und dynamisch. Das statische Wörterbuch ist eine integrierte Liste von langen Zeichenfolgen, die mit einem kurzen Code in der binären Codierung dargestellt werden können. Diese Liste der Zeichenfolgen wurde beim Erstellen des Readers behoben und kann nicht geändert werden. Keiner der Zeichenfolgen im statischen Wörterbuch, das WCF standardmäßig verwendet, ist ausreichend groß, um eine ernsthafte Denial-of-Service-Bedrohung zu darstellen, obwohl sie möglicherweise noch in einem Wörterbucherweiterungsangriff verwendet werden können. In erweiterten Szenarien, in denen Sie Ihr eigenes statisches Wörterbuch bereitstellen, sollten Sie beim Einführen großer Wörterbuchzeichenfolgen vorsichtig sein.

Mit dem Feature "Dynamische Wörterbücher" können Nachrichten ihre eigenen Zeichenfolgen definieren und sie kurzcodes zuordnen. Diese Zeichenfolgen-zu-Code-Zuordnungen werden während der gesamten Kommunikationssitzung im Arbeitsspeicher gespeichert, sodass nachfolgende Nachrichten die Zeichenfolgen nicht erneut senden müssen und Codes verwenden können, die bereits definiert sind. Diese Zeichenfolgen können beliebig lang sein und somit eine schwerwiegendere Bedrohung darstellen als die im statischen Wörterbuch.

Die erste Bedrohung, die abgemildert werden muss, ist die Möglichkeit, dass das dynamische Wörterbuch (die Zeichenfolgen-zu-Code-Zuordnungstabelle) zu groß wird. Dieses Wörterbuch kann im Laufe mehrerer Nachrichten erweitert werden, sodass das MaxReceivedMessageSize Kontingent keinen Schutz bietet, da es nur für jede Nachricht separat gilt. Daher existiert eine separate MaxSessionSize Eigenschaft auf der BinaryMessageEncodingBindingElement, die die Größe des Wörterbuchs begrenzt.

Im Gegensatz zu den meisten anderen Kontingenten gilt dieses Kontingent auch beim Schreiben von Nachrichten. Wenn beim Lesen einer Nachricht diese überschritten wird, wird wie gewohnt QuotaExceededException ausgelöst. Wenn es beim Schreiben einer Nachricht überschritten wird, werden alle Zeichenfolgen, die das Überschreiten des Kontingents verursachen, ohne Einbeziehung der Funktion für dynamische Wörterbücher so geschrieben, wie sie sind.

Bedrohungen für die Wörterbucherweiterung

Eine bedeutende Klasse binär-spezifischer Angriffe ergibt sich aus der Wörterbucherweiterung. Eine kleine Nachricht in binärer Form kann sich in eine sehr große Nachricht in vollständig erweiterter Textform verwandeln, wenn sie die Funktion "Zeichenfolgenwörterbücher" umfassend verwendet. Der Erweiterungsfaktor für dynamische Wörterbuchzeichenfolgen ist durch das MaxSessionSize Kontingent begrenzt, da keine dynamische Wörterbuchzeichenfolge die maximale Größe des gesamten Wörterbuchs überschreitet.

Die Eigenschaften MaxNameTableCharCount, MaxStringContentLength und MaxArrayLength beschränken nur den Speicherverbrauch. Sie sind normalerweise nicht erforderlich, um Bedrohungen in der nicht gestreamten Nutzung zu mindern, da die Speicherauslastung bereits durch MaxReceivedMessageSize begrenzt ist. MaxReceivedMessageSize berechnet jedoch die Bytes vor der Erweiterung. Wenn die binäre Codierung verwendet wird, könnte der Speicherverbrauch möglicherweise MaxReceivedMessageSize übersteigen und ist nur durch den Faktor MaxSessionSize begrenzt. Aus diesem Grund ist es wichtig, immer alle Lesekontingente (insbesondere MaxStringContentLength) bei Verwendung der binären Codierung festzulegen.

Wenn Sie die binäre Codierung zusammen mit der DataContractSerializerSchnittstelle verwenden, kann die IExtensibleDataObject Schnittstelle zum Bereitstellen eines Wörterbucherweiterungsangriffs missbraucht werden. Diese Schnittstelle bietet im Wesentlichen unbegrenzten Speicher für beliebige Daten, die nicht Teil des Vertrags sind. Wenn Kontingente nicht niedrig genug festgelegt werden können, sodass MaxSessionSize multipliziert mit MaxReceivedMessageSize kein Problem darstellt, deaktivieren Sie die IExtensibleDataObject Funktion bei Nutzung der binären Codierung. Legen Sie die IgnoreExtensionDataObject-Eigenschaft auf true im ServiceBehaviorAttribute-Attribut fest. Alternativ können Sie die IExtensibleDataObject Schnittstelle nicht implementieren. Weitere Informationen finden Sie unter Forward-Compatible Datenverträge.

Zusammenfassung der Kontingente

In der folgenden Tabelle sind die Anleitungen zu Kontingenten zusammengefasst.

Zustand Wichtige Kontingente, die festgelegt werden sollen
Kein Streaming oder Streaming von kleinen Nachrichten, Text oder MTOM-Codierung MaxReceivedMessageSize, MaxBytesPerRead und MaxDepth
Kein Streaming oder Streaming von kleinen Nachrichten und binäre Codierung MaxReceivedMessageSize, MaxSessionSize, und alle ReaderQuotas
Streamen großer Nachrichten, Text oder MTOM-Codierung MaxBufferSize und alle ReaderQuotas
Streaming großer Nachrichten, binäre Codierung MaxBufferSize, MaxSessionSize, und alle ReaderQuotas
  • Transportebene-Timeouts müssen immer festgelegt werden, und synchrone Lese-/Schreibvorgänge dürfen beim Streaming niemals verwendet werden, unabhängig davon, ob große oder kleine Nachrichten gestreamt werden.

  • Wenn Sie Zweifel an einem Kontingent haben, legen Sie es auf einen sicheren Wert fest, anstatt es offen zu lassen.

Verhindern der Ausführung bösartigen Codes

Die folgenden allgemeinen Bedrohungsklassen können Code ausführen und unbeabsichtigte Auswirkungen haben:

  • Das Deserialisierungsprogramm lädt einen schädlichen, unsicheren oder sicherheitsrelevanten Typ.

  • Eine eingehende Nachricht veranlasst das Deserialisierungsprogramm, eine Instanz eines normalerweise sicheren Typs so zu erstellen, dass er unerwünschte Folgen hat.

In den folgenden Abschnitten werden diese Bedrohungsklassen weiter erläutert.

DataContractSerializer

(Sicherheitsinformationen zum XmlSerializerThema finden Sie in der entsprechenden Dokumentation.) Das Sicherheitsmodell für das XmlSerializer ist ähnlich dem des DataContractSerializer, und unterscheidet sich hauptsächlich in Details. Das XmlIncludeAttribute Attribut wird für den Typeneinschluss anstelle des KnownTypeAttribute Attributs verwendet. Einige Bedrohungen, die für die XmlSerializer einzigartig sind, werden jedoch später in diesem Thema behandelt.

Verhindern des Ladens unerwünschter Typen

Das Laden unerwünschter Typen kann schwerwiegende Folgen haben, unabhängig davon, ob der Typ schädlich ist oder nur sicherheitsrelevante Nebeneffekte aufweist. Ein Typ kann ausnutzbare Sicherheitsrisiken enthalten, sicherheitsrelevante Aktionen in seinem Konstruktor oder Klassenkonstruktor ausführen, einen großen Speicherbedarf aufweisen, der Denial-of-Service-Angriffe erleichtert oder nicht wiederherstellbare Ausnahmen auslösen kann. Typen verfügen möglicherweise über Klassenkonstruktoren, die ausgeführt werden, sobald der Typ geladen wird, und bevor Instanzen erstellt werden. Aus diesen Gründen ist es wichtig, den Satz von Typen zu steuern, die der Deserialisierer laden kann.

DataContractSerializer führt die Desialisierung mit einer losen Verknüpfung durch. Es werden keine Namen von CRL-Typen (Common Language Runtime) oder von Assemblys in den eingehenden Daten gelesen. Dies ähnelt dem Verhalten des XmlSerializer, aber unterscheidet sich vom Verhalten des NetDataContractSerializer, BinaryFormatterund des .SoapFormatter Die lose Verknüpfung bietet eine gewisse Sicherheit, da Remoteangreifer das Laden eines beliebigen Typs nicht veranlassen können, indem sie diesen Typ einfach in der Nachricht benennen.

DataContractSerializer darf immer einen Typ laden, der aktuell gemäß dem Vertrag erwartet wird. Wenn z. B. ein Datenvertrag einen Datenmember vom Typ Customerenthält, darf DataContractSerializer den Customer -Typ beim Deserialisieren dieses Datenmembers laden.

Darüber hinaus unterstützt die DataContractSerializer Polymorphität. Ein Datenmitglied kann als Objectdeklariert werden, aber die eingehenden Daten können eine Customer Instanz enthalten. Dies ist nur möglich, wenn der Customer Typ dem Deserialisierer durch einen der folgenden Mechanismen "bekannt" gemacht wurde:

  • KnownTypeAttribute auf einen Typ angewendetes Attribut.

  • KnownTypeAttribute Attribut, das eine Methode angibt, die eine Liste von Typen zurückgibt.

  • ServiceKnownTypeAttribute-Attribut.

  • KnownTypes -Konfigurationsabschnitt.

  • Eine Liste bekannter Typen wird bei direkter Verwendung des Serialisierungsprogramm während der Erstellung explizit an DataContractSerializer übergeben.

Jede dieser Mechanismen erhöht die Angriffsfläche, da sie die Anzahl der Typen erhöhen, die das Deserialisierungsprogramm laden kann. Steuern Sie jeden dieser Mechanismen, um sicherzustellen, dass der Liste der bekannten Typen keine böswilligen oder unbeabsichtigten Typen hinzugefügt werden.

Sobald ein bekannter Typ innerhalb des Geltungsbereichs ist, kann er jederzeit geladen werden, und Instanzen des Typs können erstellt werden, auch wenn der Vertrag deren tatsächliche Nutzung untersagt. Angenommen, der Typ "MyDangerousType" wird der Liste bekannter Typen mithilfe eines der oben genannten Mechanismen hinzugefügt. Dies bedeutet Folgendes:

  • MyDangerousType wird geladen, und der Klassenkonstruktor wird ausgeführt.

  • Auch wenn nur ein Datenvertrag mit einem Zeichenfolgendatenmember deserialisiert wird, kann eine böswillige Nachricht dazu führen, dass eine Instanz von MyDangerousType erstellt wird. Code in MyDangerousType, z. B. ein Eigenschaftensetter, wird möglicherweise ausgeführt. Anschließend versucht das Deserialisierungsprogramm diese Instanz dem Zeichenfolgendatenmember zuzuordnen, schlägt fehl und löst eine Ausnahme aus.

Stellen Sie beim Schreiben einer Methode, die eine Liste bekannter Typen zurückgibt, oder beim direkten Übergeben einer Liste an den DataContractSerializer Konstruktor sicher, dass der Code, der die Liste vorbereitet, sicher ist und nur für vertrauenswürdige Daten verwendet wird.

Wenn Sie bekannte Typen in der Konfiguration angeben, stellen Sie sicher, dass die Konfigurationsdatei sicher ist. Verwenden Sie immer starke Namen in der Konfiguration (indem Sie den öffentlichen Schlüssel der signierten Assembly angeben, in der sich der Typ befindet), aber nicht die Version des zu ladenden Typs angeben. Das Ladeprogramm wählt, wenn möglich, automatisch die neueste Version aus. Wenn Sie eine bestimmte Version in der Konfiguration angeben, führen Sie das folgende Risiko aus: Ein Typ hat möglicherweise eine Sicherheitslücke, die in einer zukünftigen Version behoben werden kann, aber die anfällige Version wird weiterhin geladen, da sie explizit in der Konfiguration angegeben ist.

Zu viele bekannte Typen haben eine weitere Folge: Der DataContractSerializer Cache des Serialisierungs-/Deserialisierungscodes in der Anwendungsdomäne wird mit einem Eintrag für jeden Typ erstellt, der serialisiert und deserialisiert werden muss. Dieser Cache wird nie gelöscht, solange die Anwendungsdomäne ausgeführt wird. Daher kann ein Angreifer, der sich bewusst ist, dass eine Anwendung viele bekannte Typen verwendet, die Deserialisierung all dieser Typen verursachen, wodurch der Cache eine unverhältnismäßig große Speichermenge verbraucht.

Verhindern eines unerwünschter Zustands der Typen

Ein Typ kann interne Konsistenzeinschränkungen aufweisen, die erzwungen werden müssen. Diese Einschränkungen dürfen bei der Deserialisierung nicht außer Kraft gesetzt werden.

Das folgende Beispiel eines Typs stellt den Zustand einer Luftsperre auf einem Raumfahrzeug dar und erzwingt die Einschränkung, dass sowohl die inneren als auch die äußeren Türen nicht gleichzeitig geöffnet werden können.

[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

Ein Angreifer könnte eine böswillige Nachricht wie diese senden, um die Einschränkungen zu umgehen und das Objekt in einen ungültigen Zustand zu versetzen, was unbeabsichtigte und unvorhersehbare Folgen haben kann.

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

Diese Situation kann vermieden werden, indem man sich die folgenden Punkte bewusst ist:

  • Wenn DataContractSerializer Klassen deserialisiert, werden in den meisten Fällen keine Konstruktoren ausgeführt. Verlassen Sie sich daher nicht auf eine Zustandsverwaltung, die im Konstruktor ausgeführt wird.

  • Verwenden Sie Rückrufe, um sicherzustellen, dass sich das Objekt in einem gültigen Zustand befindet. Der mit dem OnDeserializedAttribute Attribut gekennzeichnete Rückruf ist besonders nützlich, da er nach Abschluss der Deserialisierung ausgeführt wird und die Möglichkeit hat, den Gesamtzustand zu untersuchen und zu korrigieren. Weitere Informationen finden Sie unter Versionstolerante Serialisierungsrückrufe.

  • Entwerfen Sie keine Datenvertragstypen, die sich auf eine bestimmte Reihenfolge verlassen, in der Eigenschafts-Setter aufgerufen werden müssen.

  • Achten Sie darauf, legacy-Typen zu verwenden, die mit dem SerializableAttribute Attribut gekennzeichnet sind. Viele davon wurden für .NET Framework-Remoting zur ausschließlichen Verwendung mit vertrauenswürdigen Daten entwickelt. Bei der Entwicklung vorhandener Typen, die mit diesem Attribut gekennzeichnet sind, spielte die Sicherheit möglicherweise keine Rolle.

  • Verlassen Sie sich nicht auf die IsRequired Eigenschaft des DataMemberAttribute Attributs, um das Vorhandensein von Daten im Hinblick auf die staatliche Sicherheit zu gewährleisten. Daten können immer null, zero oder invalid sein.

  • Vertrauen Sie niemals einem Objektdiagramm, das aus einer nicht vertrauenswürdigen Datenquelle deserialisiert wurde, ohne es zuerst zu validieren. Jedes einzelne Objekt kann sich in einem konsistenten Zustand befinden, aber das Objektdiagramm insgesamt ist möglicherweise nicht. Auch wenn der Erhaltungsmodus des Objektdiagramms deaktiviert ist, kann das deserialisierte Diagramm mehrere Verweise auf dasselbe Objekt aufweisen oder Zirkelbezüge aufweisen. Weitere Informationen finden Sie unter Serialisierung und Deserialisierung.

Sicheres Verwenden des NetDataContractSerializers

NetDataContractSerializer ist eine Serialisierungs-Engine, das eng verknüpfte Typen verwendet. Dies ähnelt dem BinaryFormatter und dem SoapFormatter. Das heißt, er bestimmt, welcher Typ instanziiert werden soll, indem die .NET Framework-Assembly und der Typname aus den eingehenden Daten gelesen wird. Obwohl es Teil von WCF ist, gibt es keine Möglichkeit, dieses Serialisierungsmodul zu anschließen. Benutzerdefinierter Code muss geschrieben werden. Dies NetDataContractSerializer wird in erster Linie bereitgestellt, um die Migration von .NET Framework-Remoting zu WCF zu vereinfachen. Weitere Informationen finden Sie im entsprechenden Abschnitt in Serialisierung und Deserialisierung.

Da die Nachricht selbst darauf hinweisen kann, dass ein beliebiger Typ geladen werden kann, ist der NetDataContractSerializer Mechanismus inhärent unsicher und sollte nur mit vertrauenswürdigen Daten verwendet werden. Weitere Informationen finden Sie im Sicherheitsleitfaden für BinaryFormatter.

Auch wenn sie mit vertrauenswürdigen Daten verwendet werden, geben die eingehenden Daten möglicherweise nicht ausreichend an, welcher Typ geladen werden soll, insbesondere wenn die AssemblyFormat-Eigenschaft auf Simple festgelegt ist. Jeder Benutzer, der Zugriff auf das Verzeichnis der Anwendung oder auf den globalen Assemblycache hat, kann einen schädlichen Typ anstelle des Typs ersetzen, der geladen werden soll. Stellen Sie immer die Sicherheit des Anwendungsverzeichnisses und des globalen Assemblycaches sicher, indem Sie die Berechtigungen richtig festlegen.

Im Allgemeinen gilt: Wenn Sie teilweise vertrauenswürdigem Code erlauben, auf Ihre NetDataContractSerializer -Instanz zuzugreifen oder auf andere Weise den Ersatzselektor (ISurrogateSelector) bzw. den Serialisierungsbinder (SerializationBinder) zu steuern, kann er möglicherweise einen großen Teil des Serialisierungs-/Deserialisierungsprozesses steuern. So kann es beispielsweise beliebige Typen einfügen, zu Einer Offenlegung von Informationen führen, das resultierende Objektdiagramm oder serialisierte Daten manipulieren oder den resultierenden serialisierten Datenstrom überlaufen.

Ein weiteres Sicherheitsanliegen mit dem NetDataContractSerializer ist ein Denial-of-Service-Problem, keine Bedrohung durch die Ausführung von bösartigem Code. Wenn Sie das NetDataContractSerializerKontingent verwenden, legen Sie das MaxItemsInObjectGraph Kontingent immer auf einen sicheren Wert fest. Es ist einfach, eine kleine bösartige Nachricht zu erstellen, die ein Array von Objekten zuordnet, deren Größe nur durch dieses Kontingent begrenzt ist.

Spezielle Bedrohungen bei XmlSerializer

Das XmlSerializer Sicherheitsmodell ähnelt dem des DataContractSerializer. Einige Bedrohungen sind jedoch einzigartig für die XmlSerializer.

Die XmlSerializer generiert zur Laufzeit Serialisierungsassemblies, die Code enthalten, der tatsächlich serialisiert und deserialisiert. Diese Assemblies werden in einem temporären Verzeichnis für Dateien erstellt. Wenn ein anderer Prozess oder Benutzer über Zugriffsrechte für dieses Verzeichnis verfügt, kann er den Serialisierungs-/Deserialisierungscode mit beliebigem Code überschreiben. XmlSerializer führt dann diesen Code mit dessen Sicherheitskontext anstelle des Serialisierungs-/Deserialisierungscode aus. Stellen Sie sicher, dass die Berechtigungen für das Verzeichnis temporärer Dateien ordnungsgemäß festgelegt sind, um dies zu verhindern.

Der XmlSerializer verfügt auch über einen Modus, in dem es vorgenerierte Serialisierungsassemblys verwendet, anstatt sie zur Laufzeit zu generieren. Dieser Modus wird ausgelöst, wenn die XmlSerializer eine geeignete Serialisierungsassembly finden kann. Die XmlSerializer überprüft, ob die Serialisierungsassembly mit demselben Schlüssel signiert wurde, der verwendet wurde, um die Assembly zu signieren, die die zu serialisierenden Typen enthält. Dies dient als Schutz vor schädlichen Assemblys, die als Serialisierungsassemblys getarnt sind. Wenn die Assembly, die Ihre serialisierbaren Typen enthält, jedoch nicht signiert ist, kann XmlSerializer diese überprüfung nicht durchführen und verwendet jede Assembly mit dem richtigen Namen. Dadurch wird das Ausführen von bösartigem Code möglich. Signieren Sie immer die Assemblys, die Ihre serialisierbaren Typen enthalten, oder kontrollieren Sie den Zugriff auf das Verzeichnis Ihrer Anwendung und den globalen Assemblycache, um die Einführung bösartiger Assemblys zu verhindern.

Dies XmlSerializer kann einem Denial-of-Service-Angriff unterliegen. XmlSerializer hat kein MaxItemsInObjectGraph-Kontingent, wie es bei DataContractSerializer verfügbar ist. Daher wird eine beliebige Menge von Objekten deserialisiert, die nur durch die Nachrichtengröße begrenzt ist.

Bedrohungen bei teilweisem Vertrauen

Beachten Sie die folgenden Hinweise bezüglich Bedrohungen im Zusammenhang mit Code, der mit eingeschränkter Vertrauensstufe ausgeführt wird. Diese Bedrohungen umfassen bösartigen teilweise vertrauenswürdigen Code sowie bösartigen teilweise vertrauenswürdigen Code in Kombination mit anderen Angriffsszenarien (z. B. teilweise vertrauenswürdiger Code, der eine bestimmte Zeichenfolge erstellt und dann deserialisiert).

  • Wenn Sie Serialisierungskomponenten verwenden, bestätigen Sie niemals berechtigungen vor dieser Verwendung, auch wenn sich das gesamte Serialisierungsszenario im Gültigkeitsbereich Ihrer Assertion befindet und Sie keine nicht vertrauenswürdigen Daten oder Objekte behandeln. Diese Verwendung kann zu Sicherheitsrisiken führen.

  • In Fällen, in denen teilweise vertrauenswürdiger Code die Kontrolle über den Serialisierungsprozess hat, entweder über Erweiterungspunkte (Surrogates), serialisiert werdende Typen oder auf andere Weise kann der teilweise vertrauenswürdige Code dazu führen, dass der Serialisierer eine große Menge an Daten in den serialisierten Datenstrom ausgibt, was dazu führen kann, dass Denial of Service (DoS) an den Empfänger dieses Datenstroms übergeben wird. Wenn Sie Daten serialisieren, die für ein Ziel vorgesehen sind, das anfällig für DoS-Bedrohungen ist, sollten Sie keine teilweise vertrauenswürdigen Typen serialisieren und dem teilweise vertrauenswürdigen Code nicht die Kontrolle über die Serialisierung überlassen.

  • Wenn Sie zulassen, dass teilweise vertrauenswürdiger Code auf Ihre DataContractSerializer-Instanz zugreift oder die Datenvertragssurrogate auf andere Weise steuert, kann der Code ein hohes Maß an Kontrolle über den Serialisierungs-/Deserialisierungsprozess ausüben. So kann es beispielsweise beliebige Typen einfügen, zu Einer Offenlegung von Informationen führen, das resultierende Objektdiagramm oder serialisierte Daten manipulieren oder den resultierenden serialisierten Datenstrom überlaufen. Eine entsprechende NetDataContractSerializer Bedrohung wird im Abschnitt "Using the NetDataContractSerializer Securely" beschrieben.

  • Wenn das DataContractAttribute Attribut auf einen Typ angewendet wird (oder der Typ, der als SerializableAttribute aber nicht ISerializablegekennzeichnet ist), kann der Deserializer eine Instanz eines solchen Typs erstellen, auch wenn alle Konstruktoren nicht öffentlich oder durch Anforderungen geschützt sind.

  • Vertrauen Sie niemals dem Ergebnis der Deserialisierung, es sei denn, die zu deserialisierten Daten sind vertrauenswürdig, und Sie sind sicher, dass alle bekannten Typen Typen sind, denen Sie vertrauen. Beachten Sie, dass bekannte Typen nicht aus der Anwendungskonfigurationsdatei geladen werden, sondern aus der Computerkonfigurationsdatei kommen, wenn sie unter partiellen Vertrauensbedingungen ausgeführt werden.

  • Wenn Sie eine DataContractSerializer -Instanz mit einem Ersatzselektor übergeben, der im teilweise vertrauenswürdigen Modus hinzugefügt wurde, kann der Code alle änderbaren Einstellungen dieses Ersatzselektors ändern.

  • Bei einem deserialisierten Objekt sollten Sie das resultierende Objekt als nicht vertrauenswürdige Daten behandeln, wenn der XML-Reader oder die darin enthaltenen Daten aus teilweise vertrauenswürdigem Code stammen.

  • Die Tatsache, dass der ExtensionDataObject Typ keine öffentlichen Member hat, bedeutet nicht, dass Daten darin sicher sind. Wenn Sie beispielsweise Daten aus einer privilegierten Quelle in ein Objekt deserialisieren, in dem sich einige Daten befinden, und dieses Objekt dann an teilweise vertrauenswürdigen Code übergeben, kann dieser Code die Daten im ExtensionDataObject lesen, indem er das Objekt serialisiert. Erwägen Sie, IgnoreExtensionDataObject auf true zu setzen, wenn Sie aus einer privilegierten Datenquelle in ein Objekt deserialisieren, das später an teilweise vertrauenswürdigen Code übergeben wird.

  • DataContractSerializer und DataContractJsonSerializer unterstützen die Serialisierung von privaten, geschützten, internen und öffentlichen Membern mit vollständiger Vertrauenswürdigkeit. Bei teilweiser Vertrauenswürdigkeit können jedoch nur öffentliche Member serialisiert werden. Wenn eine Anwendung versucht, einen nicht öffentlichen Member zu serialisieren, wird eine SecurityException ausgelöst.

    Verwenden Sie das InternalsVisibleToAttribute -Assemblyattribut, um zuzulassen, dass interne oder geschützte interne Member serialisiert werden können. Mit diesem Attribut kann eine Assembly festlegen, dass ihre internen Mitglieder für eine andere Assembly sichtbar sind. In diesem Fall deklariert eine Assembly, deren interne Member serialisiert werden sollen, dass ihre internen Member für System.Runtime.Serialization.dll sichtbar sein sollen.

    Der Vorteil dieser Vorgehensweise besteht darin, dass kein Codegenerierungspfad mit erweiterten Berechtigungen erforderlich ist.

    Gleichzeitig gibt es zwei Hauptnachteile.

    Der erste Nachteil besteht darin, dass die Opt-In-Eigenschaft des InternalsVisibleToAttribute Attributs assemblyweit ist. Das heißt, Sie können nicht angeben, dass nur die internen Member einer bestimmten Klasse serialisiert werden sollen. Natürlich können Sie sich entscheiden, kein bestimmtes internes Element zu serialisieren, indem Sie diesem Element einfach kein Attribut hinzufügen DataMemberAttribute . Auf ähnliche Weise kann ein Entwickler auch entscheiden, ein Mitglied intern statt privat oder geschützt zu machen, mit geringfügigen Sichtbarkeitsbedenken.

    Der zweite Nachteil besteht darin, dass weiterhin keine privaten oder geschützten Mitglieder unterstützt werden.

    Betrachten Sie das folgende Programm, das die Verwendung des InternalsVisibleToAttribute -Attributs bei teilweiser Vertrauenswürdigkeit veranschaulicht:

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

    Im obigen Beispiel entspricht PermissionsHelper.InternetZone dem PermissionSet für teilweise Vertrauenswürdigkeit. Ohne das Attribut InternalsVisibleToAttribute tritt ein Anwendungsfehler auf. Dabei wird eine SecurityException ausgelöst, die angibt, dass nicht öffentliche Member im Modus „Teilweise vertrauenswürdig“ nicht serialisiert werden können.

    Wenn wir der Quelldatei jedoch die folgende Zeile hinzufügen, wird das Programm erfolgreich ausgeführt.

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

Weitere Überlegungen zur Zustandsverwaltung

Einige weitere Bedenken hinsichtlich der Objektzustandsverwaltung sind erwähnenswert:

  • Wenn Sie das streambasierte Programmiermodell mit einem Streaming-Transport verwenden, erfolgt die Verarbeitung der Nachricht, sobald die Nachricht eingeht. Der Absender der Nachricht kann den Sendevorgang in der Mitte des Datenstroms abbrechen und den Code in einem unvorhersehbaren Zustand lassen, wenn weitere Inhalte erwartet wurden. Verlassen Sie sich im Allgemeinen nicht darauf, dass der Datenstrom abgeschlossen ist, und führen Sie keine Arbeit in einem streambasierten Vorgang aus, der nicht zurückgesetzt werden kann, falls der Datenstrom abgebrochen wird. Das gilt auch für den Fall, dass eine Nachricht nach dem Streamingtext falsch formatiert ist (es fehlt z. B. ein Endtag für den SOAP-Umschlag oder es ist ein zweiter Nachrichtentext vorhanden).

  • Die Verwendung des IExtensibleDataObject Features kann dazu führen, dass vertrauliche Daten ausgegeben werden. Wenn Sie Daten aus einer nicht vertrauenswürdigen Quelle in Datenverträge mit IExtensibleObjectData übernehmen und sie später auf einem sicheren Kanal, auf dem Nachrichten signiert sind, erneut übertragen, bürgen Sie möglicherweise für Daten, über die Sie nichts wissen. Darüber hinaus ist der Gesamtzustand, den Sie senden, möglicherweise ungültig, wenn Sie sowohl die bekannten als auch unbekannten Datenteile berücksichtigen. Vermeiden Sie diese Situation, indem Sie entweder die Erweiterungsdateneigenschaft selektiv auf null oder durch selektives Deaktivieren des IExtensibleObjectData Features festlegen.

Schemaimport

Normalerweise erfolgt der Importvorgang des Schemas zum Generieren von Typen nur zur Entwurfszeit, z. B. bei Verwendung des ServiceModel Metadata Utility Tool (Svcutil.exe) in einem Webdienst, um eine Clientklasse zu generieren. In komplexeren Szenarien können Sie jedoch das Schema zur Laufzeit verarbeiten. Beachten Sie, dass Sie dadurch Denial-of-Service-Risiken ausgesetzt sein können. Einige Schemas können lange dauern, bis sie importiert werden. Verwenden Sie niemals die XmlSerializer Schemaimportkomponente in solchen Szenarien, wenn Schemas möglicherweise aus einer nicht vertrauenswürdigen Quelle stammen.

Spezifische Bedrohungen für ASP.NET AJAX-Integration

Wenn der Benutzer WebScriptEnablingBehavior oder WebHttpBehavior implementiert, stellt WCF einen Endpunkt bereit, der sowohl XML- als auch JSON-Nachrichten akzeptieren kann. Es gibt jedoch nur eine Gruppe von Lesekontingenten, die sowohl vom XML-Reader als auch vom JSON-Reader verwendet werden. Einige Kontingenteinstellungen sind möglicherweise für einen Leser geeignet, aber zu groß für die andere.

Bei der Implementierung WebScriptEnablingBehaviorhat der Benutzer die Möglichkeit, einen JavaScript-Proxy am Endpunkt verfügbar zu machen. Die folgenden Sicherheitsprobleme müssen berücksichtigt werden:

  • Informationen zum Dienst (Vorgangsnamen, Parameternamen usw.) können durch Untersuchen des JavaScript-Proxys abgerufen werden.

  • Wenn Sie den JavaScript-Endpunkt verwenden, werden vertrauliche und private Informationen möglicherweise im Clientwebbrowsercache aufbewahrt.

Hinweis zu Komponenten

WCF ist ein flexibles und anpassbares System. Die meisten Inhalte dieses Themas konzentrieren sich auf die am häufigsten verwendeten WCF-Verwendungsszenarien. Es ist jedoch möglich, Komponenten zu verfassen, die WCF auf viele verschiedene Arten bereitstellt. Es ist wichtig, die Sicherheitsauswirkungen der Verwendung der einzelnen Komponenten zu verstehen. Im Besonderen:

  • Wenn Sie XML-Reader verwenden müssen, verwenden Sie die Leser, die die XmlDictionaryReader Klasse bereitstellt, im Gegensatz zu anderen Lesern. Sichere Leser werden mit CreateTextReader, CreateBinaryReaderoder CreateMtomReader Methoden erstellt. Verwenden Sie die Create Methode nicht. Konfigurieren Sie die Leser immer mit sicheren Kontingenten. Die Serialisierungsmodule in WCF sind nur sicher, wenn sie mit sicheren XML-Readern aus WCF verwendet werden.

  • Wenn Sie DataContractSerializer verwenden, um potenziell nicht vertrauenswürdige Daten zu deserialisieren, legen Sie immer die MaxItemsInObjectGraph Eigenschaft fest.

  • Legen Sie beim Erstellen einer Nachricht den maxSizeOfHeaders Parameter fest, wenn MaxReceivedMessageSize nicht genügend Schutz geboten wird.

  • Konfigurieren Sie beim Erstellen eines Encoders immer die relevanten Kontingente, z. B. MaxSessionSize und MaxBufferSize.

  • Wenn Sie einen XPath-Nachrichtenfilter verwenden, setzen Sie das NodeQuota, um die Anzahl der XML-Knoten zu begrenzen, die der Filter besucht. Verwenden Sie keine XPath-Ausdrücke, deren Berechnung lange dauert, ohne dass viele Knoten durchlaufen werden.

  • Im Allgemeinen sollten Sie bei Verwendung einer Komponente, die ein Kontingent akzeptiert, ihre Sicherheitsauswirkungen verstehen und auf einen sicheren Wert festlegen.

Siehe auch