Wprowadzenie do zabezpieczeń usługi SignalR

Autor: Patrick Fletcher, Tom FitzMacken

Ostrzeżenie

Ta dokumentacja nie jest przeznaczona dla najnowszej wersji usługi SignalR. Przyjrzyj się ASP.NET Core SignalR.

W tym artykule opisano problemy z zabezpieczeniami, które należy wziąć pod uwagę podczas tworzenia aplikacji usługi SignalR.

Wersje oprogramowania używane w tym temacie

Poprzednie wersje tego tematu

Aby uzyskać informacje o wcześniejszych wersjach usługi SignalR, zobacz SignalR Older Versions (Starsze wersje usługi SignalR).

Pytania i komentarze

Prześlij opinię na temat tego, jak podoba ci się ten samouczek i co możemy poprawić w komentarzach w dolnej części strony. Jeśli masz pytania, które nie są bezpośrednio związane z samouczkiem, możesz opublikować je na forum ASP.NET SignalR lub StackOverflow.com.

Omówienie

Ten dokument zawiera następujące sekcje:

Pojęcia dotyczące zabezpieczeń usługi SignalR

Uwierzytelnianie i autoryzacja

Usługa SignalR nie udostępnia żadnych funkcji uwierzytelniania użytkowników. Zamiast tego integrujesz funkcje usługi SignalR z istniejącą strukturą uwierzytelniania dla aplikacji. Użytkownicy są uwierzytelniani tak, jak zwykle w aplikacji, i pracować z wynikami uwierzytelniania w kodzie signalR. Możesz na przykład uwierzytelnić użytkowników za pomocą uwierzytelniania ASP.NET formularzy, a następnie w centrum wymusić, którzy użytkownicy lub role są autoryzowani do wywoływania metody. W centrum możesz również przekazać informacje uwierzytelniania, takie jak nazwa użytkownika lub czy użytkownik należy do roli, do klienta.

Usługa SignalR udostępnia atrybut Autoryzuj , aby określić, którzy użytkownicy mają dostęp do centrum lub metody. Atrybut Autoryzuj stosuje się do centrum lub określonych metod w centrum. Bez atrybutu Autoryzuj wszystkie publiczne metody w centrum są dostępne dla klienta połączonego z koncentratorem. Aby uzyskać więcej informacji na temat centrów, zobacz Authentication and Authorization for SignalR Hubs (Uwierzytelnianie i autoryzacja dla usług SignalR Hubs).

Atrybut jest Authorize stosowany do centrów, ale nie połączeń trwałych. Aby wymusić reguły autoryzacji w przypadku używania PersistentConnection metody , należy zastąpić metodę AuthorizeRequest . Aby uzyskać więcej informacji na temat połączeń trwałych, zobacz Uwierzytelnianie i autoryzacja dla połączeń trwałych usługi SignalR.

Token połączenia

Usługa SignalR ogranicza ryzyko wykonywania złośliwych poleceń, sprawdzając tożsamość nadawcy. Dla każdego żądania klient i serwer przekazują token połączenia, który zawiera identyfikator połączenia i nazwę użytkownika uwierzytelnionych użytkowników. Identyfikator połączenia jednoznacznie identyfikuje każdego połączonego klienta. Serwer losowo generuje identyfikator połączenia po utworzeniu nowego połączenia i utrzymuje ten identyfikator przez czas trwania połączenia. Mechanizm uwierzytelniania aplikacji internetowej udostępnia nazwę użytkownika. Usługa SignalR używa szyfrowania i podpisu cyfrowego do ochrony tokenu połączenia.

Diagram przedstawiający strzałkę ze strony Żądania nowego połączenia klienta do odebranego połączenia z serwerem do odpowiedzi serwera na odpowiedź odebraną przez klienta. System uwierzytelniania generuje token połączenia w polach Odpowiedź i Odebrano odpowiedź.

Dla każdego żądania serwer weryfikuje zawartość tokenu, aby upewnić się, że żądanie pochodzi od określonego użytkownika. Nazwa użytkownika musi odpowiadać identyfikatorowi połączenia. Sprawdzając zarówno identyfikator połączenia, jak i nazwę użytkownika, usługa SignalR uniemożliwia złośliwemu użytkownikowi łatwe personifikację innego użytkownika. Jeśli serwer nie może zweryfikować tokenu połączenia, żądanie zakończy się niepowodzeniem.

Diagram przedstawiający strzałkę od żądania klienta do odebranego żądania serwera do zapisanego tokenu. Token połączenia i komunikat znajdują się zarówno w polu Klient, jak i w polu Serwer.

Ponieważ identyfikator połączenia jest częścią procesu weryfikacji, nie należy ujawniać identyfikatora połączenia jednego użytkownika innym użytkownikom lub przechowywać wartość na kliencie, na przykład w pliku cookie.

Tokeny połączenia a inne typy tokenów

Tokeny połączeń są od czasu do czasu oflagowane przez narzędzia zabezpieczeń, ponieważ wydają się być tokenami sesji lub tokenami uwierzytelniania, co stanowi ryzyko, jeśli zostanie ujawnione.

Token połączenia usługi SignalR nie jest tokenem uwierzytelniania. Służy do potwierdzenia, że użytkownik wysyłający to żądanie jest taki sam, który utworzył połączenie. Token połączenia jest niezbędny, ponieważ usługa ASP.NET SignalR zezwala na przenoszenie połączeń między serwerami. Token kojarzy połączenie z określonym użytkownikiem, ale nie potwierdza tożsamości użytkownika wysyłającego żądanie. Aby żądanie usługi SignalR było prawidłowo uwierzytelnione, musi mieć inny token, który potwierdza tożsamość użytkownika, na przykład token pliku cookie lub elementu nośnego. Jednak sam token połączenia nie twierdzi, że żądanie zostało wykonane przez tego użytkownika, tylko że identyfikator połączenia zawarty w tokenie jest skojarzony z tym użytkownikiem.

Ponieważ token połączenia nie zapewnia własnego oświadczenia uwierzytelniania, nie jest uważany za token "sesji" lub "uwierzytelniania". Pobranie tokenu połączenia danego użytkownika i odtworzenie go w żądaniu uwierzytelnianym jako inny użytkownik (lub żądanie nieuwierzytelnione) zakończy się niepowodzeniem, ponieważ tożsamość użytkownika żądania i tożsamość przechowywana w tokenie nie będzie zgodna.

Ponowne dołączanie grup podczas ponownego nawiązywania połączenia

Domyślnie aplikacja SignalR automatycznie przypisze użytkownika do odpowiednich grup podczas ponownego nawiązywania połączenia z powodu tymczasowego zakłóceń, na przykład po usunięciu połączenia i ponownym nawiązaniu połączenia przed przekroczeniem limitu czasu połączenia. Podczas ponownego nawiązywania połączenia klient przekazuje token grupy, który zawiera identyfikator połączenia i przypisane grupy. Token grupy jest podpisany cyfrowo i zaszyfrowany. Klient zachowuje ten sam identyfikator połączenia po ponownym połączeniu; w związku z tym identyfikator połączenia przekazany z ponownie połączonego klienta musi być zgodny z poprzednim identyfikatorem połączenia używanym przez klienta. Ta weryfikacja uniemożliwia złośliwemu użytkownikowi przekazywanie żądań dołączenia do nieautoryzowanych grup podczas ponownego nawiązywania połączenia.

Należy jednak pamiętać, że token grupy nie wygasa. Jeśli użytkownik należał do grupy w przeszłości, ale został zakazany z tej grupy, ten użytkownik może być w stanie naśladować token grupy, który zawiera zabronione grupy. Jeśli musisz bezpiecznie zarządzać użytkownikami należącymi do tych grup, musisz przechowywać te dane na serwerze, na przykład w bazie danych. Następnie dodaj logikę do aplikacji, która weryfikuje na serwerze, czy użytkownik należy do grupy. Aby zapoznać się z przykładem weryfikowania członkostwa w grupie, zobacz Praca z grupami.

Automatyczne ponowne dołączanie grup ma zastosowanie tylko wtedy, gdy połączenie zostanie ponownie nawiązane po tymczasowym zakłóceniu. Jeśli użytkownik rozłącza się, przechodząc z dala od aplikacji lub ponownie uruchamianą aplikację, aplikacja musi obsługiwać sposób dodawania tego użytkownika do odpowiednich grup. Aby uzyskać więcej informacji, zobacz Praca z grupami.

Jak usługa SignalR uniemożliwia fałszerzowaniu żądań między lokacjami

Fałszerzować żądania między witrynami (CSRF) jest atakiem, w którym złośliwa witryna wysyła żądanie do podatnej na zagrożenia lokacji, w której użytkownik jest obecnie zalogowany. Usługa SignalR uniemożliwia csrF, co sprawia, że jest bardzo mało prawdopodobne, aby złośliwa witryna utworzyła prawidłowe żądanie dla aplikacji SignalR.

Opis ataku CSRF

Oto przykład ataku CSRF:

  1. Użytkownik loguje się do www.example.com przy użyciu uwierzytelniania formularzy.

  2. Serwer uwierzytelnia użytkownika. Odpowiedź z serwera zawiera plik cookie uwierzytelniania.

  3. Bez wylogowywania użytkownik odwiedza złośliwą witrynę internetową. Ta złośliwa witryna zawiera następujący formularz HTML:

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

    Zwróć uwagę, że akcja formularza publikuje w witrynie podatnej na zagrożenia, a nie złośliwą witrynę. Jest to część CSRF "cross-site".

  4. Użytkownik kliknie przycisk Prześlij. Przeglądarka zawiera plik cookie uwierzytelniania z żądaniem.

  5. Żądanie jest uruchamiane na serwerze example.com z kontekstem uwierzytelniania użytkownika i może wykonywać wszystkie czynności, które może wykonać uwierzytelniony użytkownik.

Mimo że ten przykład wymaga kliknięcia przycisku formularza przez użytkownika, złośliwa strona może równie łatwo uruchomić skrypt, który wysyła żądanie AJAX do aplikacji SignalR. Ponadto użycie protokołu SSL nie zapobiega atakowi CSRF, ponieważ złośliwa witryna może wysłać żądanie "https://".

Zazwyczaj ataki CSRF są możliwe w przypadku witryn internetowych, które używają plików cookie do uwierzytelniania, ponieważ przeglądarki wysyłają wszystkie odpowiednie pliki cookie do docelowej witryny sieci Web. Jednak ataki CSRF nie są ograniczone do wykorzystywania plików cookie. Na przykład uwierzytelnianie podstawowe i szyfrowane również jest podatne na zagrożenia. Po zalogowaniu się użytkownika przy użyciu uwierzytelniania podstawowego lub szyfrowego przeglądarka automatycznie wysyła poświadczenia do momentu zakończenia sesji.

Środki zaradcze CSRF podjęte przez usługę SignalR

Usługa SignalR wykonuje następujące kroki, aby zapobiec tworzeniu prawidłowych żądań do aplikacji przez złośliwą witrynę. Usługa SignalR domyślnie wykonuje te kroki, dlatego nie trzeba wykonywać żadnych działań w kodzie.

  • Wyłączanie żądań między domenami Usługa SignalR wyłącza żądania między domenami, aby uniemożliwić użytkownikom wywoływanie punktu końcowego usługi SignalR z domeny zewnętrznej. Usługa SignalR traktuje wszelkie żądania z domeny zewnętrznej jako nieprawidłowe i blokuje żądanie. Zalecamy zachowanie tego domyślnego zachowania; w przeciwnym razie złośliwa witryna może skłonić użytkowników do wysyłania poleceń do witryny. Jeśli chcesz użyć żądań między domenami, zobacz Jak ustanowić połączenie między domenami .
  • Przekazywanie tokenu połączenia w parametrach zapytania, a nie pliku cookie Usługa SignalR przekazuje token połączenia jako wartość parametrów zapytania, a nie jako plik cookie. Przechowywanie tokenu połączenia w pliku cookie jest niebezpieczne, ponieważ przeglądarka może przypadkowo przekazać token połączenia po napotkaniu złośliwego kodu. Ponadto przekazanie tokenu połączenia w parametrach zapytania uniemożliwia utrwalanie tokenu połączenia poza bieżącym połączeniem. W związku z tym złośliwy użytkownik nie może wysłać żądania w ramach poświadczeń uwierzytelniania innego użytkownika.
  • Weryfikowanie tokenu połączenia Zgodnie z opisem w sekcji Token połączenia serwer wie, który identyfikator połączenia jest skojarzony z każdym uwierzytelnionymi użytkownikami. Serwer nie przetwarza żadnych żądań z identyfikatora połączenia, który nie jest zgodny z nazwą użytkownika. Jest mało prawdopodobne, aby złośliwy użytkownik mógł odgadnąć prawidłowe żądanie, ponieważ złośliwy użytkownik musiałby znać nazwę użytkownika i bieżący losowo wygenerowany identyfikator połączenia. Ten identyfikator połączenia staje się nieprawidłowy zaraz po zakończeniu połączenia. Użytkownicy anonimowi nie powinni mieć dostępu do żadnych poufnych informacji.

Zalecenia dotyczące zabezpieczeń usługi SignalR

Protokół Secure Socket Layer (SSL)

Protokół SSL używa szyfrowania w celu zabezpieczenia transportu danych między klientem a serwerem. Jeśli aplikacja SignalR przesyła poufne informacje między klientem a serwerem, użyj protokołu SSL do transportu. Aby uzyskać więcej informacji na temat konfigurowania protokołu SSL, zobacz How to set up SSL on IIS 7 (Jak skonfigurować protokół SSL w usługach IIS 7).

Nie używaj grup jako mechanizmu zabezpieczeń

Grupy to wygodny sposób zbierania powiązanych użytkowników, ale nie są bezpiecznym mechanizmem ograniczania dostępu do poufnych informacji. Jest to szczególnie istotne, gdy użytkownicy mogą automatycznie ponownie dołączać grupy podczas ponownego nawiązywania połączenia. Zamiast tego rozważ dodanie uprzywilejowanych użytkowników do roli i ograniczenie dostępu do metody centrum tylko do członków tej roli. Aby zapoznać się z przykładem ograniczania dostępu na podstawie roli, zobacz Uwierzytelnianie i autoryzacja dla usługi SignalR Hubs. Aby zapoznać się z przykładem sprawdzania dostępu użytkowników do grup podczas ponownego nawiązywania połączenia, zobacz Praca z grupami.

Bezpieczne obsługa danych wejściowych od klientów

Aby upewnić się, że złośliwy użytkownik nie wysyła skryptu do innych użytkowników, należy zakodować wszystkie dane wejściowe od klientów przeznaczonych do emisji do innych klientów. Należy kodować komunikaty na klientach odbierających, a nie na serwerze, ponieważ aplikacja SignalR może mieć wiele różnych typów klientów. W związku z tym kodowanie HTML działa dla klienta internetowego, ale nie w przypadku innych typów klientów. Na przykład metoda klienta sieci Web umożliwiająca wyświetlenie komunikatu czatu bezpiecznie obsłuży nazwę użytkownika i komunikat przez wywołanie html() funkcji .

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>');
};

Uzgadnianie zmiany stanu użytkownika z aktywnym połączeniem

Jeśli stan uwierzytelniania użytkownika zmieni się, gdy istnieje aktywne połączenie, zostanie wyświetlony błąd informujący o błędzie "Tożsamość użytkownika nie może ulec zmianie podczas aktywnego połączenia usługi SignalR". W takim przypadku aplikacja powinna ponownie nawiązać połączenie z serwerem, aby upewnić się, że identyfikator połączenia i nazwa użytkownika są skoordynowane. Jeśli na przykład aplikacja umożliwia użytkownikowi wylogowanie się, gdy istnieje aktywne połączenie, nazwa użytkownika połączenia nie będzie już zgodna z nazwą przekazaną dla następnego żądania. Należy zatrzymać połączenie, zanim użytkownik wyloguje się, a następnie ponownie go uruchomić.

Należy jednak pamiętać, że większość aplikacji nie będzie musiała ręcznie zatrzymywać i uruchamiać połączenia. Jeśli aplikacja przekierowuje użytkowników do oddzielnej strony po wylogowaniu, na przykład domyślne zachowanie w aplikacji Web Forms lub aplikacji MVC albo odświeża bieżącą stronę po wylogowaniu, aktywne połączenie zostanie automatycznie rozłączone i nie wymaga żadnej dodatkowej akcji.

W poniższym przykładzie pokazano, jak zatrzymać i uruchomić połączenie po zmianie stanu użytkownika.

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

Lub stan uwierzytelniania użytkownika może ulec zmianie, jeśli witryna używa przesuwanego wygaśnięcia z uwierzytelnianiem formularzy i nie ma żadnych działań w celu zachowania poprawności pliku cookie uwierzytelniania. W takim przypadku użytkownik zostanie wylogowany, a nazwa użytkownika nie będzie już zgodna z nazwą użytkownika w tokenie połączenia. Ten problem można rozwiązać, dodając skrypt, który okresowo żąda zasobu na serwerze internetowym, aby zachować prawidłowy plik cookie uwierzytelniania. W poniższym przykładzie pokazano, jak zażądać zasobu co 30 minut.

$(function () {
    setInterval(function() {
        $.ajax({
            url: "Ping.aspx",
            cache: false
        });
    }, 1800000);
});

Automatycznie generowane pliki serwera proxy JavaScript

Jeśli nie chcesz uwzględniać wszystkich centrów i metod w pliku serwera proxy języka JavaScript dla każdego użytkownika, możesz wyłączyć automatyczne generowanie pliku. Możesz wybrać tę opcję, jeśli masz wiele centrów i metod, ale nie chcesz, aby każdy użytkownik wiedział o wszystkich metodach. Automatyczne generowanie można wyłączyć, ustawiając wartość EnableJavaScriptProxies na false.

var hubConfiguration = new HubConfiguration();
hubConfiguration.EnableJavaScriptProxies = false;
app.MapSignalR(hubConfiguration);

Aby uzyskać więcej informacji na temat plików serwera proxy języka JavaScript, zobacz Wygenerowany serwer proxy i co robi za Ciebie.

Wyjątki

Należy unikać przekazywania obiektów wyjątków do klientów, ponieważ obiekty mogą uwidaczniać poufne informacje klientom. Zamiast tego wywołaj metodę na kliencie, która wyświetla odpowiedni komunikat o błędzie.

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