Sicherheitsüberlegungen in ASP.NET Core SignalR

Von Andrew Stanton-Nurse

Dieser Artikel enthält Informationen zum Sichern von SignalR.

Cross-Origin Resource Sharing

Cors (Cross Origin Resource Sharing) kann verwendet werden, um ursprungsübergreifende SignalR Verbindungen im Browser zuzulassen. Wenn JavaScript-Code in einer anderen Domäne als der SignalR App gehostet wird, muss CORS-Middleware aktiviert sein, damit JavaScript eine Verbindung mit der SignalR App herstellen kann. Lassen Sie ursprungsübergreifende Anforderungen nur von Domänen zu, denen Sie vertrauen oder die Sie steuern. Beispiel:

  • Ihre Website wird unter gehostet. http://www.example.com
  • Ihre SignalR App wird unter gehostet. http://signalr.example.com

CORS sollte in der SignalR App so konfiguriert werden, dass nur der Ursprung www.example.comzugelassen wird.

Weitere Informationen zum Konfigurieren von CORS finden Sie unter Enable Cross-Origin Requests (CORS). SignalRerfordert die folgenden CORS-Richtlinien:

  • Lassen Sie die spezifischen erwarteten Ursprünge zu. Das Zulassen eines beliebigen Ursprungs ist möglich, aber nicht sicher oder empfohlen.
  • HTTP-Methoden GET und POST müssen zulässig sein.
  • Anmeldeinformationen müssen zulässig sein, damit cookie-basierte festhältige Sitzungen ordnungsgemäß funktionieren. Sie müssen auch dann aktiviert werden, wenn die Authentifizierung nicht verwendet wird.

In Version 5.0 haben wir jedoch eine Option im TypeScript-Client bereitgestellt, um keine Anmeldeinformationen zu verwenden. Die Option, keine Anmeldeinformationen zu verwenden, sollte nur verwendet werden, wenn Sie zu 100 % wissen, dass Anmeldeinformationen wie Cookies in Ihrer App nicht benötigt werden (cookies werden von Azure App Service verwendet, wenn mehrere Server für persistente Sitzungen verwendet werden).

Die folgende CORS-Richtlinie ermöglicht z. B. einem SignalR in gehosteten https://example.com Browserclient den Zugriff auf die app, die SignalR unter https://signalr.example.comgehostet wird:

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    // ... other middleware ...

    // Make sure the CORS middleware is ahead of SignalR.
    app.UseCors(builder =>
    {
        builder.WithOrigins("https://example.com")
            .AllowAnyHeader()
            .WithMethods("GET", "POST")
            .AllowCredentials();
    });

    // ... other middleware ...
    app.UseRouting();

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapHub<ChatHub>("/chathub");
    });

    // ... other middleware ...
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    // ... other middleware ...

    // Make sure the CORS middleware is ahead of SignalR.
    app.UseCors(builder =>
    {
        builder.WithOrigins("https://example.com")
            .AllowAnyHeader()
            .WithMethods("GET", "POST")
            .AllowCredentials();
    });

    // ... other middleware ...

    app.UseSignalR(routes =>
    {
        routes.MapHub<ChatHub>("/chathub");
    });

    // ... other middleware ...
}

WebSocket-Ursprungseinschränkung

Der von CORS erzeugte Schutz gilt nicht für WebSockets. Informationen zur Ursprungseinschränkung für WebSockets finden Sie unter WebSockets-Ursprungseinschränkung.

Der von CORS erzeugte Schutz gilt nicht für WebSockets. Für Browser gilt Folgendes nicht:

  • Ausführen von CORS-Preflightanforderungen
  • Berücksichtigen der Einschränkungen, die in den Access-Control-Headern bei der Erstellung von WebSocket-Anforderungen angegeben sind

Allerdings senden Browser den Origin-Header, wenn die WebSocket-Anforderungen ausgegeben werden. Anwendungen sollten zur Überprüfung dieser Header konfiguriert werden, um sicherzustellen, dass nur WebSockets von den erwarteten Ursprüngen zulässig sind.

In ASP.NET Core Version 2.1 und höher kann die Headervalidierung mithilfe einer benutzerdefinierten Middleware vor und Authentifizierungsmiddleware UseSignalR in Configureerreicht werden:


// In Startup, add a static field listing the allowed Origin values:
private static readonly HashSet<string> _allowedOrigins = new HashSet<string>()
{
    // Add allowed origins here. For example:
    "https://www.mysite.com",
    "https://mysite.com",
};

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    // ... other middleware ...

    // Validate Origin header on WebSocket requests to prevent unexpected cross-site 
    // WebSocket requests.
    app.Use((context, next) =>
    {
        // Check for a WebSocket request.
        if (string.Equals(context.Request.Headers["Upgrade"], "websocket"))
        {
            var origin = context.Request.Headers["Origin"];

            // If there is an origin header, and the origin header doesn't match 
            // an allowed value:
            if (!string.IsNullOrEmpty(origin) && !_allowedOrigins.Contains(origin))
            {
                // The origin is not allowed, reject the request
                context.Response.StatusCode = (int) HttpStatusCode.Forbidden;
                return Task.CompletedTask;
            }
        }

        // The request is a valid Origin or not a WebSocket request, so continue.
        return next();
    });

    // ... other middleware ...

    app.UseSignalR(routes =>
    {
        routes.MapHub<ChatHub>("/chathub");
    });

    // ... other middleware ...
}

Hinweis

Der Origin-Header wird vom Client gesteuert und kann wie der Referer-Header überlistet werden. Diese Header sollten nicht als Authentifizierungsmechanismus verwendet werden.

ConnectionId

Das Verfügbarmachen ConnectionId kann zu einem böswilligen Identitätswechsel führen, wenn die SignalR Server- oder Clientversion ASP.NET Core Version 2.2 oder früher ist. Wenn der Server und die SignalR Clientversion ASP.NET Core Version 3.0 oder höher sind, muss anstelle ConnectionToken von ConnectionId geheim gehalten werden. Die ConnectionToken wird absichtlich in keiner API verfügbar gemacht. Es kann schwierig sein, sicherzustellen, dass ältere SignalR Clients keine Verbindung mit dem Server herstellen. Selbst wenn Ihre SignalR Serverversion ASP.NET Core 3.0 oder höher ist, sollte die ConnectionId nicht verfügbar gemacht werden.

Zugriffstokenprotokollierung

Bei Verwendung von WebSockets oder Server-Sent Events sendet der Browserclient das Zugriffstoken in der Abfragezeichenfolge. Das Empfangen des Zugriffstokens über eine Abfragezeichenfolge ist im Allgemeinen genauso sicher wie die Verwendung des Standardheaders Authorization . Verwenden Sie immer HTTPS, um eine sichere End-to-End-Verbindung zwischen dem Client und dem Server sicherzustellen. Viele Webserver protokollieren die URL für jede Anforderung, einschließlich der Abfragezeichenfolge. Die Protokollierung der URLs kann das Zugriffstoken protokollieren. ASP.NET Core protokolliert standardmäßig die URL für jede Anforderung, die die Abfragezeichenfolge enthält. Beispiel:

info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1]
      Request starting HTTP/1.1 GET http://localhost:5000/chathub?access_token=1234

Wenn Sie Bedenken hinsichtlich der Protokollierung dieser Daten mit Ihren Serverprotokollen haben, können Sie diese Protokollierung vollständig deaktivieren, indem Sie die Microsoft.AspNetCore.Hosting Protokollierung auf die Warning Ebene oder höher konfigurieren (diese Nachrichten werden auf Info ebener Ebene geschrieben). Weitere Informationen finden Sie unter Anwenden von Protokollfilterregeln im Code . Wenn Sie dennoch bestimmte Anforderungsinformationen protokollieren möchten, können Sie Middleware schreiben , um die benötigten Daten zu protokollieren und den access_token Abfragezeichenfolgenwert (sofern vorhanden) herauszufiltern.

Ausnahmen

Ausnahmemeldungen werden generell als vertrauliche Daten angesehen, die nicht für einen Client offengelegt werden dürfen. Standardmäßig werden die Details einer Ausnahme, die von einer Hubmethode ausgelöst wird, SignalR nicht an den Client gesendet. Stattdessen erhält der Client eine generische Nachricht, die angibt, dass ein Fehler aufgetreten ist. Die Übermittlung von Ausnahmemeldungen an den Client kann mit EnableDetailedErrors überschrieben werden (z. B. in der Entwicklung oder im Test). Ausnahmemeldungen sollten für den Client in Produktions-Apps nicht verfügbar gemacht werden.

Pufferverwaltung

SignalR verwendet Puffer pro Verbindung, um eingehende und ausgehende Nachrichten zu verwalten. Standardmäßig SignalR werden diese Puffer auf 32 KB beschränkt. Die größte Nachricht, die ein Client oder Server senden kann, ist 32 KB. Der maximale Arbeitsspeicherverbrauch einer Verbindung für Nachrichten beträgt 32 KB. Wenn Ihre Nachrichten immer kleiner als 32 KB sind, können Sie den Grenzwert verringern, was:

  • Verhindert, dass ein Client eine größere Nachricht senden kann.
  • Der Server muss niemals große Puffer zuordnen, um Nachrichten zu akzeptieren.

Wenn Ihre Nachrichten größer als 32 KB sind, können Sie den Grenzwert erhöhen. Das Erhöhen dieses Grenzwerts bedeutet Folgendes:

  • Der Client kann dazu führen, dass der Server große Speicherpuffer zuordnet.
  • Die Serverzuordnung von großen Puffern kann die Anzahl gleichzeitiger Verbindungen verringern.

Es gibt Grenzwerte für eingehende und ausgehende Nachrichten. Beide können für das httpConnectionDispatcherOptions-Objekt konfiguriert werden, das in MapHubkonfiguriert ist:

  • ApplicationMaxBufferSize stellt die maximale Anzahl von Bytes vom Client dar, die der Server puffert. Wenn der Client versucht, eine Nachricht zu senden, die diesen Grenzwert überschreitet, wird die Verbindung möglicherweise geschlossen.
  • TransportMaxBufferSize stellt die maximale Anzahl von Bytes dar, die der Server senden kann. Wenn der Server versucht, eine Nachricht (einschließlich Rückgabewerten von Hubmethoden) zu senden, die diesen Grenzwert überschreitet, wird eine Ausnahme ausgelöst.

Wenn Sie den Grenzwert auf festlegen, 0 wird der Grenzwert deaktiviert. Durch das Entfernen des Grenzwerts kann ein Client eine Nachricht beliebiger Größe senden. Böswillige Clients, die große Nachrichten senden, können dazu führen, dass überschüssiger Arbeitsspeicher zugeordnet wird. Eine übermäßige Speicherauslastung kann die Anzahl gleichzeitiger Verbindungen erheblich reduzieren.