Leitfaden zur Bedrohungsminderung für ASP.NET Core Blazor interaktives serverseitiges Rendering
Hinweis
Dies ist nicht die neueste Version dieses Artikels. Die aktuelle Version finden Sie in der .NET 9-Version dieses Artikels.
Warnung
Diese Version von ASP.NET Core wird nicht mehr unterstützt. Weitere Informationen finden Sie in der .NET- und .NET Core-Supportrichtlinie. Die aktuelle Version finden Sie in der .NET 9-Version dieses Artikels.
Wichtig
Diese Informationen beziehen sich auf ein Vorabversionsprodukt, das vor der kommerziellen Freigabe möglicherweise noch wesentlichen Änderungen unterliegt. Microsoft gibt keine Garantie, weder ausdrücklich noch impliziert, hinsichtlich der hier bereitgestellten Informationen.
Die aktuelle Version finden Sie in der .NET 9-Version dieses Artikels.
In diesem Artikel wird erläutert, wie Sicherheitsbedrohungen in interaktiven serverseitigen Blazor verringert werden.
Apps übernehmen ein zustandsbehaftetes Datenverarbeitungsmodell, in dem die Beziehung zwischen Server und Client langlebig ist. Der persistente Zustand wird durch einen Circuit aufrechterhalten, der mehrere potenziell langlebige Verbindungen umfassen kann.
Wenn ein Benutzer eine -Website besucht, erstellt der Server einen Circuit im Arbeitsspeicher des Servers. Der Circuit meldet dem Browser, welche Inhalte dieser rendern und wie dieser auf Ereignisse antworten soll, z. B. wenn der Benutzer in der Benutzeroberfläche auf eine Schaltfläche klickt. Für die Ausführung dieser Aktionen ruft ein Circuit JavaScript-Funktionen in den Browser- und .NET-Methoden des Benutzers auf dem Server auf. Diese bidirektionale JavaScript-basierte Interaktion wird als JavaScript-Interop (JS-Interop) bezeichnet.
Da ein JS-Interop eine Internetverbindung erfordert und der Client einen Remotebrowser verwendet, betreffen die meisten Sicherheitsprobleme bei Web-Apps alle Apps. In diesem Artikel werden häufige Bedrohungen für serverseitige Blazor-Apps beschrieben. Außerdem enthält er einen Leitfaden zur Bedrohungsminderung, der auf internetseitige Apps ausgerichtet ist.
In eingeschränkten Umgebungen wie Unternehmensnetzwerken oder Intranets:
- treffen einige der Hinweise aus diesem Artikel nicht zu
- rechnen sich die Implementierungskosten für einige der Hinweise aus diesem Artikel nicht, da das Sicherheitsrisiko in eingeschränkten Umgebungen gering ist.
Interaktive Serverkomponenten mit aktivierter WebSocket-Komprimierung
Die Komprimierung kann die Anwendung Seitenkanalangriffen auf die TLS-Verschlüsselung der Verbindung aussetzen wie beispielsweise CRIME- und BREACH-Angriffe. Diese Arten von Angriffen erfordern, dass der Cyberangreifende:
- Zwingen Sie einen Browser dazu, Anfragen mit einer Payload, die der Cyberangreifer kontrolliert, an eine anfällige Website zu senden, indem Sie ein Formular Website-übergreifend versenden oder die Website in einen Iframe einer anderen Website einbetten.
- Beobachten Sie die Länge der komprimierten und verschlüsselten Antwort über das Netzwerk.
Damit die App angreifbar ist, muss sie die Nutzlast des Cyberangreifernden in der Antwort widerspiegeln, z. B. indem sie den Pfad oder den Abfrage-String in die Antwort schreibt. Anhand der Länge der Antwort kann der Cyberangreifende alle Informationen in der Antwort „erraten“ und so die Verschlüsselung der Verbindung umgehen.
Im Allgemeinen können Blazor-Anwendungen die Komprimierung über die WebSocket-Verbindung mit geeigneten Sicherheitsmaßnahmen aktivieren:
Die Anwendung kann verwundbar sein, wenn sie Inhalte aus der Anfrage (z. B. den Pfad oder die Abfragezeichenfolge) übernimmt, die von Cyberangreifenden beeinflusst werden können, und diese in den HTML-Code der Seite einfügt oder auf andere Weise zum Bestandteil der Antwort macht.
Blazor wendet die folgenden Sicherheitsmaßnahmen automatisch an:
Wenn die Komprimierung konfiguriert ist, wird das Einbetten der Anwendung in einen iFrame automatisch von Blazor blockiert, wodurch die anfängliche (nicht komprimierte) Antwort vom Server beim Rendern blockiert und die WebSocket-Verbindung nicht mehr gestartet wird.
Die Beschränkung für die Einbettung der Anwendung in einen iframe kann gelockert werden. Eine Lockerung der Beschränkung macht die Anwendung jedoch angreifbar, wenn das einbettende Dokument über eine Cross-Site-Scripting-Schwachstelle kompromittiert wird, da dies Cyberangreifenden eine Möglichkeit zur Ausführung des Angriffs bietet.
Normalerweise muss die App für diese Art von Angriff den Inhalt der Antworten wiederholt wiedergeben, damit Cyberangreifende die Antwort erraten können. In Anbetracht der Art und Weise, wie Blazor gerendert wird (es wird einmal gerendert und erzeugt dann nur für die Elemente, die sich geändert haben, eine Differenzierung des Inhalts), ist dies für Cyberangreifende schwer zu bewerkstelligen. Für Cyberangreifende ist dies jedoch nicht unmöglich, weshalb darauf geachtet werden muss, dass sensible Informationen nicht zusammen mit externen Informationen dargestellt werden, die von Cyberangreifenden manipuliert werden können. Einige Beispiele:
Rendern von Personenbezogenen Informationen (Personally Identifiable Information, PII) auf der Seite gleichzeitig mit dem Rendern von Datenbankdaten, die von einem anderen Benutzer hinzugefügt wurden.
Rendering von PII-Informationen auf der Seite zur gleichen Zeit wie Daten, die von einem anderen Benutzer über JS Interop oder einen lokalen Singleton-Dienst auf dem Server kommen.
Im Allgemeinen empfehlen wir, dass Sie es vermeiden, Komponenten, die sensible Informationen enthalten, zusammen mit Komponenten zu rendern, die Daten aus nicht vertrauenswürdigen Quellen als Teil desselben Renderbatches rendern können. Nicht vertrauenswürdige Quellen umfassen Routenparameter, Abfragezeichenfolgen, Daten aus JS-Interoperabilität und andere Datenquellen, die ein Drittbenutzer steuern kann (Datenbanken, externe Dienste).
Freigegebener Zustand
Serverseitige Blazor-Apps befinden sich im Serverspeicher, und mehrere App-Sitzungen werden innerhalb desselben Prozesses gehostet. Blazor startet für jede App-Sitzung eine Verbindung mit einem eigenen Containerbereich für Abhängigkeitsinjektion, sodass bereichsbezogene Dienste pro Blazor-Sitzung eindeutig sind.
Warnung
Es wird nicht empfohlen, dass Apps auf demselben Server sich einen Zustand mithilfe von Singletondiensten teilen, es sei denn, es wird mit äußerster Vorsicht vorgegangen, da dies zu Sicherheitsrisiken führen kann, wie z. B. verbindungsübergreifende Preisgabe des Benutzerzustands.
Sie können zustandsbehaftete Singletondienste in Blazor-Apps verwenden, sofern diese speziell dafür konzipiert sind. Beispielsweise ist die Verwendung eines Singletonspeichercaches akzeptabel, da ein Speichercache einen Schlüssel für den Zugriff auf einen bestimmten Eintrag erfordert. Unter der Annahme, dass Benutzer keine Kontrolle über die Cacheschlüssel besitzen, die mit dem Cache verwendet werden, wird der im Cache gespeicherte Zustand nicht über Verbindungen hinweg weitergegeben.
Eine allgemeine Anleitung zur Zustandsverwaltung finden Sie unter ASP.NET Core Blazor-Zustandsverwaltung.
IHttpContextAccessor
/HttpContext
in Razor-Komponenten
IHttpContextAccessor muss mit interaktivem Rendering vermieden werden, da kein gültiger HttpContext
verfügbar ist.
IHttpContextAccessor kann für Komponenten verwendet werden, die auf dem Server statisch gerendert werden. Es wird jedoch empfohlen, dies nach Möglichkeit zu vermeiden.
HttpContext kann nur in statisch gerenderten Stammkomponenten für allgemeine Aufgaben als kaskadierender Parameter verwendet werden, z. B. beim Überprüfen und Ändern von Headern oder anderen Eigenschaften in der App
-Komponente (Components/App.razor
). Der Wert lautet immer null
zum interaktiven Rendern.
[CascadingParameter]
public HttpContext? HttpContext { get; set; }
Für Szenarios, in denen HttpContext in interaktiven Komponenten erforderlich ist, empfehlen wir, die Daten über den permanenten Komponentenstatus vom Server zu übertragen. Weitere Informationen finden Sie unter ASP.NET core serverseitigen und Blazor Web App zusätzlichen Sicherheitsszenarien.
Verwenden Sie IHttpContextAccessor/HttpContext nicht direkt oder indirekt in den Razor-Komponenten serverseitiger Blazor-Apps. Blazor-Apps werden außerhalb des ASP.NET Core-Pipelinekontexts ausgeführt. Die Verfügbarkeit von HttpContext in IHttpContextAccessor kann nicht sichergestellt, und es ist auch nicht gewährleistet, dass HttpContext den Kontext zum Starten der Blazor-App enthält.
Der empfohlene Ansatz für das Übergeben des Anforderungsstatus an die Blazor-App erfolgt über Stammkomponentenparameter während des anfänglichen Renderings der App. Alternativ kann die App die Daten in einen bereichsbezogenen Dienst im Initialisierungslebenszyklusereignis der Stammkomponente kopieren, um sie in der gesamten App zu verwenden. Weitere Informationen finden Sie unter ASP.NET core serverseitigen und Blazor Web App zusätzlichen Sicherheitsszenarien.
Ein wichtiger Aspekt der serverseitigen Blazor-Sicherheit ist, dass Benutzer:innen, die an eine bestimmte Verbindung angefügt sind, möglicherweise irgendwann aktualisiert werden, nachdem die Blazor-Verbindung hergestellt wurde, IHttpContextAccessor aber nicht aktualisiert wird. Weitere Informationen zur Behebung dieser Situation mit benutzerdefinierten Diensten finden Sie unter ASP.NET core serverseitigen und Blazor Web App zusätzlichen Sicherheitsszenarien.
Ressourcenerschöpfung
Ressourcenerschöpfung kann auftreten, wenn ein Client mit dem Server interagiert und eine exzessive Ressourcenverbrauch auf dem Server verursacht. Übermäßiger Ressourcenverbrauch beeinträchtigt maßgeblich die folgenden Komponenten:
Das Ziel von Denial-of-Service-Angriffen (DoS) ist es üblicherweise, die Ressourcen einer App oder eines Servers zu erschöpfen. Ressourcenerschöpfung ist jedoch nicht notwendigerweise das Ergebnis eines Angriffs auf das System. Endliche Ressourcen können z. B. aufgrund einer hohen Benutzernachfrage aufgebraucht werden. Weitere Informationen zu DoS-Angriffen finden Sie im Abschnitt zu DoS-Angriffen.
Blazor-externe Ressourcen wie Datenbanken und Dateihandles (für den Lese- und Schreibvorgänge in Dateien) können ebenso erschöpft werden. Weitere Informationen finden Sie unter Best Practices für ASP.NET Core.
CPU
CPU-Erschöpfung kann auftreten, wenn mindestens ein Client den Server zu CPU-intensiven Prozessen zwingt.
Ein Beispiel hierfür wäre eine App, die eine Fibonacci-Zahl berechnet. Fibonacci-Zahlen sind die Bestandteile einer Fibonnacci-Reihe, in der jede Zahl die Summe der beiden vorherigen Zahlen darstellt. Der für die Lösung erforderliche Arbeitsaufwand hängt von der Länge der Reihe und der Größe des ersten Wertes ab. Wenn die App die Clientanforderung nicht einschränkt, dominieren die CPU-intensiven Berechnungen unter Umständen die CPU-Zeit und schmälern die Leistung von anderen Tasks. Exzessiver Ressourcenverbrauch ist ein Sicherheitsproblem, das sich auf die Verfügbarkeit auswirkt.
CPU-Erschöpfung ist ein Problem, das alle öffentlich zugänglichen Apps betrifft. In regulären Web-Apps wird für Anforderungen und Verbindungen als Schutzvorkehrung ein Timeoutzeitraum festgelegt. In Blazor-Apps sind diese Schutzmaßnahmen jedoch nicht vorgesehen. Blazor-Apps müssen die richtigen Überprüfungen und Grenzwerte enthalten, bevor potenziell CPU-intensive Prozesse ausgeführt werden.
Arbeitsspeicher
Arbeitsspeichererschöpfung kann auftreten, wenn mindestens ein Client den Server zwingt, große Teile des Arbeitsspeichers zu belegen.
Ein Beispiel hierfür wäre eine App mit einer Komponente, die eine Elementliste akzeptiert und anzeigt. Wenn die Anzahl der zulässigen Elemente oder die Anzahl der an den Client gerenderten Elemente nicht durch die Blazor-App eingeschränkt wird, dominieren die arbeitsspeicherintensiven Verarbeitungs- und Renderingprozesse unter Umständen den Arbeitsspeicher des Servers und ziehen die Serverleistung in Mitleidenschaft. Der Server kann infolge abstürzen oder so langsam werden, dass es scheint, als sei er abgestürzt.
Stellen Sie sich das folgende Szenario vor, in dem eine Elementliste gewartet und angezeigt wird, die mit einer potenziellen Arbeitsspeichererschöpfung auf dem Server zusammenhängt:
- Die Elemente in einer
List<T>
-Eigenschaft oder einem solchen Feld nutzen den Arbeitsspeicher des Servers. Wenn die App zulässt, dass die Elementliste uneingeschränkt wächst, besteht die Gefahr, dass der Arbeitsspeicher des Servers irgendwann vollständig belegt ist. Wenn der Arbeitsspeicher erschöpft ist, wird die aktuelle Sitzung beendet (stürzt ab), und alle gleichzeitigen Sitzungen in dieser Serverinstanz empfangen eine Ausnahme mit einem Hinweis auf unzureichenden Arbeitsspeicher. Damit dieses Szenario nicht auftritt, muss die App eine Datenstruktur verwenden, die eine Elementobergrenze für gleichzeitige Benutzer durchsetzt. - Wenn beim Rendering kein Paginierungsschema verwendet wird, belegt der Server zusätzlichen Arbeitsspeicher für Objekte, die nicht in der Benutzeroberfläche angezeigt werden. Ohne eine Obergrenze für die Anzahl der Elemente kann der verfügbare Arbeitsspeicher durch die Nachfrage erschöpft werden. Dieses Szenario lässt sich mit einem der folgenden Ansätze verhindern:
- Verwenden von paginierten Listen beim Rendering
- Anzeigen der ersten 100 bis 1.000 Elemente und Durchsetzen, dass Benutzer Suchkriterien eingeben, um andere Elemente als die angezeigten zu finden
- In einem fortgeschrittenen Renderingszenario können Sie Listen oder Raster implementieren, die Virtualisierung unterstützen. Wenn Virtualisierung eingesetzt wird, rendern Listen nur eine Teilmenge der Elemente, die für den Benutzer aktuell sichtbar sind. Interagiert der Benutzer in der Benutzeroberfläche mit der Scrollleiste, rendert die Komponente nur diejenigen Elemente, die angezeigt werden sollen. Aktuell nicht angezeigte Elemente werden idealerweise im sekundären Speicher aufbewahrt. Sie können auch im Speicher abgelegt werden, was jedoch weniger empfehlenswert ist.
Hinweis
Blazor verfügt über integrierte Unterstützung für die Virtualisierung. Weitere Informationen finden Sie unter Razor-Komponentenvirtualisierung in ASP.NET Core.
Blazor-Apps bieten ein ähnliches Programmiermodell wie andere Benutzeroberflächenframeworks für zustandsbehaftete Apps wie WPF, Windows Forms oder Blazor WebAssembly. Der Hauptunterschied besteht darin, dass der von der App benutzte Arbeitsspeicher in vielen Benutzeroberflächenframeworks dem Client gehört und sich die Auswirkungen daher auf den einzelnen Client beschränken. Eine Blazor WebAssembly-App wird z. B. vollständig auf dem Client ausgeführt und verwendet ausschließlich die Arbeitsspeicherressourcen des Clients. Bei einer serverseitigen Blazor-App gehört der von der App verbrauchte Arbeitsspeicher dem Server und wird von den Clients auf der Serverinstanz gemeinsam genutzt.
Der serverseitige Arbeitsspeicherbedarf muss bei allen serverseitigen Blazor-Apps berücksichtigt werden. Die meisten Web-Apps sind jedoch zustandslos, und der für die Verarbeitung einer Anforderung belegte Arbeitsspeicher wird wieder freigegeben, nachdem die Antwort zurückgegeben wurde. Es ist allgemein empfehlenswert, es Clients nicht zu erlauben, den Arbeitsspeicher uneingeschränkt zu belegen, so wie bei anderen serverseitigen Apps mit dauerhaften Clientverbindungen auch. Der von einer serverseitigen Blazor-App verbrauchte Arbeitsspeicher bleibt auch nach der Verarbeitung einer einzelnen Anforderung belegt.
Hinweis
Während der Entwicklung kann mithilfe eines Profilers oder einer Überwachung der Arbeitsspeicherbedarf von Clients bewertet werden. Ein Profiler oder eine Überwachung erfassen jedoch nicht den von einem bestimmten Client belegten Arbeitsspeicher. Wenn Sie den Arbeitsspeicherverbrauch eines bestimmten Clients während der Entwicklung bestimmen möchten, müssen Sie eine Sicherungskopie erfassen und den Arbeitsspeicherbedarf aller Objekte untersuchen, die sich im Circuit eines Benutzers befinden.
Clientverbindungen
Verbindungserschöpfung kann auftreten, wenn mindestens ein Client zu viele gleichzeitige Verbindungen zum Server geöffnet hat und so andere Clients daran hindert, neue Verbindungen herzustellen.
Blazor-Clients stellen eine Verbindung pro Sitzung her und halten diese so lange offen, wie auch das Browserfenster geöffnet ist. Da die Verbindungen dauerhaft und serverseitige Blazor-Apps zustandsbehaftet sind, stellt die Verbindungserschöpfung ein größeres Risiko für die Verfügbarkeit der App dar.
Für eine App gibt es keine Grenze für die Anzahl der Verbindungen pro Nutzer. Wenn die App eine Verbindungsobergrenze erfordert, sollten Sie mindestens eine der folgenden Maßnahmen ergreifen:
- Erzwingen der Authentifizierung, wodurch nicht autorisierten Benutzern automatisch die Möglichkeit genommen wird, eine Verbindung zur App herzustellen. Damit dieses Szenario eintritt, müssen Sie verhindern, dass Benutzer bei Bedarf neue Benutzer bereitstellen.
- Beschränken der pro Benutzer zulässigen Anzahl von Verbindungen. Verbindungen lassen sich mithilfe der folgenden Maßnahmen beschränken. Wägen Sie sorgfältig ab, bevor Sie legitimen Benutzern Zugriff auf die App erteilen (z. B., wenn eine Verbindungsobergrenze für die IP-Adresse eines Clients festgelegt wird).
- Ebene der Anwendung
- Erweiterung des Endpunktroutings
- Durchsetzen der Authentifizierung bei der Verbindungsherstellung zur App und Nachverfolgen der aktiven Sitzungen pro Benutzer
- Ablehnen neuer Sitzungen, wenn die Obergrenze erreicht wird
- Proxy-WebSocket-Verbindungen zu einer App mithilfe eines Proxys wie Azure SignalR Service. Dieser Dienst bündelt Client-zu-App-Verbindungen. Auf diese Weise kann eine App mehr Verbindungen akzeptieren, als ein einzelner Client herstellen kann. Dies verhindert, dass ein Client alle Verbindungen zum Server belegt.
- Serverebene
- Verwenden eines der App vorgeschalteten Proxys/Gateways. Zum Beispiel ist Azure Application Gateway ein Load Balancer für den Webverkehr (OSI Ebene 7), mit dem Sie den Verkehr zu Ihren Webanwendungen verwalten können. Weitere Informationen finden Sie unter Übersicht über die WebSocket-Unterstützung in Application Gateway.
- Obwohl Long Polling für Blazor Apps unterstützt wird, was die Verwendung von Azure Front Door ermöglichen würde, ist WebSockets das empfohlene Transportprotokoll. Ab September 2024 unterstützt Azure Front Door keine WebSockets, aber die Unterstützung für WebSockets wird in Betracht gezogen. Weitere Informationen finden Sie unter Unterstützung von WebSocket-Verbindungen in Azure Front Door.
- Ebene der Anwendung
- Erzwingen der Authentifizierung, wodurch nicht autorisierten Benutzern automatisch die Möglichkeit genommen wird, eine Verbindung zur App herzustellen. Damit dieses Szenario eintritt, müssen Sie verhindern, dass Benutzer bei Bedarf neue Benutzer bereitstellen.
- Beschränken der pro Benutzer zulässigen Anzahl von Verbindungen. Verbindungen lassen sich mithilfe der folgenden Maßnahmen beschränken. Wägen Sie sorgfältig ab, bevor Sie legitimen Benutzern Zugriff auf die App erteilen (z. B., wenn eine Verbindungsobergrenze für die IP-Adresse eines Clients festgelegt wird).
- Ebene der Anwendung
- Erweiterung des Endpunktroutings
- Durchsetzen der Authentifizierung bei der Verbindungsherstellung zur App und Nachverfolgen der aktiven Sitzungen pro Benutzer
- Ablehnen neuer Sitzungen, wenn die Obergrenze erreicht wird
- Proxy-WebSocket-Verbindungen zu einer App mithilfe eines Proxys wie Azure SignalR Service. Dieser Dienst bündelt Client-zu-App-Verbindungen. Auf diese Weise kann eine App mehr Verbindungen akzeptieren, als ein einzelner Client herstellen kann. Dies verhindert, dass ein Client alle Verbindungen zum Server belegt.
- Serverebene
- Verwenden eines der App vorgeschalteten Proxys/Gateways.
- Obwohl lange Abrufintervalle für Blazor-Apps unterstützt werden, ist WebSockets das empfohlene Transportprotokoll. Wir empfehlen, einen Proxy/Gateway zu wählen, der WebSockets unterstützt.
- Ebene der Anwendung
Denial-of-Service-Angriffe (DoS)
Bei Denial-of-Service-Angriffen (DoS) verursacht ein Client die Erschöpfung von mindestens einer Serverressource, sodass die App nicht mehr verfügbar ist. Blazor-Apps enthalten Standardgrenzwerte und nutzen andere Grenzwerte aus ASP.NET Core und SignalR, die für CircuitOptions festgelegt sind, um sich gegen DoS-Angriffe zu schützen:
- CircuitOptions.DisconnectedCircuitMaxRetained
- CircuitOptions.DisconnectedCircuitRetentionPeriod
- CircuitOptions.JSInteropDefaultCallTimeout
- CircuitOptions.MaxBufferedUnacknowledgedRenderBatches
- HubConnectionContextOptions.MaximumReceiveMessageSize
Weitere Informationen und Programmierbeispiele für Konfigurationen finden Sie in den folgenden Artikeln:
Interaktionen mit dem Browser (Client)
Ein Client interagiert über die von JS-Interop gesendeten Ereignisse und Meldungen zur Renderingfertigstellung mit dem Server. Die Kommunikation von JS-Interop verläuft bidirektional zwischen JavaScript und .NET:
- Browserereignisse werden asynchron vom Client an den Server gesendet.
- Der Server antwortet asynchron und rendert die Benutzeroberfläche nach Bedarf neu.
Über .NET aufgerufene JavaScript-Funktionen
Für .NET-Methoden, die JavaScript aufrufen:
- Für alle Aufrufe ist ein Timeout festgelegt. Dies bedeutet, dass Aufrufe fehlschlagen, sobald der Wert überschritten wird, und OperationCanceledException an den Aufrufer zurückgegeben wird.
- Das Standardtimeout für Aufrufe (CircuitOptions.JSInteropDefaultCallTimeout) liegt bei einer Minute. Weitere Informationen zum Konfigurieren dieses Grenzwerts finden Sie unter Aufrufen von JavaScript-Funktionen über .NET-Methoden in Blazor in ASP.NET Core.
- Ein Abbruchtoken kann bereitgestellt werden, um den Abbruch aufrufsweise zu steuern. Verwenden Sie nach Möglichkeit das Standardtimeout für Clientaufrufe. Ein Zeitintervall sollte nur festgelegt werden, wenn ein Abbruchtoken bereitgestellt wird.
- Das Ergebnis eines JavaScript-Aufrufs ist nicht vertrauenswürdig. Die Blazor-App-Client, der im Browser ausgeführt wird, sucht nach der aufzurufenden JavaScript-Funktion. Die Funktion wird aufgerufen, und es wird entweder das Ergebnis oder ein Fehler zurückgegeben. Ein böswilliger Client kann Folgendes versuchen:
- Verursachen eines Problems in der App, indem von der JavaScript-Funktion ein Fehler zurückgegeben wird
- Einführen eines unbeabsichtigten Verhaltens auf dem Server, indem ein unerwartetes Ergebnis von der JavaScript-Funktion zurückgegeben wird
Treffen Sie die folgenden Vorkehrungen, um sich gegen die oben genannten Szenarien zu schützen:
- Umschließen Sie Aufrufe von JS-Interop in
try-catch
-Anweisungen, um während der Aufrufe gegebenenfalls Fehler abzufangen. Weitere Informationen finden Sie unter Fehlerbehandlung in Blazor-Apps in ASP.NET Core. - Überprüfen Sie die Daten, die bei JS-Interop-Aufrufen zurückgegeben werden, einschließlich der Fehlermeldungen, bevor Sie etwas unternehmen.
Über den Browser aufgerufene .NET-Methoden
Vertrauen Sie keinen von JavaScript aus aufgerufenen .NET-Methoden. Wenn eine .NET-Methode für JavaScript verfügbar gemacht wird, sollten Sie sich ansehen, wie die .NET-Methode aufgerufen wird:
- Behandeln Sie jede .NET-Methode, die für JavaScript verfügbar gemacht wird, wie einen öffentlichen App-Endpunkt.
- Überprüfen Sie die Eingabe.
- Stellen Sie sicher, dass die Werte innerhalb der erwarteten Bereiche liegen.
- Stellen Sie sicher, dass der Benutzer dazu berechtigt ist, die angeforderte Aktion auszuführen.
- Belegen Sie durch den Aufruf der .NET-Methode nicht übermäßig viele Ressourcen. Führen Sie zum Beispiel Überprüfungen aus, und legen Sie Obergrenzen für die CPU- und Arbeitsspeicherauslastung fest.
- Beachten Sie, dass statische Methoden und Instanzmethoden für JavaScript-Clients verfügbar gemacht werden können. Zustände sollten nicht sitzungsübergreifend verwendet werden, es sei denn, das Design gibt dies vor, und es sind angemessene Beschränkungen vorgesehen.
- Für Instanzmethoden, die über DotNetObjectReference-Objekte verfügbar gemacht werden, die ursprünglich per Dependency Injection (DI) erstellt wurden, sollten die Objekte als bereichsbezogen registriert werden. Dies gilt für alle DI-Dienste, die die -App nutzt.
- Bei statischen Methoden sollten Sie vermeiden, einen Zustand herzustellen, der sich auf den Client festlegen lässt. Die einzige Ausnahme stellen Apps dar, in denen Zustände absichtlich für alle Benutzer auf einer Serverinstanz verwendet.
- Von Benutzern bereitgestellte Daten sollten nicht als Parameter für JavaScript-Aufrufe genutzt werden. Wenn das Übergeben von Daten in Parametern absolut notwendig ist, sollten Sie sicherstellen, dass der JavaScript-Code bei der Datenübergabe keine Gelegenheit für Cross-Site-Scripting (XSS) bietet. Schreiben Sie beispielsweise keine von Benutzern bereitgestellten Daten in das Dokumentobjektmodell (DOM), indem Sie die Eigenschaft
innerHTML
eines Elements festlegen. Außerdem können Sie mit Content Security Policy (zu Deutsch: „Richtlinie für Inhaltssicherheit“) ggf.eval
und andere unsichere primitive JavaScript-Datentypen deaktivieren. Weitere Informationen finden Sie unter Erzwingen einer Inhaltssicherheitsrichtlinie für Blazor in ASP.NET Core.
- Überprüfen Sie die Eingabe.
- Implementieren Sie keine benutzerdefinierte Sendung von .NET-Aufrufen zusätzlich zur Sendungsimplementierung des Frameworks. Das Verfügbarmachen von .NET-Methoden für den Browser ist ein fortgeschrittenes Szenario, das sich nicht als allgemeine Blazor-Entwicklungstechnik empfiehlt.
Ereignisse
Ereignisse stellen einen Einstiegspunkt in eine App dar. Für das Ereignishandling in Blazor-Apps gelten dieselben Regeln wie für das Schützen von Endpunkten in Web-Apps. Ein böswilliger Client kann beliebige Daten als Nutzlast für ein Ereignis senden.
Zum Beispiel:
- Ein Änderungsereignis für
<select>
könnte einen Wert senden, der nicht innerhalb der Optionen liegt, die die App dem Client präsentiert hat. <input>
könnte beliebige Textdaten an den Server senden und dabei die clientseitige Überprüfung umgehen.
Die App muss die Daten für alle Ereignisse überprüfen, die die App verarbeitet. Die Formularkomponenten des Blazor-Frameworks führen grundlegende Überprüfungen aus. Wenn die App benutzerdefinierte Formularkomponenten verwendet, müssen Benutzer Code schreiben, damit die Ereignisdaten richtig ausgewertet werden.
Ereignisse sind asynchron, wodurch mehrere Ereignisse an den Server gesendet werden können, bevor die App darauf reagieren und ein neues Rendering produzieren kann. Dies birgt einige Sicherheitsrisiken, die bedacht werden sollten. Clientaktionen in der App müssen innerhalb des Ereignishandlers beschränkt werden und dürfen nicht vom aktuell gerenderten Anzeigezustand abhängen.
Stellen Sie sich eine Zählerkomponente vor, mit der ein Benutzer einen Zähler höchstens dreimal erhöhen können soll. Die Schaltfläche zum Hochsetzen des Zählers basiert bedingungsweise auf dem Wert von count
:
<p>Count: @count</p>
@if (count < 3)
{
<button @onclick="IncrementCount" value="Increment count" />
}
@code
{
private int count = 0;
private void IncrementCount()
{
count++;
}
}
Ein Client kann mindestens ein Inkrementierungsereignis senden, bevor das Framework ein neues Rendering dieser Komponente generiert. Dies führt dazu, dass count
mehr als dreimal vom Benutzer inkrementiert werden kann, weil die Schaltfläche nicht schnell genug aus der Benutzeroberfläche entfernt wird. Die richtige Vorgehensweise für das Durchsetzen der Obergrenze von drei count
-Inkrementen wird im folgenden Beispiel veranschaulicht:
<p>Count: @count</p>
@if (count < 3)
{
<button @onclick="IncrementCount" value="Increment count" />
}
@code
{
private int count = 0;
private void IncrementCount()
{
if (count < 3)
{
count++;
}
}
}
Indem die Überprüfung if (count < 3) { ... }
innerhalb des Handlers hinzugefügt wird, wird die Entscheidung über das Inkrementieren von count
anhand des aktuellen App-Zustands getroffen. Die Entscheidung basiert nicht wie im vorherigen Beispiel auf dem Zustand der Benutzeroberfläche, der vorübergehen veraltet sein kann.
Schutz vor Mehrfachversand
Wenn ein Ereignisrückruf eine lange laufende Operation asynchron aufruft, wie z. B. das Abrufen von Daten aus einem externen Dienst oder einer Datenbank, sollten Sie eine Schutzmaßnahme in Betracht ziehen. Die Sicherheitsfunktion kann die Benutzenden daran hindern, mehrere Vorgänge in die Warteschlange einzureihen, während der Vorgang noch läuft, mit visueller Rückmeldung. Der folgende Komponentencode legt isLoading
auf true
fest, während DataService.GetDataAsync
Daten vom Server abruft. Während isLoading
true
ist, ist die Schaltfläche in der Benutzeroberfläche deaktiviert:
<button disabled="@isLoading" @onclick="UpdateData">Update</button>
@code {
private bool isLoading;
private Data[] data = Array.Empty<Data>();
private async Task UpdateData()
{
if (!isLoading)
{
isLoading = true;
data = await DataService.GetDataAsync(DateTime.Now);
isLoading = false;
}
}
}
Das im vorangegangenen Beispiel demonstrierte Schutzmuster funktioniert, wenn die Hintergrundoperation asynchron mit dem Muster async
-await
ausgeführt wird.
Frühes Abbrechen und Vermeiden der Verwendung nach der Entfernung (use-after-dispose)
Zusätzlich zur Verwendung einer Schutzmaßnahme, wie im Abschnitt Schutz vor Mehrfachversand beschrieben, sollte ein CancellationToken verwendet werden, um lang laufende Operationen abzubrechen, wenn die Komponente entsorgt wird. Dieser Ansatz bietet den zusätzlichen Vorteil, dass eine Verwendung nach der Entfernung (use-after-dispose) in Komponenten vermieden wird:
@implements IDisposable
...
@code {
private readonly CancellationTokenSource TokenSource =
new CancellationTokenSource();
private async Task UpdateData()
{
...
data = await DataService.GetDataAsync(DateTime.Now, TokenSource.Token);
if (TokenSource.Token.IsCancellationRequested)
{
return;
}
...
}
public void Dispose()
{
TokenSource.Cancel();
}
}
Vermeiden von großen Datenmengen produzierenden Ereignissen
Einige DOM-Ereignisse wie oninput
oder onscroll
können eine große Menge von Daten generieren. Diese Ereignisse sollten nicht in serverseitigen Blazor-Apps verwendet werden.
Zusätzlicher Sicherheitsleitfaden
Die folgenden Abschnitte in diesem Artikel enthalten einen Leitfaden zum Schutz von ASP.NET Core-Apps, der für serverseitige Blazor-Apps gilt:
- Protokollierung und vertrauliche Daten
- Schützen von Informationen während der Übertragung mit HTTPS
- Cross-Site-Scripting (XSS)
- Ursprungsübergreifender Schutz
- Clickjacking
- Open Redirects
Protokollierung und vertrauliche Daten
JS-Interop-Interaktionen zwischen dem Client und dem Server werden in den Serverprotokollen mit ILogger-Instanzen erfasst. In Blazor werden keine vertraulichen Informationen wie tatsächliche Ereignisse oder JS-Interop-Ein- und -Ausgaben protokolliert.
Wenn auf dem Server ein Fehler auftritt, benachrichtigt das Framework den Client und beendet die Sitzung. Der Client empfängt automatisch eine Standardfehlermeldung, die in den Entwicklertools des Browsers angezeigt werden kann.
Die clientseitige Fehlermeldung enthält weder die Aufrufliste noch Details zur Fehlerursache, die Serverprotokolle hingegen schon. Zu Entwicklungszwecken können vertrauliche Fehlerinformationen für den Client verfügbar gemacht werden, indem Sie detaillierte Fehlermeldungen aktivieren.
Warnung
Das Verfügbarmachen von Fehlerinformationen für internetseitige Clients ist ein Sicherheitsrisiko, dass immer vermieden werden sollte.
Schützen von Informationen während der Übertragung mit HTTPS
Blazor stellt die Kommunikation zwischen Client und Server mithilfe von SignalR bereit. Normalerweise verwendet Blazor den Transport, der von SignalR ausgehandelt wird. Dabei handelt es sich in der Regel um WebSockets.
Blazor bietet keine Garantie für die Integrität und Vertraulichkeit der Daten, die zwischen Server und Client gesendet werden. Verwenden Sie daher immer HTTPS.
Cross-Site-Scripting (XSS)
XSS ermöglicht es einem nicht autorisierten Benutzer, eine beliebige Logik im Kontext des Browsers auszuführen. Eine kompromittierte App kann beliebigen Code auf dem Client ausführen. Das Sicherheitsrisiko kann ausgenutzt werden, um eine Reihe schädlicher Aktionen für den Server auszuführen:
- Senden gefälschter/ungültiger Ereignisse an den Server
- Senden von fehlgeschlagenen/ungültigen Meldungen zur Renderingfertigstellung
- Vermeiden des Sendens von Meldungen zur Renderingfertigstellung
- Senden von Interop-Aufrufen aus JavaScript an .NET
- Ändern der Antwort von Interop-Aufrufen aus .NET an JavaScript
- Vermeiden des Sendens von .NET-Ergebnissen an JS-Interop.
Das Blazor-Framework bietet die folgenden Maßnahmen zum Schutz vor einigen der zuvor genannten Bedrohungen:
- Es werden keine neuen Benutzeroberflächenupdates generiert, wenn der Client die Renderbatches nicht bestätigt. Dies wird mit CircuitOptions.MaxBufferedUnacknowledgedRenderBatches konfiguriert.
- Für alle JavaScript-Aufrufe von .NET aus tritt nach einer Minute ein Timeout auf, ohne dass der Client eine Antwort sendet. Dies wird mit CircuitOptions.JSInteropDefaultCallTimeout konfiguriert.
- Während JS-Interop wird eine grundlegende Überprüfung für alle Eingaben ausgeführt, die aus dem Browser stammen:
- .NET-Verweise sind gültig und weisen den von der .NET-Methode erwarteten Typ auf.
- Die Daten sind nicht falsch formatiert.
- Die Nutzlast enthält die korrekte Anzahl von Argumenten für die Methode.
- Die Argumente oder das Ergebnis lässt sich korrekt deserialisieren, bevor die Methode aufgerufen wird.
- Eine grundlegende Überprüfung wird für alle Eingaben ausgeführt, die aus dem Browser aus gesendeten Ereignissen stammen:
- Das Ereignis weist einen gültigen Typ auf.
- Die Daten für das Ereignis lassen sich deserialisieren.
- Es gibt einen Ereignishandler, der dem Ereignis zugeordnet ist.
Zusätzlich zu den vom Framework implementierten Schutzmaßnahmen muss die App vom Entwickler so programmiert werden, dass sie gegen Bedrohungen geschützt ist und die angemessenen Aktionen ausführen kann:
- Beim Ereignishandling müssen Daten immer überprüft werden.
- Werden ungültige Daten empfangen, müssen die angemessenen Aktionen ausgeführt werden:
- Die Daten und die Rückgabe müssen ignoriert werden. Dies ermöglicht es der App, weiterhin Anforderungen zu verarbeiten.
- Wenn die App eine ungültige Eingabe erkennt, die nicht von einem legitimen Client stammen kann, wird eine Ausnahme zurückgegeben. Aufgrund der Ausnahme werden sowohl der Circuit als auch die Sitzung beendet.
- Vertrauen Sie nicht der Fehlermeldung, die in den Fertigstellungsmeldungen zum Batchrendering in den Protokollen enthalten ist. Der Fehler stammt vom Client und ist nicht unbedingt vertrauenswürdig, da der Client kompromittiert sein könnte.
- Vertrauen Sie nicht der Eingabe für JS-Interop-Aufrufe zwischen JavaScript und .NET-Methoden (beide Richtungen).
- Die App ist dafür zuständig, dass die Gültigkeit der Argumente und Ergebnisse überprüft wird, selbst wenn die Argumente oder Ergebnisse korrekt deserialisiert wurden.
Das Risiko von XSS besteht nur, wenn die App Benutzereingaben in die gerenderte Seite einbindet. Blazor führt einen Schritt zur Kompilierzeit aus, bei dem Markup in einer .razor
-Datei in eine prozedurale C#-Logik transformiert wird. Zur Laufzeit erstellt die C#-Logik eine Renderingstruktur, die die Elemente, den Text und die untergeordneten Komponenten beschreibt. Diese wird mithilfe mehrerer JavaScript-Anweisungen auf das DOM des Browsers angewendet (oder im Falle von Prerendering in HTML serialisiert):
- Benutzereingaben, die über eine normale Razor-Syntax gerendert werden (z. B.
@someStringValue
) stellen kein XSS-Risiko dar, weil die Razor-Syntax mithilfe von Befehlen, die nur Text schreiben können, dem DOM hinzugefügt wird. Selbst wenn der Wert HTML-Markup enthält, wird er als statischer Text angezeigt. Während des Prerenderings wird die Ausgabe HTML-codiert, wodurch der Inhalt ebenfalls als statischer Text angezeigt wird. - Skripttags sind nicht zulässig und sollten in der Komponentenrenderingstruktur der App nicht verwendet werden. Wenn Sie ein Skripttag das Markup einer Komponente aufnehmen, wird ein Kompilierzeitfehler zurückgegeben.
- Komponentenersteller können auch ohne Razor Komponenten in C# erstellen. Der Komponentenersteller ist dafür verantwortlich, das beim Ausgeben der Ausgabe die richtigen APIs benutzt werden. Es muss beispielsweise
builder.AddContent(0, someUserSuppliedString)
und nichtbuilder.AddMarkupContent(0, someUserSuppliedString)
verwendet werden, da letztere API das Risiko von XSS birgt.
Erwägen Sie, weitere Vorkehrungen gegen XSS-Sicherheitsrisiken zu treffen. Implementieren Sie beispielsweise eine restriktive Inhaltssicherheitsrichtlinie (Content Security Policy, CSP). Weitere Informationen finden Sie unter Erzwingen einer Inhaltssicherheitsrichtlinie für Blazor in ASP.NET Core.
Weitere Informationen finden Sie unter Verhindern von Cross-Site Scripting (XSS) in ASP.NET Core.
Ursprungsübergreifender Schutz
Bei ursprungsübergreifenden Angriffen führt ein Client, der einen anderen Ursprung hat, eine Aktion für den Server aus. Bei der böswilligen Aktion handelt es sich üblicherweise um eine GET-Anforderung oder eine Art von POST-Methode (Cross-Site Request Forgery, CSRF). Es ist jedoch ebenso möglich, dass eine böswillige WebSocket-Verbindung geöffnet wird. Blazor-Apps bieten dieselben Garantien wie jede andere SignalR-App, die das angebotene Hubprotokoll verwendet:
- Es besteht ursprungsübergreifender Zugriff auf Apps, sofern dies nicht durch zusätzliche Schutzmaßnahmen verhindert wird. Wenn Sie den ursprungsübergreifenden Zugriff deaktivieren möchten, müssen Sie entweder Cross-Origin Resource Sharing (CORS) im Endpunkt deaktivieren, indem Sie die CORS-Middleware der Pipeline und das DisableCorsAttribute-Objekt den Metadaten des Blazor-Endpunkts hinzufügen oder die zulässigen Ursprünge eingrenzen, indem Sie SignalR für Cross-Origin Resource Sharing konfigurieren. Anleitungen zu WebSocket-Ursprungseinschränkungen finden Sie unter WebSockets-Unterstützung in ASP.NET Core.
- Wenn CORS aktiviert ist, sind je nach CORS-Konfiguration unter Umständen weitere Schritte erforderlich, um die App zu schützen. Ist CORS global aktiviert, lässt sich der Mechanismus für den BlazorSignalR-Hub deaktivieren, indem die DisableCorsAttribute-Metadaten den Endpunktmetadaten hinzugefügt werden, nachdem MapBlazorHub im Endpunktroutengenerator aufgerufen wurde.
Weitere Informationen finden Sie unter Prevent Cross-Site Request Forgery (XSRF/CSRF) Attacks in ASP.NET Core (Verhindern von websiteübergreifenden Anforderungsfälschungen (XSRF/CSRF) in ASP.NET Core).
Clickjacking
Beim Clickjacking wird eine Website als <iframe>
innerhalb einer Website gerendert, die einen anderen Ursprung hat. So soll der Benutzer überlistet und dazu gebracht werden, Aktionen auf der angegriffenen Website auszuführen.
Mithilfe von Content Security Policy (CSP) und dem Header X-Frame-Options
können Sie eine App davor schützen, innerhalb eines Inlineframes (<iframe>
) gerendert zu werden.
Weitere Informationen finden Sie in den folgenden Ressourcen:
- Erzwingen einer Inhaltssicherheitsrichtlinie für Blazor in ASP.NET Core
- MDN-Webdokumentation: X-Frame-Optionen
Open Redirects
Wenn eine App-Sitzung beginnt, führt der Server eine grundlegende Validierung der URLs aus, die beim Sitzungsstart gesendet wurden. Das Framework überprüft, ob es sich bei der Basis-URL um eine übergeordnete URL der aktuellen handelt, bevor der Circuit hergestellt wird. Weitere Überprüfungen führt das Framework nicht aus.
Wenn ein Benutzer auf dem Client auf einen Link klickt, wird die Link-URL an den Server gesendet. Dieser entscheidet wiederum, welche Aktion anschließend ausgeführt wird. Die App kann beispielsweise eine clientseitige Navigation ausführen oder den Browser anweisen, einen neuen Inhalt aufzurufen.
Komponenten können Navigationsanforderungen mithilfe von NavigationManager auch programmgesteuert auslösen. In einem solchen Szenario kann die App eine clientseitige Navigation ausführen oder den Browser anweisen, einen neuen Inhalt aufzurufen.
Für Komponenten gilt Folgendes:
- Benutzereingaben dürfen nicht in Argumenten von Navigationsaufrufen verwendet werden.
- Argumente müssen überprüft werden, um sicherzustellen, dass das Ziel von der App erlaubt wird.
Andernfalls können böswilliger Benutzende den Browser zwingen, auf eine von Cyberangreifenden kontrollierte Website zu gehen. In diesem Szenario tricksen Cyberangreifende die Anwendung aus, indem sie eine Benutzereingabe als Teil des Aufrufs der Methode NavigationManager.NavigateTo verwenden.
Diese Empfehlung gilt auch, wenn Links als App-Inhalt gerendert werden:
- Wenn möglich, sollten Sie sich immer für relative Links entscheiden.
- Überprüfen Sie, ob die Ziele von absoluten Links gültig sind, bevor Sie die Links in eine Seite einbetten.
Weitere Informationen finden Sie unter Verhindern von Open-Redirect-Angriffen in ASP.NET Core.
Checkliste für die Sicherheit
Die folgende Liste von Sicherheitsmaßnahmen ist nicht abschließend:
- Überprüfen Sie die Argumente von Ereignissen.
- Überprüfen Sie die Eingaben und Ergebnisse aus JS-Interop-Aufrufen.
- Vermeiden Sie Benutzereingaben in JS-Interop-Aufrufen von .NET aus (oder überprüfen Sie diese vorher).
- Verhindern Sie, dass der Client unbegrenzt Arbeitsspeicher belegen kann.
- Daten innerhalb der Komponente
- DotNetObjectReference-Objekte, die an den Client zurückgegeben werden
- Schutz vor Mehrfachversand.
- Brechen Sie zeitintensive Vorgängen ab, wenn die Komponente entfernt wird.
- Vermeiden Sie Ereignisse, die große Datenmengen produzieren.
- Benutzereingaben sollten nie für NavigationManager.NavigateTo-Aufrufe verwendet werden, und überprüfen Sie Benutzereingaben für URLs zuerst auf zulässige Ursprünge, wenn dies unvermeidlich ist.
- Treffen Sie keine Autorisierungsentscheidungen auf Grundlage des Zustands der Benutzeroberfläche, sondern nur anhand des Komponentenzustands.
- Verwenden Sie ggf. Content Security Policy (CSP), um Apps vor XSS-Angriffen zu schützen. Weitere Informationen finden Sie unter Erzwingen einer Inhaltssicherheitsrichtlinie für Blazor in ASP.NET Core.
- Verwenden Sie ggf. CSP und X-Frame-Options, um Apps vor Clickjacking zu schützen.
- Stellen Sie sicher, dass die CORS-Einstellungen angemessen sind, wenn Sie CORS aktivieren, oder deaktivieren Sie CORS explizit für Blazor-Apps.
- Führen Sie Tests durch, um sicherzustellen, dass die serverseitigen Grenzwerte für die Blazor-App eine akzeptable Nutzungsqualität gewährleisten und keine nicht akzeptablen Risiken bergen.