Einführung in die Sicherheit von SignalR-Anwendungen (SignalR 1.x)
von Patrick Fletcher, Tom FitzMacken
Warnung
Diese Dokumentation ist nicht für die neueste Version von SignalR vorgesehen. Sehen Sie sich ASP.NET Core SignalR an.
In diesem Artikel werden die Sicherheitsprobleme beschrieben, die Sie beim Entwickeln einer SignalR-Anwendung berücksichtigen müssen.
Überblick
Dieses Dokument enthält folgende Abschnitte:
SignalR-Sicherheitskonzepte
Authentifizierung und Autorisierung
SignalR ist für die Integration in die vorhandene Authentifizierungsstruktur für eine Anwendung konzipiert. Es bietet keine Features für die Authentifizierung von Benutzern. Stattdessen authentifizieren Sie Benutzer wie gewohnt in Ihrer Anwendung und arbeiten dann mit den Ergebnissen der Authentifizierung in Ihrem SignalR-Code. Beispielsweise können Sie Ihre Benutzer mit ASP.NET Formularauthentifizierung authentifizieren und dann in Ihrem Hub erzwingen, welche Benutzer oder Rollen zum Aufrufen einer Methode autorisiert sind. In Ihrem Hub können Sie auch Authentifizierungsinformationen wie benutzername oder ob ein Benutzer einer Rolle angehört, an den Client übergeben.
SignalR stellt das Authorize-Attribut bereit, um anzugeben, welche Benutzer Zugriff auf einen Hub oder eine Methode haben. Sie wenden das Authorize-Attribut entweder auf einen Hub oder auf bestimmte Methoden in einem Hub an. Ohne das Authorize-Attribut sind alle öffentlichen Methoden auf dem Hub für einen Client verfügbar, der mit dem Hub verbunden ist. Weitere Informationen zu Hubs finden Sie unter Authentifizierung und Autorisierung für SignalR Hubs.
Das Authorize
Attribut wird nur mit Hubs verwendet. Um Autorisierungsregeln bei Verwendung eines PersistentConnection
zu erzwingen, müssen Sie die AuthorizeRequest
-Methode überschreiben. Weitere Informationen zu persistenten Verbindungen finden Sie unter Authentifizierung und Autorisierung für persistente SignalR-Verbindungen.
Verbindungstoken
SignalR verringert das Risiko, böswillige Befehle auszuführen, indem die Identität des Absenders überprüft wird. Zwischen Client und Server wird für jede Anforderung ein Verbindungstoken übergeben, das die Verbindungs-ID und den Benutzernamen für authentifizierte Benutzer enthält. Die Verbindungs-ID ist ein eindeutiger Bezeichner, der vom Server zufällig generiert wird, wenn eine neue Verbindung erstellt wird und für die Dauer der Verbindung beibehalten wird. Der Benutzername wird vom Authentifizierungsmechanismus für die Webanwendung bereitgestellt. Das Verbindungstoken wird durch Verschlüsselung und eine digitale Signatur geschützt.
Für jede Anforderung überprüft der Server den Inhalt des Tokens, um sicherzustellen, dass die Anforderung vom angegebenen Benutzer stammt. Der Benutzername muss der Verbindungs-ID entsprechen. Durch die Überprüfung sowohl der Verbindungs-ID als auch des Benutzernamens verhindert SignalR, dass ein böswilliger Benutzer leicht die Identität eines anderen Benutzers annehmen kann. Wenn der Server das Verbindungstoken nicht überprüfen kann, schlägt die Anforderung fehl.
Da die Verbindungs-ID Teil des Überprüfungsprozesses ist, sollten Sie die Verbindungs-ID eines Benutzers anderen Benutzern nicht preisgeben oder den Wert auf dem Client speichern, z. B. in einem Cookie.
Erneutes Beitreten von Gruppen beim erneuten Herstellen der Verbindung
Standardmäßig weist die SignalR-Anwendung einen Benutzer automatisch den entsprechenden Gruppen zu, wenn die Verbindung nach einer vorübergehenden Unterbrechung wiederhergestellt wird, z. B. wenn eine Verbindung unterbrochen und erneut hergestellt wird, bevor das Verbindungstimeout auftritt. Beim erneuten Herstellen der Verbindung übergibt der Client ein Gruppentoken, das die Verbindungs-ID und die zugewiesenen Gruppen enthält. Das Gruppentoken wird digital signiert und verschlüsselt. Der Client behält die gleiche Verbindungs-ID nach einer erneuten Verbindung bei. Daher muss die verbindungs-ID, die vom erneut verbundenen Client übergeben wurde, mit der vorherigen Verbindungs-ID übereinstimmen, die vom Client verwendet wurde. Diese Überprüfung verhindert, dass ein böswilliger Benutzer Anforderungen zum Beitreten nicht autorisierter Gruppen übergibt, wenn die Verbindung wiederhergestellt wird.
Beachten Sie jedoch, dass das Gruppentoken nicht abläuft. Wenn ein Benutzer in der Vergangenheit zu einer Gruppe gehört, aber für diese Gruppe gesperrt wurde, kann dieser Benutzer möglicherweise ein Gruppentoken nachahmen, das die unzulässige Gruppe enthält. Wenn Sie sicher verwalten müssen, welche Benutzer zu welchen Gruppen gehören, müssen Sie diese Daten auf dem Server speichern, z. B. in einer Datenbank. Fügen Sie dann Ihrer Anwendung Logik hinzu, die auf dem Server überprüft, ob ein Benutzer zu einer Gruppe gehört. Ein Beispiel für die Überprüfung der Gruppenmitgliedschaft finden Sie unter Arbeiten mit Gruppen.
Die automatische Wiedereingliederung von Gruppen gilt nur, wenn eine Verbindung nach einer vorübergehenden Unterbrechung wiederhergestellt wird. Wenn ein Benutzer die Verbindung trennt, indem er von der Anwendung navigiert, oder die Anwendung neu gestartet wird, muss ihre Anwendung behandeln, wie dieser Benutzer den richtigen Gruppen hinzugefügt werden kann. Weitere Informationen finden Sie unter Arbeiten mit Gruppen.
So verhindert SignalR die standortübergreifende Anforderungsfälschung
Cross-Site Request Forgery (CSRF) ist ein Angriff, bei dem eine böswillige Website eine Anforderung an einen anfälligen Standort sendet, auf dem der Benutzer derzeit angemeldet ist. SignalR verhindert CSRF, da es extrem unwahrscheinlich ist, dass eine böswillige Website eine gültige Anforderung für Ihre SignalR-Anwendung erstellt.
Beschreibung des CSRF-Angriffs
Hier sehen Sie ein Beispiel für einen CSRF-Angriff:
Ein Benutzer meldet sich mithilfe der Formularauthentifizierung bei
www.example.com
an.Der Server authentifiziert den Benutzer. Die Antwort vom Server enthält ein Authentifizierungscookies.
Ohne Sich abmelden, besucht der Benutzer eine böswillige Website. Diese böswillige Website enthält das folgende HTML-Formular:
<h1>You Are a Winner!</h1> <form action="http://example.com/api/account" method="post"> <input type="hidden" name="Transaction" value="withdraw" /> <input type="hidden" name="Amount" value="1000000" /> <input type="submit" value="Click Me"/> </form>
Beachten Sie, dass die Formularaktion Beiträge auf der anfälligen Website und nicht auf der böswilligen Website sendet. Dies ist der "websiteübergreifende" Teil von CSRF.
Der Benutzer klickt auf die Schaltfläche Senden. Der Browser enthält das Authentifizierungscookies mit der Anforderung.
Die Anforderung wird auf dem example.com Server mit dem Authentifizierungskontext des Benutzers ausgeführt und kann alles tun, was ein authentifizierter Benutzer tun darf.
Obwohl in diesem Beispiel der Benutzer auf die Formularschaltfläche klicken muss, kann die böswillige Seite genauso einfach ein Skript ausführen, das eine AJAX-Anforderung an Ihre SignalR-Anwendung sendet. Darüber hinaus verhindert die Verwendung von SSL keinen CSRF-Angriff, da die böswillige Website eine "https://"-Anforderung senden kann.
In der Regel sind CSRF-Angriffe auf Websites möglich, die Cookies für die Authentifizierung verwenden, da Browser alle relevanten Cookies an die Zielwebsite senden. CSRF-Angriffe sind jedoch nicht auf das Ausnutzen von Cookies beschränkt. Beispielsweise sind die Standard- und Digestauthentifizierung ebenfalls anfällig. Nachdem sich ein Benutzer mit der Standard- oder Digestauthentifizierung angemeldet hat, sendet der Browser die Anmeldeinformationen automatisch bis zum Ende der Sitzung.
CSRF-Entschärfungen von SignalR
SignalR führt die folgenden Schritte aus, um zu verhindern, dass eine böswillige Website gültige Anforderungen an Ihre SignalR-Anwendung erstellt. Diese Schritte werden standardmäßig ausgeführt und erfordern keine Aktion im Code.
- Deaktivieren von domänenübergreifenden Anforderungen
Standardmäßig sind domänenübergreifende Anforderungen in einer SignalR-Anwendung deaktiviert, um zu verhindern, dass Benutzer einen SignalR-Endpunkt aus einer externen Domäne aufrufen. Jede Anforderung, die von einer externen Domäne stammt, wird automatisch als ungültig betrachtet und blockiert. Es wird empfohlen, dieses Standardverhalten beizubehalten. Andernfalls könnte eine böswillige Website Benutzer dazu verleiten, Befehle an Ihre Website zu senden. Wenn Sie domänenübergreifende Anforderungen verwenden müssen, finden Sie weitere Informationen unter Herstellen einer domänenübergreifenden Verbindung . - Übergeben des Verbindungstokens in der Abfragezeichenfolge, nicht in einem Cookie
SignalR übergibt das Verbindungstoken als Abfragezeichenfolgenwert und nicht als Cookie. Wenn das Verbindungstoken nicht als Cookie gespeichert wird, wird das Verbindungstoken nicht versehentlich vom Browser weitergeleitet, wenn bösartiger Code gefunden wird. Außerdem wird das Verbindungstoken nicht über die aktuelle Verbindung hinaus beibehalten. Daher kann ein böswilliger Benutzer keine Anforderung unter den Authentifizierungsanmeldeinformationen eines anderen Benutzers stellen. - Überprüfen des Verbindungstokens
Wie im Abschnitt Verbindungstoken beschrieben, weiß der Server, welche Verbindungs-ID jedem authentifizierten Benutzer zugeordnet ist. Der Server verarbeitet keine Anforderung von einer Verbindungs-ID, die nicht mit dem Benutzernamen übereinstimmt. Es ist unwahrscheinlich, dass ein böswilliger Benutzer eine gültige Anforderung erraten könnte, da der böswillige Benutzer den Benutzernamen und die aktuelle zufällig generierte Verbindungs-ID kennen müsste. Diese Verbindungs-ID wird ungültig, sobald die Verbindung beendet wird. Anonyme Benutzer sollten keinen Zugriff auf vertrauliche Informationen haben.
SignalR-Sicherheitsempfehlungen
SSL-Protokoll (Secure Socket Layer)
Das SSL-Protokoll verwendet verschlüsselung, um den Transport von Daten zwischen einem Client und einem Server zu schützen. Wenn Ihre SignalR-Anwendung vertrauliche Informationen zwischen Client und Server überträgt, verwenden Sie SSL für den Transport. Weitere Informationen zum Einrichten von SSL finden Sie unter Einrichten von SSL in IIS 7.
Verwenden Sie keine Gruppen als Sicherheitsmechanismus.
Gruppen sind eine bequeme Möglichkeit, verwandte Benutzer zu sammeln, aber sie sind kein sicherer Mechanismus zum Einschränken des Zugriffs auf vertrauliche Informationen. Dies gilt insbesondere, wenn Benutzer während einer erneuten Verbindung automatisch Gruppen beitreten können. Erwägen Sie stattdessen, einer Rolle privilegierte Benutzer hinzuzufügen und den Zugriff auf eine Hubmethode nur auf Mitglieder dieser Rolle zu beschränken. Ein Beispiel für die Einschränkung des Zugriffs basierend auf einer Rolle finden Sie unter Authentifizierung und Autorisierung für SignalR Hubs. Ein Beispiel für die Überprüfung des Benutzerzugriffs auf Gruppen beim erneuten Herstellen der Verbindung finden Sie unter Arbeiten mit Gruppen.
Sichere Verarbeitung von Eingaben von Clients
Alle Eingaben von Clients, die für die Übertragung an andere Clients vorgesehen sind, müssen codiert werden, um sicherzustellen, dass ein böswilliger Benutzer kein Skript an andere Benutzer sendet. Es ist am besten, Nachrichten auf den empfangenden Clients und nicht auf dem Server zu codieren, da Ihre SignalR-Anwendung über viele verschiedene Arten von Clients verfügen kann. Daher funktioniert die HTML-Codierung für einen Webclient, aber nicht für andere Clienttypen. Beispielsweise würde eine Webclientmethode zum Anzeigen einer Chatnachricht den Benutzernamen und die Nachricht sicher verarbeiten, indem die html()
Funktion aufgerufen wird.
chat.client.addMessageToPage = function (name, message) {
// Html encode display name and message.
var encodedName = $('<div />').text(name).html();
var encodedMsg = $('<div />').text(message).html();
// Add the message to the page.
$('#discussion').append('<li><strong>' + encodedName
+ '</strong>: ' + encodedMsg + '</li>');
};
Abstimmung einer Änderung in der Benutzer-status mit einer aktiven Verbindung
Wenn sich die Authentifizierung eines Benutzers status ändert, während eine aktive Verbindung besteht, erhält der Benutzer eine Fehlermeldung, die besagt: "Die Benutzeridentität kann sich während einer aktiven SignalR-Verbindung nicht ändern." In diesem Fall sollte Ihre Anwendung erneut eine Verbindung mit dem Server herstellen, um sicherzustellen, dass die Verbindungs-ID und der Benutzername koordiniert sind. Wenn Ihre Anwendung beispielsweise ermöglicht, dass sich der Benutzer abmelden kann, während eine aktive Verbindung besteht, stimmt der Benutzername für die Verbindung nicht mehr mit dem Namen überein, der für die nächste Anforderung übergeben wird. Sie sollten die Verbindung beenden, bevor sich der Benutzer abmeldet, und sie dann neu starten.
Beachten Sie jedoch, dass die meisten Anwendungen die Verbindung nicht manuell beenden und starten müssen. Wenn Ihre Anwendung Benutzer nach dem Abmelden zu einer separaten Seite umleitet, z. B. das Standardverhalten in einer Web Forms- oder MVC-Anwendung, oder die aktuelle Seite nach dem Abmelden aktualisiert, wird die aktive Verbindung automatisch getrennt und erfordert keine zusätzliche Aktion.
Das folgende Beispiel zeigt, wie eine Verbindung beendet und gestartet wird, wenn sich der Benutzer status geändert hat.
<script type="text/javascript">
$(function () {
var chat = $.connection.sampleHub;
$.connection.hub.start().done(function () {
$('#logoutbutton').click(function () {
chat.connection.stop();
$.ajax({
url: "Services/SampleWebService.svc/LogOut",
type: "POST"
}).done(function () {
chat.connection.start();
});
});
});
});
</script>
Oder die Authentifizierungs-status des Benutzers kann sich ändern, wenn Ihre Website den gleitenden Ablauf mit der Formularauthentifizierung verwendet und es keine Aktivität gibt, um das Authentifizierungscookie gültig zu halten. In diesem Fall wird der Benutzer abgemeldet, und der Benutzername stimmt nicht mehr mit dem Benutzernamen im Verbindungstoken überein. Sie können dieses Problem beheben, indem Sie ein Skript hinzufügen, das regelmäßig eine Ressource auf dem Webserver anfordert, um das Authentifizierungscookies gültig zu halten. Das folgende Beispiel zeigt, wie sie alle 30 Minuten eine Ressource anfordern.
$(function () {
setInterval(function() {
$.ajax({
url: "Ping.aspx",
cache: false
});
}, 1800000);
});
Automatisch generierte JavaScript-Proxydateien
Wenn Sie nicht alle Hubs und Methoden für jeden Benutzer in die JavaScript-Proxydatei einschließen möchten, können Sie die automatische Generierung der Datei deaktivieren. Sie können diese Option auswählen, wenn Sie über mehrere Hubs und Methoden verfügen, aber nicht möchten, dass jeder Benutzer alle Methoden kennt. Sie deaktivieren die automatische Generierung, indem Sie EnableJavaScriptProxies auf false festlegen.
var hubConfiguration = new HubConfiguration();
hubConfiguration.EnableJavaScriptProxies = false;
RouteTable.Routes.MapHubs("/signalr", hubConfiguration);
Weitere Informationen zu den JavaScript-Proxydateien finden Sie unter Der generierte Proxy und was er für Sie tut.
Ausnahmen
Sie sollten das Übergeben von Ausnahmeobjekten an Clients vermeiden, da die Objekte vertrauliche Informationen für die Clients verfügbar machen können. Rufen Sie stattdessen eine Methode auf dem Client auf, die die relevante Fehlermeldung anzeigt.
public Task SampleMethod()
{
try
{
// code that can throw an exception
}
catch(Exception e)
{
// add code to log exception and take remedial steps
return Clients.Caller.DisplayError("Sorry, the request could not be processed.");
}
}