Nota
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare ad accedere o modificare le directory.
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare a modificare le directory.
di Patrick Fletcher, Tom FitzMacken
Avvertimento
Questa documentazione non è per la versione più recente di SignalR. Dai un'occhiata a 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
- Visual Studio 2013
- .NET 4.5
- SignalR versione 2
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 su cosa è possibile migliorare nei commenti nella parte inferiore della pagina. Se si hanno domande non direttamente correlate all'esercitazione, è possibile pubblicarle nel forum ASP.NET SignalR o StackOverflow.com.
Informazioni generali
Questo documento contiene le sezioni seguenti:
Concetti relativi alla 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. Autenticare gli utenti come normalmente nell'applicazione e usare i risultati dell'autenticazione nel codice SignalR. Ad esempio, è possibile autenticare gli utenti con ASP.NET autenticazione basata su moduli 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 il nome utente o se un utente appartiene a un ruolo, al client.
SignalR fornisce l'attributo Authorize per specificare quali utenti hanno accesso a un hub o a un metodo. L'attributo Authorize viene applicato 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 applicato agli hub, ma non alle connessioni persistenti. Per applicare le regole di autorizzazione quando si usa un oggetto PersistentConnection, devi eseguire l'override del metodo AuthorizeRequest. 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 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.
Per ogni richiesta, il server convalida il contenuto del token per assicurarsi che la richiesta proveni 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 è in grado di convalidare il token di connessione, la richiesta non riesce.
Poiché l'ID connessione fa parte del processo di verifica, non è consigliabile rivelare l'ID di 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 esposti.
Il token di connessione di SignalR non è un token di autenticazione. Viene usato per confermare che l'utente che effettua questa richiesta è lo stesso che ha creato la connessione. Il token di connessione è necessario perché ASP.NET SignalR consente alle connessioni di spostarsi tra server. Il token associa la connessione a un determinato utente, ma non asserisce l'identità dell'utente che effettua la richiesta. Affinché una richiesta SignalR sia autenticata correttamente, 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 attestazioni di autenticazione proprie, non viene considerato un token di "sessione" o "autenticazione". L'acquisizione del token di connessione di un determinato utente e la riproduzione 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 corrisponderanno.
Ricongiungersi ai gruppi quando ci si riconnette
Per impostazione predefinita, l'applicazione SignalR riassegnare automaticamente un utente ai gruppi appropriati durante la riconnessione da un'interruzione temporanea, ad esempio quando una connessione viene eliminata e ristabilita prima del timeout della connessione. Durante la riconnessione, 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 richieste di aggiunta 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 potrebbe essere in grado di simulare un token di gruppo che include il gruppo proibito. Se è necessario gestire in modo sicuro quali utenti appartengono 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 uscendo dall'applicazione o l'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 contraffazione della richiesta tra siti
Cross-Site Request 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 richiesta CSRF rendendo estremamente improbabile che un sito dannoso crei una richiesta valida per l'applicazione SignalR.
Descrizione dell'attacco CSRF
Di seguito è riportato un esempio di attacco CSRF:
Un utente accede a
www.example.comusando l'autenticazione basata su form.Il server autentica l'utente. La risposta dal server include un cookie di autenticazione.
Senza disconnettersi, l'utente visita un sito Web dannoso. Questo sito dannoso contiene il formato 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 del modulo invia al sito vulnerabile, non al sito dannoso. Questa è la parte "cross-site" di CSRF.
L'utente fa clic sul pulsante invia. Il browser include il cookie di autenticazione nella richiesta.
La richiesta viene eseguita nel server example.com con il contesto di autenticazione dell'utente e può eseguire qualsiasi operazione consentita a 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, anche l'autenticazione di base e digest è vulnerabile. Dopo che un utente accede con l'autenticazione Di base o Digest, il browser invia automaticamente le credenziali fino al termine della sessione.
Mitigazioni CSRF adottate da SignalR
SignalR esegue i passaggi seguenti 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 che qualsiasi richiesta da un dominio esterno non sia valida e blocchi la richiesta. È consigliabile mantenere questo comportamento predefinito; in caso contrario, un sito dannoso potrebbe indurre gli utenti a inviare comandi al sito. Se è necessario usare le 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ò inavvertitamente inoltrare 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 dovrà conoscere il nome utente e l'ID di connessione generato in modo casuale. Tale ID di connessione non viene più valido non appena viene terminata la connessione. Gli utenti anonimi non devono avere accesso ad alcuna informazione sensibile.
Raccomandazioni sulla sicurezza di SignalR
Protocollo SSL (Secure Socket Layer)
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 dei clienti
Per assicurarsi 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 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. Devi arrestare la connessione prima che l'utente si disconnetta e poi 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 Form o in 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 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 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 alcuni script che richiede periodicamente una risorsa nel server Web per mantenere il cookie di autenticazione valido. 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 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 operazioni che esegue automaticamente.
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.");
}
}