Share via


Introduzione alla sicurezza di SignalR

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.

Versioni software usate in questo argomento

Versioni precedenti di questo argomento

Per informazioni sulle versioni precedenti di SignalR, vedere Versioni precedenti di SignalR.

Domande e commenti

Lasciare commenti e suggerimenti su come è piaciuta questa esercitazione e ciò che è possibile migliorare nei commenti nella parte inferiore della pagina. Se si hanno domande che non sono direttamente correlate all'esercitazione, è possibile pubblicarli nel forum ASP.NET SignalR o StackOverflow.com.

Panoramica

Questo documento contiene le seguenti sezioni:

Concetti di sicurezza di SignalR

Autenticazione e autorizzazione

SignalR non fornisce funzionalità per l'autenticazione degli utenti. Si integrano invece le funzionalità SignalR nella struttura di autenticazione esistente per un'applicazione. È possibile autenticare gli utenti come normalmente nell'applicazione e 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.

Si applica l'attributo Authorize agli hub, ma non le connessioni persistenti. 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. Per ogni richiesta, il client e il server passano un token di connessione che contiene l'ID connessione e il nome utente per gli utenti autenticati. L'ID di connessione identifica in modo univoco ogni client connesso. Il server genera in modo casuale l'ID connessione quando viene creata una nuova connessione e mantiene tale ID per la durata della connessione. Il meccanismo di autenticazione per l'applicazione Web fornisce il nome utente. SignalR usa la crittografia e una firma digitale per proteggere il token di connessione.

Diagramma che mostra una freccia da Una nuova richiesta di connessione client a una richiesta di connessione ricevuta dal server alla risposta del server alla risposta ricevuta dal client. Il sistema di autenticazione genera un token di connessione nelle caselle Risposta e Risposta ricevuta.

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 che mostra una freccia dalla richiesta client al server richiesta ricevuta al token salvato. Il token di connessione e il messaggio si trovano nella casella Client e nella casella Server.

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.

Token di connessione e altri tipi di token

I token di connessione vengono occasionalmente contrassegnati dagli strumenti di sicurezza perché sembrano essere token di sessione o token di autenticazione, che rappresentano un rischio se esposto.

Il token di connessione di SignalR non è un token di autenticazione. Viene usato per verificare che l'utente che effettua questa richiesta sia lo stesso che ha creato la connessione. Il token di connessione è necessario perché ASP.NET SignalR consente alle connessioni di spostarsi tra i server. Il token associa la connessione a un utente specifico, ma non asserisce l'identità dell'utente che effettua la richiesta. Per l'autenticazione corretta di una richiesta SignalR, deve avere un altro token che asserisce l'identità dell'utente, ad esempio un cookie o un token di connessione. Tuttavia, il token di connessione stesso non dichiara che la richiesta è stata effettuata dall'utente, solo che l'ID di connessione contenuto all'interno del token è associato a tale utente.

Poiché il token di connessione non fornisce alcuna attestazione di autenticazione personalizzata, non viene considerato un token di "sessione" o "autenticazione". L'acquisizione di un token di connessione di un determinato utente e la copia in una richiesta autenticata come utente diverso (o una richiesta non autenticata) avrà esito negativo, perché l'identità utente della richiesta e l'identità archiviata nel token non corrisponderà.

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 all'uso dei cookie. Ad esempio, anche l'autenticazione di base e digest è vulnerabile. Dopo che un utente accede con l'autenticazione Basic o Digest, il browser invia automaticamente le credenziali fino al termine della sessione.

Mitigazioni CSRF prese da SignalR

SignalR esegue la procedura seguente per impedire a un sito dannoso di creare richieste valide all'applicazione. SignalR esegue questi passaggi per impostazione predefinita, non è necessario eseguire alcuna azione nel codice.

  • Disabilitare le richieste tra domini SignalR disabilita le richieste tra domini per impedire agli utenti di chiamare un endpoint SignalR da un dominio esterno. SignalR considera una richiesta da un dominio esterno non valida e blocca la richiesta. È consigliabile mantenere questo comportamento predefinito; in caso contrario, un sito dannoso potrebbe ingannare gli utenti a inviare 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 il cookie SignalR passa il token di connessione come valore della stringa di query, anziché come cookie. L'archiviazione del token di connessione in un cookie non è sicura perché il browser può inoltrare inavvertitamente il token di connessione quando viene rilevato codice dannoso. Inoltre, il passaggio del token di connessione nella stringa di query impedisce che il token di connessione venga 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 di connessione associato a ogni utente autenticato. Il server non elabora alcuna richiesta da un ID 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 in modo casuale corrente. L'ID di 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 Layers (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ò vale soprattutto quando gli utenti possono riconnettersi automaticamente ai gruppi durante una riconnessione. Prendere invece in considerazione l'aggiunta di 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.

Gestione sicura dell'input dai client

Per garantire che un utente malintenzionato non invii script ad altri utenti, è necessario codificare tutti gli input dei client destinati alla trasmissione ad altri client. È consigliabile codificare i messaggi nei client riceventi anziché nel 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 gestirà 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>');
};

Riconciliazione di una modifica dello stato dell'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 corrisponderà più al nome passato per la richiesta successiva. Si vuole arrestare la connessione prima della disconnessa dell'utente e quindi riavviarla.

Tuttavia, è importante notare che la maggior parte delle applicazioni non dovrà arrestare e avviare manualmente 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 o 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 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 una scadenza scorrevole con l'autenticazione basata su form e non esiste 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. L'esempio seguente illustra 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 desidera 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 si dispone di più hub e metodi, ma non si vuole che tutti gli utenti siano a conoscenza di tutti i metodi. Per disabilitare la generazione automatica, impostare EnableJavaScriptProxies su false.

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

Per altre informazioni sui file proxy JavaScript, vedere Il proxy generato e le relative operazioni.

Eccezioni

È consigliabile evitare di passare oggetti eccezione ai client perché gli oggetti possono esporre informazioni riservate ai client. Chiamare invece un metodo sul 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.");
    }
}