Leitfaden zur Bedrohungsminderung für ASP.NET Core Blazor interaktives serverseitiges Rendering

Hinweis

Dies ist nicht die neueste Version dieses Artikels. Informationen zum aktuellen Release finden Sie in der ASP.NET Core 8.0-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 für Querkanalangriffe auf die TLS-Verschlüsselung der Verbindung verfügbar machen wie beispielsweise CRIME- und BREACH-Angriffe. Diese Arten von Angriffen erfordern, dass der Angreifer:

  • Zwingen Sie einen Browser dazu, Anfragen mit einer Nutzlast, die der Angreifer kontrolliert, an eine verwundbare Website zu senden, indem Sie ein Formular seitenü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 Anwendung risikoanfällig ist, muss sie die Nutzdaten des Angreifers in der Antwort widerspiegeln, z. B. indem sie den Pfad oder den Abfrage-String in die Antwort schreibt. Mit der Länge der Antwort kann der Angreifer alle Informationen zur Antwort „erraten“, wobei die Verschlüsselung der Verbindung umgangen wird.

Im Allgemeinen können Blazor-Anwendungen die Komprimierung über die WebSocket-Verbindung mit geeigneten Sicherheitsmaßnahmen aktivieren:

  • Die Anwendung kann risikoanfällig sein, wenn sie Inhalte aus der Anforderung (z. B. den Pfad oder die Abfragezeichenfolge) verwendet, die von einem Angreifer 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 dem Angreifer die Möglichkeit gibt, den Angriff auszuführen.

  • Normalerweise muss die Anwendung für diese Art von Angriff den Inhalt der Antworten wiederholt reproduzieren, damit der Angreifer die Antwort erraten kann. In Anbetracht der Art und Weise, wie Blazor gerendert wird (es wird einmal gerendert und dann werden Diffs des Inhalts nur für die Elemente erstellt, die sich geändert haben), ist dies für einen Angreifer schwer zu erreichen. Es ist jedoch nicht unmöglich für einen Angreifer, daher muss darauf geachtet werden, dass vertrauliche Informationen zusammen mit externen Informationen gerendert werden, die von einem Angreifer 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.

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 Zusätzliche serverseitige Sicherheitsszenarien für Blazor in ASP.NET Core.

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 Zusätzliche serverseitige Sicherheitsszenarien für Blazor in ASP.NET Core.

Ein wichtiger Aspekt der serverseitigen Blazor-Sicherheit ist, dass der Benutzer, der an eine bestimmte Verbindung angefügt ist, möglicherweise irgendwann aktualisiert wird, nachdem die Blazor-Verbindung hergestellt wurde, der IHttpContextAccessor aber nicht aktualisiert wird. Weitere Informationen zum Beheben dieses Problems mit benutzerdefinierten Diensten finden Sie unter Zusätzliche serverseitige Sicherheitsszenarios für ASP.NET Core Blazor.

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.

Standardmäßig wird die Anzahl der Verbindungen pro Benutzer für eine App nicht automatisch beschränkt. 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).
    • Auf App-Ebene:

      • 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.
    • Auf Serverebene: Verwenden eines der App vorgeschalteten Proxys/Gateways. Mit Azure Front Door können Sie beispielsweise das globale Routing des Webdatenverkehrs zur App definieren, verwalten und überwachen. Das funktioniert, wenn Apps für lange Abrufe konfiguriert sind.

      Hinweis

      Obwohl lange Abrufe unterstützt werden, ist WebSockets das empfohlene Transportprotokoll. Azure Front Door unterstützt WebSockets derzeit nicht (Stand: Februar 2023), aber die Unterstützung von WebSockets befindet sich für ein zukünftiges Release des Diensts in Entwicklung. Weitere Informationen finden Sie unter Unterstützung von WebSocket-Verbindungen in Azure Front Door.

  • 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).
    • Auf App-Ebene:

      • 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.
    • Auf Serverebene: Verwenden eines der App vorgeschalteten Proxys/Gateways.

      Hinweis

      Obwohl lange Abrufe unterstützt werden, ist WebSockets das empfohlene Transportprotokoll.

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:

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 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 zuvor genannten Szenarios 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.
  • 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 countmehr 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.

Wächter gegen mehrere Sendungen

Wenn ein Ereignisrückruf einen zeitintensiven Vorgang wie das Abrufen von Daten aus einem externen Dienst oder einer Datenbank asynchron aufruft, sollten Sie möglicherweise einen Wächter einsetzen. Der Wächter kann verhindern, dass Benutzer mehrere Vorgänge an die Warteschlange senden, während der Vorgang bearbeitet und visuelles Feedback geliefert wird. Der folgende Komponentencode legt isLoading auf true fest, während DataService.GetDataAsync Daten vom Server abruft. Während isLoadingtrue 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 vorherigen Beispiel gezeigte Wächtermuster funktioniert, wenn der Hintergrundvorgang asynchron mit dem async-await-Muster ausgeführt wird.

Frühes Abbrechen und Vermeiden der Verwendung nach der Entfernung (use-after-dispose)

Zusätzlich zu einem Wächter (Abschnitt Wächter gegen mehrere Sendungen) sollten Sie auch ein Abbruchtoken (CancellationToken) verwenden, um zeitintensive Vorgänge nach dem Entfernen der Komponente abbrechen zu können. 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

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 aufgerufen 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:

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 kann ein böswilliger Benutzer den Browser zwingen, eine vom Angreifer kontrollierte Website aufzurufen. In diesem Szenario überlistet der Angreifer die App, sodass diese eine Benutzereingabe im Rahmen eines Aufrufs der NavigationManager.NavigateTo-Methode verwendet.

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.
  • Schützen Sie sich vor mehreren Sendungen.
  • 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.