Wprowadzenie do zabezpieczeń usługi SignalR (SignalR 1.x)

Fletcher Patryk, Tomasz FitzMacken

Warning

Ta dokumentacja nie jest najnowsza dla najnowszej wersji usługi sygnalizującej. Zapoznaj się z tematem ASP.NET Core sygnalizujący.

W tym artykule opisano problemy związane z zabezpieczeniami, które należy wziąć pod uwagę podczas tworzenia aplikacji sygnalizującej.

Omówienie

Ten dokument zawiera następujące sekcje:

Pojęcia dotyczące zabezpieczeń sygnałów

Uwierzytelnianie i autoryzacja

Sygnał został zaprojektowany do zintegrowania z istniejącą strukturą uwierzytelniania dla aplikacji. Nie udostępnia żadnych funkcji uwierzytelniania użytkowników. Zamiast tego uwierzytelniasz użytkowników jako normalnie w aplikacji, a następnie pracujesz z wynikami uwierzytelniania w kodzie sygnalizującym. Można na przykład uwierzytelniać użytkowników za pomocą uwierzytelniania ASP.NET Forms, a następnie w centrum, wymusić, którzy użytkownicy lub które role mają autoryzację, aby wywołać metodę. W centrum można także przekazać informacje uwierzytelniania, takie jak nazwa użytkownika lub czy użytkownik należy do roli, do klienta programu.

Sygnalizujący udostępnia atrybut Autoryzuj , aby określić, którzy użytkownicy mają dostęp do centrum lub metody. Należy zastosować atrybut Autoryzuj do koncentratora lub określonych metod w centrum. Bez atrybutu Autoryzuj wszystkie metody publiczne w centrum są dostępne dla klienta, który jest podłączony do centrum. Aby uzyskać więcej informacji na temat centrów, zobacz uwierzytelnianie i autoryzacja dla centrów sygnałów.

Atrybut Authorize jest używany tylko z koncentratorami. Aby wymusić reguły autoryzacji przy użyciu PersistentConnection 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.

Token połączenia

Program sygnalizujący zmniejsza ryzyko wykonania złośliwych poleceń przez zweryfikowanie tożsamości nadawcy. Token połączenia zawierający identyfikator i nazwę użytkownika dla uwierzytelnionych użytkowników jest przesyłany między klientem i serwerem dla każdego żądania. Identyfikator połączenia jest unikatowym identyfikatorem, który jest generowana losowo przez serwer w przypadku utworzenia nowego połączenia i jest utrwalany na czas trwania połączenia. Nazwa użytkownika jest udostępniana przez mechanizm uwierzytelniania dla aplikacji sieci Web. Token połączenia jest chroniony przy użyciu szyfrowania i podpisu cyfrowego.

Dla każdego żądania serwer sprawdza zawartość tokenu, aby upewnić się, że żądanie pochodzi od określonego użytkownika. Nazwa użytkownika musi odpowiadać identyfikatorowi połączenia. Sprawdzając poprawność zarówno identyfikatora połączenia, jak i nazwy użytkownika, sygnalizujący uniemożliwia złośliwemu użytkownikowi łatwe personifikowanie innego użytkownika. Jeśli serwer nie może zweryfikować tokenu połączenia, żądanie nie powiedzie się.

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

Ponowne łączenie grup po ponownym połączeniu

Domyślnie aplikacja sygnalizująca automatycznie ponownie przypisze użytkownika do odpowiednich grup podczas ponownego nawiązywania połączenia po tymczasowym przerwaniu, na przykład w przypadku porzucenia i ponownego ustanowienia połączenia przed upływem limitu czasu połączenia. Po ponownym nawiązaniu połączenia klient przekazuje token grupy zawierający identyfikator połączenia i przypisane grupy. Token grupy jest podpisany cyfrowo i szyfrowany. Klient zachowuje ten sam identyfikator połączenia po ponownej nawiązaniu połączenia; w związku z tym identyfikator połączenia przekazaną przez ponownie podłączony klient musi być zgodny z poprzednim identyfikatorem połączenia używanym przez klienta. Ta weryfikacja uniemożliwia złośliwemu użytkownikowi przekazanie żą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ży do grupy w przeszłości, ale został zabroniony z tej grupy, może być w stanie naśladować token grupy zawierający zabronioną grupę. Jeśli musisz bezpiecznie zarządzać użytkownikami należącymi do grup, musisz przechowywać te dane na serwerze, na przykład w bazie danych. Następnie należy dodać 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 przyłączanie grup ma zastosowanie tylko wtedy, gdy połączenie jest ponownie nawiązywane po tymczasowym przerwie. Jeśli użytkownik odłączy się przez przejście do aplikacji lub ponowne uruchomienie aplikacji, aplikacja musi obsłużyć procedurę dodawania tego użytkownika do odpowiednich grup. Aby uzyskać więcej informacji, zobacz Praca z grupami.

Jak sygnalizujący zapobiega podrabianiu żądań między lokacjami

Fałszerstwo żądania między lokacjami (CSRF) to atak polegający na tym, że złośliwa witryna wysyła żądanie do zagrożonej lokacji, w której użytkownik jest aktualnie zalogowany. Program sygnalizujący uniemożliwia CSRF, uniemożliwiając złośliwej lokacji utworzenie prawidłowego żądania dla aplikacji sygnalizującej.

Opis ataku CSRF

Oto przykład ataku CSRF:

  1. Użytkownik loguje się do www.example.comprzy 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ę sieci Web. Ta złośliwa witryna zawiera następującą postać 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>
    

    Należy zauważyć, że w ramach akcji formularza są umieszczane wpisy w zagrożonej witrynie, a nie w złośliwej witrynie. To jest część "wiele witryn" z CSRF.

  4. Użytkownik klika 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 mogą wykonać uwierzytelnionego użytkownika.

Mimo że ten przykład wymaga, aby użytkownik klikał przycisk formularza, złośliwa strona może jak równie łatwo uruchomić skrypt wysyłający żądanie AJAX do aplikacji sygnalizującej. Ponadto użycie protokołu SSL nie uniemożliwia ataku CSRF, ponieważ złośliwa witryna może wysłać żądanie "https://".

Zazwyczaj ataki CSRF są dostępne dla witryn sieci Web, 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 ograniczają się do korzystania z plików cookie. Na przykład uwierzytelnianie podstawowe i szyfrowane jest również zagrożone. Gdy użytkownik zaloguje się przy użyciu uwierzytelniania podstawowego lub szyfrowanego, przeglądarka automatycznie wyśle poświadczenia do momentu zakończenia sesji.

CSRF środki zaradcze wykonywane przez sygnalizującego

Program sygnalizujący wykonuje poniższe kroki, aby uniemożliwić złośliwym lokacjom Tworzenie prawidłowych żądań do aplikacji sygnalizującej. Te kroki są domyślnie podejmowane i nie wymagają żadnych akcji w kodzie.

  • Wyłącz żądania między domenami
    Domyślnie żądania między domenami są wyłączone w aplikacji sygnalizującej, aby uniemożliwić użytkownikom wywoływanie punktu końcowego sygnalizującego z domeny zewnętrznej. Wszystkie żądania, które pochodzą z domeny zewnętrznej, są automatycznie traktowane jako nieprawidłowe i zablokowane. Zalecane jest zachowanie tego zachowania domyślnego; w przeciwnym razie złośliwa witryna może dowolnych użytkowników do wysyłania poleceń do witryny. Jeśli konieczne jest użycie żądań między domenami, zobacz jak ustanowić połączenie między domenami .
  • Przekaż token połączenia w ciągu zapytania, a nie w pliku cookie
    Sygnał przekazuje token połączenia jako wartość ciągu zapytania, a nie jako plik cookie. W przypadku braku przechowywania tokenu połączenia jako pliku cookie token połączenia nie jest przypadkowo przekazywany przez przeglądarkę w przypadku napotkania złośliwego kodu. Ponadto token połączenia nie jest utrwalany poza bieżącym połączeniem. W związku z tym złośliwy użytkownik nie może wykonać żądania w ramach poświadczeń uwierzytelniania innego użytkownika.
  • Weryfikuj token połączenia
    Zgodnie z opisem w sekcji token połączenia serwer wie, który identyfikator połączenia jest skojarzony z każdym uwierzytelnionym użytkownikiem. Serwer nie przetwarza żadnych żądań z identyfikatora połączenia, który nie jest zgodny z nazwą użytkownika. Jest mało prawdopodobne, że złośliwy użytkownik może odgadnąć prawidłowe żądanie, ponieważ złośliwy użytkownik musi znać nazwę użytkownika i bieżący wygenerowany losowo identyfikator połączenia. Ten identyfikator połączenia będzie nieprawidłowy, gdy tylko zakończy się połączenie. Użytkownicy anonimowi nie powinni mieć dostępu do żadnych poufnych informacji.

Zalecenia dotyczące zabezpieczeń sygnałów

Protokół SSL (Secure Sockets)

Protokół SSL używa szyfrowania w celu zabezpieczenia transportu danych między klientem i serwerem. Jeśli aplikacja sygnalizująca przesyła poufne informacje między klientem a serwerem, należy użyć protokołu SSL do transportu. Aby uzyskać więcej informacji o konfigurowaniu protokołu SSL, zobacz 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 prawdziwe, gdy użytkownicy mogą automatycznie odłączać grupy podczas ponownego nawiązywania połączenia. Zamiast tego należy rozważyć dodanie uprzywilejowanych użytkowników do roli i ograniczenie dostępu do metody centrum tylko do członków tej roli. Aby uzyskać przykład ograniczenia dostępu na podstawie roli, zobacz uwierzytelnianie i autoryzacja dla centrów sygnałów. 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ługiwanie danych wejściowych z klientów

Wszystkie dane wejściowe z klientów, które są przeznaczone do emisji do innych klientów, muszą zostać zakodowane w celu zapewnienia, że złośliwy użytkownik nie wyśle skryptu do innych użytkowników. Najlepszym rozwiązaniem jest kodowanie komunikatów na klientach otrzymujących zamiast na serwerze, ponieważ aplikacja sygnalizująca może mieć wiele różnych typów klientów. W związku z tym kodowanie HTML działa dla klienta sieci Web, ale nie dla innych typów klientów. Na przykład Metoda klienta sieci Web, aby wyświetlić komunikat rozmowy, bezpiecznie obsłużyć nazwę użytkownika i komunikat, wywołując funkcję html().

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 uwierzytelnienia użytkownika zostanie zmieniony, gdy istnieje aktywne połączenie, użytkownik otrzyma komunikat o błędzie z informacją "tożsamość użytkownika nie może ulec zmianie podczas aktywnego połączenia sygnalizującego". W takim przypadku aplikacja powinna ponownie połączyć się z serwerem, aby upewnić się, że identyfikator połączenia i nazwa użytkownika są skoordynowane. Na przykład jeśli aplikacja zezwala użytkownikowi na wylogowanie się, gdy istnieje aktywne połączenie, nazwa użytkownika dla połączenia nie będzie już zgodna z nazwą przekazaną dla następnego żądania. Należy zatrzymać połączenie przed wylogowaniem użytkownika, a następnie uruchomić go ponownie.

Należy jednak pamiętać, że większość aplikacji nie będzie musiała ręcznie zatrzymać i uruchomić połączenia. Jeśli aplikacja przekierowuje użytkowników do osobnej strony po wylogowaniu, takiej jak domyślne zachowanie w aplikacji formularzy sieci Web lub aplikacji MVC, lub odświeża bieżącą stronę po wylogowaniu, aktywne połączenie zostanie automatycznie rozłączone i nie Wymagaj wszelkich dodatkowych akcji.

Poniższy przykład pokazuje, jak zatrzymać i uruchomić połączenie, gdy stan użytkownika został zmieniony.

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

Można również zmienić stan uwierzytelniania użytkownika, jeśli w lokacji jest używane okresowe wygaśnięcie z uwierzytelnianiem formularzy i nie ma aktywności, aby zachować prawidłowy plik 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 jakiś skrypt, który okresowo żąda zasobu na serwerze sieci Web, aby zachować prawidłowy plik cookie uwierzytelniania. Poniższy przykład pokazuje, jak zażądać zasobu co 30 minut.

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

Automatycznie generowane pliki proxy JavaScript

Jeśli nie chcesz uwzględniać wszystkich centrów i metod w pliku proxy 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 miał świadomość wszystkich metod. Aby wyłączyć generowanie automatyczne, należy ustawić EnableJavaScriptProxies na false.

var hubConfiguration = new HubConfiguration();
hubConfiguration.EnableJavaScriptProxies = false;
RouteTable.Routes.MapHubs("/signalr", hubConfiguration);

Aby uzyskać więcej informacji na temat plików serwera proxy języka JavaScript, zobacz wygenerowany serwer proxy i jego zawartość.

Wyjątki

Należy unikać przekazywania obiektów wyjątków do klientów, ponieważ obiekty mogą uwidaczniać klientom poufne informacje. Zamiast tego należy wywołać metodę na kliencie, który 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.");
    }
}