Share via


Introduzione alla sicurezza di SignalR (SignalR 1.x)

di Patrick Fletcher, Tom FitzMacken

Avviso

Questa documentazione non è per la versione più recente di SignalR. Esaminare ASP.NET Core SignalR.

Questo articolo descrive i problemi di sicurezza da considerare durante lo sviluppo di un'applicazione SignalR.

Panoramica

Questo documento contiene le seguenti sezioni:

Concetti di sicurezza di SignalR

Autenticazione e autorizzazione

SignalR è progettato per essere integrato nella struttura di autenticazione esistente per un'applicazione. Non fornisce funzionalità per l'autenticazione degli utenti. È invece possibile autenticare gli utenti come normalmente nell'applicazione e quindi usare i risultati dell'autenticazione nel codice SignalR. Ad esempio, è possibile autenticare gli utenti con l'autenticazione dei moduli ASP.NET e quindi nell'hub applicare quali utenti o ruoli sono autorizzati a chiamare un metodo. Nell'hub è anche possibile passare informazioni di autenticazione, ad esempio nome utente o se un utente appartiene a un ruolo, al client.

SignalR fornisce l'attributo Autorizza per specificare quali utenti hanno accesso a un hub o a un metodo. Si applica l'attributo Authorize a un hub o a metodi specifici in un hub. Senza l'attributo Authorize, tutti i metodi pubblici nell'hub sono disponibili per un client connesso all'hub. Per altre informazioni sugli hub, vedere Autenticazione e autorizzazione per hub SignalR.

L'attributo Authorize viene usato solo con hub. Per applicare le regole di autorizzazione quando si usa un PersistentConnection oggetto è necessario eseguire l'override del AuthorizeRequest metodo. Per altre informazioni sulle connessioni persistenti, vedere Autenticazione e autorizzazione per le connessioni persistenti di SignalR.

Token di connessione

SignalR riduce il rischio di eseguire comandi dannosi convalidando l'identità del mittente. Un token di connessione contenente l'ID di connessione e il nome utente per gli utenti autenticati, viene passato tra il client e il server per ogni richiesta. L'ID connessione è un identificatore univoco generato in modo casuale dal server quando viene creata una nuova connessione e viene mantenuto per la durata della connessione. Il nome utente viene fornito dal meccanismo di autenticazione per l'applicazione Web. Il token di connessione è protetto con la crittografia e una firma digitale.

Sistema token di connessione diagramma che mostra la relazione tra client, server, sistema di autenticazione e token di connessione.

Per ogni richiesta, il server convalida il contenuto del token per assicurarsi che la richiesta venga dall'utente specificato. Il nome utente deve corrispondere all'ID connessione. Convalidando sia l'ID connessione che il nome utente, SignalR impedisce a un utente malintenzionato di rappresentare facilmente un altro utente. Se il server non può convalidare il token di connessione, la richiesta ha esito negativo.

Diagramma del sistema token di connessione che mostra la relazione tra client, server e token salvato.

Poiché l'ID connessione fa parte del processo di verifica, non è consigliabile rivelare l'ID connessione di un utente ad altri utenti o archiviare il valore nel client, ad esempio in un cookie.

Riconnessione dei gruppi durante la riconnessione

Per impostazione predefinita, l'applicazione SignalR riassegnare automaticamente un utente ai gruppi appropriati quando si riconnette da un'interruzione temporanea, ad esempio quando una connessione viene eliminata e riristabilita prima del timeout della connessione. Quando si riconnette, il client passa un token di gruppo che include l'ID connessione e i gruppi assegnati. Il token di gruppo è firmato digitalmente e crittografato. Il client mantiene lo stesso ID di connessione dopo una riconnessione; pertanto, l'ID di connessione passato dal client riconnesso deve corrispondere all'ID di connessione precedente usato dal client. Questa verifica impedisce a un utente malintenzionato di passare le richieste per partecipare a gruppi non autorizzati durante la riconnessione.

Tuttavia, è importante notare che il token di gruppo non scade. Se un utente appartiene a un gruppo in passato, ma è stato vietato da tale gruppo, tale utente può essere in grado di simulare un token di gruppo che include il gruppo vietato. Se è necessario gestire in modo sicuro gli utenti appartenenti a quali gruppi, è necessario archiviare tali dati nel server, ad esempio in un database. Aggiungere quindi la logica all'applicazione che verifica nel server se un utente appartiene a un gruppo. Per un esempio di verifica dell'appartenenza al gruppo, vedere Uso dei gruppi.

La riconnessione automatica dei gruppi si applica solo quando una connessione viene riconnessa dopo un'interruzione temporanea. Se un utente si disconnette passando dall'applicazione o dall'applicazione viene riavviata, l'applicazione deve gestire come aggiungere tale utente ai gruppi corretti. Per altre informazioni, vedere Uso dei gruppi.

In che modo SignalR impedisce la richiesta tra siti forgery

La richiesta cross-site forgery (CSRF) è un attacco in cui un sito dannoso invia una richiesta a un sito vulnerabile in cui l'utente è attualmente connesso. SignalR impedisce la CSRF rendendo estremamente improbabile che un sito dannoso crei una richiesta valida per l'applicazione SignalR.

Descrizione dell'attacco CSRF

Ecco un esempio di attacco CSRF:

  1. Un utente accede a www.example.com, usando l'autenticazione dei moduli.

  2. Il server autentica l'utente. La risposta dal server include un cookie di autenticazione.

  3. Senza disconnettersi, l'utente visita un sito Web dannoso. Questo sito dannoso contiene il modulo HTML seguente:

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

    Si noti che l'azione modulo invia al sito vulnerabile, non al sito dannoso. Si tratta della parte "cross-site" di CSRF.

  4. L'utente fa clic sul pulsante invia. Il browser include il cookie di autenticazione con la richiesta.

  5. La richiesta viene eseguita nel server example.com con il contesto di autenticazione dell'utente e può eseguire qualsiasi operazione consentita da un utente autenticato.

Anche se questo esempio richiede all'utente di fare clic sul pulsante del modulo, la pagina dannosa potrebbe eseguire facilmente uno script che invia una richiesta AJAX all'applicazione SignalR. Inoltre, l'uso di SSL non impedisce un attacco CSRF, perché il sito dannoso può inviare una richiesta "https://".

In genere, gli attacchi CSRF sono possibili contro siti Web che usano cookie per l'autenticazione, perché i browser inviano tutti i cookie pertinenti al sito Web di destinazione. Tuttavia, gli attacchi CSRF non sono limitati a sfruttare i cookie. Ad esempio, l'autenticazione Basic e Digest è vulnerabile. Dopo aver eseguito l'accesso di un utente con l'autenticazione Basic o Digest, il browser invia automaticamente le credenziali fino al termine della sessione.

Mitigazioni CSRF prese da SignalR

SignalR esegue i passaggi seguenti per impedire a un sito dannoso di creare richieste valide all'applicazione SignalR. Questi passaggi vengono eseguiti per impostazione predefinita e non richiedono alcuna azione nel codice.

  • Disabilitare le richieste tra domini
    Per impostazione predefinita, le richieste tra domini vengono disabilitate in un'applicazione SignalR per impedire agli utenti di chiamare un endpoint SignalR da un dominio esterno. Qualsiasi richiesta proveniente da un dominio esterno viene considerata automaticamente non valida e viene bloccata. È consigliabile mantenere questo comportamento predefinito; in caso contrario, un sito dannoso potrebbe ingannare gli utenti nell'invio di comandi al sito. Se è necessario usare richieste tra domini, vedere Come stabilire una connessione tra domini .
  • Passare il token di connessione nella stringa di query, non cookie
    SignalR passa il token di connessione come valore stringa di query, anziché come cookie. Non archiviando il token di connessione come cookie, il token di connessione non viene inoltrato inavvertitamente dal browser quando viene rilevato codice dannoso. Inoltre, il token di connessione non viene mantenuto oltre la connessione corrente. Pertanto, un utente malintenzionato non può effettuare una richiesta con le credenziali di autenticazione di un altro utente.
  • Verificare il token di connessione
    Come descritto nella sezione Token di connessione , il server conosce l'ID connessione associato a ogni utente autenticato. Il server non elabora alcuna richiesta da un ID di connessione che non corrisponde al nome utente. È improbabile che un utente malintenzionato possa indovinare una richiesta valida perché l'utente malintenzionato deve conoscere il nome utente e l'ID connessione generato casualmente in modo casuale. L'ID connessione diventa non valido non appena la connessione viene terminata. Gli utenti anonimi non devono avere accesso ad alcuna informazione sensibile.

Raccomandazioni sulla sicurezza di SignalR

Protocollo Secure Socket Layer (SSL)

Il protocollo SSL usa la crittografia per proteggere il trasporto di dati tra un client e un server. Se l'applicazione SignalR trasmette informazioni riservate tra il client e il server, usare SSL per il trasporto. Per altre informazioni sulla configurazione di SSL, vedere Come configurare SSL in IIS 7.

Non usare i gruppi come meccanismo di sicurezza

I gruppi sono un modo pratico per raccogliere utenti correlati, ma non sono un meccanismo sicuro per limitare l'accesso alle informazioni riservate. Ciò è particolarmente vero quando gli utenti possono riconnettersi automaticamente durante una riconnessione. È invece consigliabile aggiungere utenti con privilegi a un ruolo e limitare l'accesso a un metodo hub solo ai membri di tale ruolo. Per un esempio di limitazione dell'accesso in base a un ruolo, vedere Autenticazione e autorizzazione per hub SignalR. Per un esempio di controllo dell'accesso utente ai gruppi durante la riconnessione, vedere Uso dei gruppi.

Gestire in modo sicuro l'input dai client

Tutti gli input dei client destinati alla trasmissione ad altri client devono essere codificati per garantire che un utente malintenzionato non invii script ad altri utenti. È consigliabile codificare i messaggi nei client di ricezione anziché sul server, perché l'applicazione SignalR potrebbe avere molti tipi diversi di client. Pertanto, la codifica HTML funziona per un client Web, ma non per altri tipi di client. Ad esempio, un metodo client Web per visualizzare un messaggio di chat gestirebbe in modo sicuro il nome utente e il messaggio chiamando la html() funzione.

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

La riconciliazione di una modifica dello stato utente con una connessione attiva

Se lo stato di autenticazione di un utente cambia mentre esiste una connessione attiva, l'utente riceverà un errore che indica che "L'identità utente non può cambiare durante una connessione SignalR attiva". In tal caso, l'applicazione deve riconnettersi al server per assicurarsi che l'ID connessione e il nome utente siano coordinati. Ad esempio, se l'applicazione consente all'utente di disconnettersi mentre esiste una connessione attiva, il nome utente per la connessione non corrisponde più al nome passato per la richiesta successiva. Si vuole arrestare la connessione prima dell'uscita dell'utente e quindi riavviarla.

Tuttavia, è importante notare che la maggior parte delle applicazioni non dovrà arrestare manualmente e avviare la connessione. Se l'applicazione reindirizza gli utenti a una pagina separata dopo la disconnessione, ad esempio il comportamento predefinito in un'applicazione Web Forms o un'applicazione MVC oppure aggiorna la pagina corrente dopo la disconnessione, la connessione attiva viene disconnessa automaticamente e non richiede alcuna azione aggiuntiva.

Nell'esempio seguente viene illustrato come arrestare e avviare una connessione quando lo stato dell'utente è cambiato.

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

In alternativa, lo stato di autenticazione dell'utente può cambiare se il sito usa la scadenza scorrevole con l'autenticazione form e non è presente alcuna attività per mantenere valido il cookie di autenticazione. In tal caso, l'utente verrà disconnesso e il nome utente non corrisponderà più al nome utente nel token di connessione. È possibile risolvere questo problema aggiungendo uno script che richiede periodicamente una risorsa nel server Web per mantenere valido il cookie di autenticazione. Nell'esempio seguente viene illustrato come richiedere una risorsa ogni 30 minuti.

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

File proxy JavaScript generati automaticamente

Se non si vuole includere tutti gli hub e i metodi nel file proxy JavaScript per ogni utente, è possibile disabilitare la generazione automatica del file. È possibile scegliere questa opzione se sono presenti più hub e metodi, ma non si vuole che tutti gli utenti siano consapevoli di tutti i metodi. Disabilita la generazione automatica impostando EnableJavaScriptProxies su false.

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

Per altre informazioni sui file proxy JavaScript, vedere Proxy generato e cosa fa per l'utente.

Eccezioni

È consigliabile evitare di passare oggetti eccezioni ai client perché gli oggetti possono esporre informazioni riservate ai client. Chiamare invece un metodo nel client che visualizza il messaggio di errore pertinente.

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