2012-03-29 4 views
15

Abbiamo un'applicazione ASP.NET MVC 2 (.NET 4) in esecuzione su Windows Azure (ultima versione OS 2.x) con due istanze di ruolo web.Perché HttpAntiForgeryException si verifica in modo casuale anche con una chiave macchina statica?

Utilizziamo il token anti-contraffazione fornito da MVC per tutte le richieste POST e abbiamo impostato un codice macchina statico in web.config, quindi tutto funziona su più macchine e attraverso riavvii. Il 99,9% dei casi funziona perfettamente.

Ogni tanto, tuttavia, registriamo una HttpAntiForgeryException, con messaggio "Un token anti-contraffazione richiesto non è stato fornito o non era valido."

So che il problema potrebbe essere che i cookie non sono consentiti nel browser, ma abbiamo verificato che i cookie sono abilitati e vengono inviati avanti e indietro correttamente.

L'errore si verifica con una varietà di browser e ovviamente causa problemi agli utenti perché devono ripetere l'operazione o possono perdere alcuni dati. Basti dire che non siamo stati in grado di riprodurre il problema localmente, ma si verifica solo su Windows Azure.

Perché sta succedendo? Come possiamo evitarlo?

+0

Ho controllato con i ragazzi della sicurezza (sul team MVC) e probabilmente Darin ha ragione - il nome utente probabilmente è cambiato. – RickAndMSFT

risposta

0

Ci sono un paio di opzioni per ciò che si potrebbe provare. Potresti provare i remoting nella macchina e guardare il registro degli eventi per vedere se puoi ottenere maggiori informazioni da questo riguardo a dove sta accadendo. Se questo non aiuta, puoi usare DebugDiag o qualche altro strumento per catturare un dump del processo (DebugDiag ti permetterà di catturarne uno al momento di questa specifica eccezione). E poi guarda quello per vedere cosa sta succedendo.

Se non riesci a capire da lì, puoi sempre creare un caso di supporto con Microsoft per aiutarti a indagare.

7

Il token anti contraffazione contiene il nome utente dell'utente attualmente connesso quando viene emesso. E quando verifica la sua validità, l'utente attualmente connesso viene confrontato con quello usato quando il token è stato emesso. Quindi, ad esempio, se si dispone di un modulo in cui l'utente non è ancora autenticato e si emette un token anti-contraffazione, non verrà memorizzato alcun nome utente. Se quando si invia il modulo si autentica l'utente, il token non sarà più valido. Lo stesso vale per il logout.

Ecco come il metodo validate assomiglia:

public void Validate(HttpContextBase context, string salt) 
{ 
    string antiForgeryTokenName = AntiForgeryData.GetAntiForgeryTokenName(null); 
    string str2 = AntiForgeryData.GetAntiForgeryTokenName(context.Request.ApplicationPath); 
    HttpCookie cookie = context.Request.Cookies[str2]; 
    if ((cookie == null) || string.IsNullOrEmpty(cookie.Value)) 
    { 
     throw CreateValidationException(); 
    } 
    AntiForgeryData data = this.Serializer.Deserialize(cookie.Value); 
    string str3 = context.Request.Form[antiForgeryTokenName]; 
    if (string.IsNullOrEmpty(str3)) 
    { 
     throw CreateValidationException(); 
    } 
    AntiForgeryData data2 = this.Serializer.Deserialize(str3); 
    if (!string.Equals(data.Value, data2.Value, StringComparison.Ordinal)) 
    { 
     throw CreateValidationException(); 
    } 
    string username = AntiForgeryData.GetUsername(context.User); 
    if (!string.Equals(data2.Username, username, StringComparison.OrdinalIgnoreCase)) 
    { 
     throw CreateValidationException(); 
    } 
    if (!string.Equals(salt ?? string.Empty, data2.Salt, StringComparison.Ordinal)) 
    { 
     throw CreateValidationException(); 
    } 
} 

Un modo possibile eseguire il debug di questo è di ricompilare ASP.NET MVC dal suo codice sorgente e accedere esattamente in quale dei casi, se si entra quando l'eccezione viene lanciato

+0

I membri della sicurezza del team ASP.NET MVC sono d'accordo, probabilmente il nome utente è cambiato. Ora che MVC è open source, potresti creare un ramo diagnoistico. Una nota minore, il token AF NON contiene il nome utente - il codice mostra che il nome utente proviene dal token del modulo, non dal token del cookie. – RickAndMSFT

+0

Il problema è che non usiamo l'autenticazione ASP.NET e non impostiamo mai l'utente/identità. Il nome utente può cambiare autonomamente (a condizione che l'app venga eseguita come servizio di rete su Azure)? –

+0

Hai provato a ricompilare ASP.NET MVC dal codice sorgente e la traccia, nel qual caso la verifica antifronto non riesce? –

15

Mi sono imbattuto in questo recentemente e ho trovato due cause.

1. Browser ripristina ultima sessione sulla aperto per la pagina che viene memorizzata nella cache

Se si dispone di una pagina che è cachable che esegue un post al server (cioè antiforgery sarà acceso) e l'utente ha la loro browser impostato per ripristinare l'ultima sessione all'avvio (questa opzione esiste in chrome) la pagina verrà sottoposta a rendering dalla cache. Tuttavia, il cookie di verifica della richiesta non sarà presente perché è un cookie di sessione del browser e viene eliminato quando il browser viene chiuso. Dal momento che il cookie è sparito ottieni l'eccezione anti-contraffazione. Soluzione: restituire le intestazioni di risposta in modo che la pagina non venga memorizzata nella cache (ad es. Cache-Control: private, no-store).

2.Condizioni di gara se si apre più di una scheda all'avvio sul proprio sito

I browser hanno la possibilità di aprire una serie di schede all'avvio. Se più di uno di questi ha colpito il tuo sito che restituisce un cookie di verifica delle richieste, puoi toccare una condizione di competizione in cui il cookie di verifica della richiesta viene sovrascritto. Ciò accade perché più di una richiesta colpisce il tuo server da un utente che non ha il set di cookie di verifica delle richieste. La prima richiesta viene gestita e imposta il cookie di verifica della richiesta. Successivamente viene gestita la seconda richiesta, ma non ha inviato il cookie (non era ancora stato impostato al momento della richiesta), quindi il server ne genera uno nuovo. Il nuovo sovrascrive il primo e ora quella pagina riceverà un'eccezione di richiesta antifora quando verrà eseguito un post successivo. Il framework MVC non gestisce questo scenario. Questo bug è stato segnalato al team MVC in Microsoft.

+0

Anche io vedo la condizione 2. Succede solo al primo utente. Una volta caricata la pagina per quel primo utente, non succede più. – GnomeCubed

1

Ho alcune applicazioni Web MVC3 che ottengono questo abbastanza regolarmente anche. La maggior parte di questi sono perché il client non invia un corpo POST. E la maggior parte di questi sono IE8 a causa di alcuni bug con richieste Ajax che precedono un normale modulo post. C'è una correzione per IE che sembra affrontare i sintomi, che tipo di dimostra che si tratta di un bug cliente in questi casi

http://support.microsoft.com/?kbid=831167

Ci sono alcune discussioni circa la questione giro per il web, niente di troppo utile se , io sicuramente non sto per pasticciare con timeout keep-alive che è una "soluzione" suggerita in alcuni luoghi ...

https://www.google.com/search?q=ie8+empty+post+body

non sono mai stato in grado di riprodurre con una varietà di tentativi di ripristinare le connessioni tra i POSTS quindi temo di non avere un reale soluzione per il caso di IE corpi vuoti POST. Il modo in cui l'abbiamo attenuato un po 'è assicurarci che non usiamo mai il metodo POST quando si recuperano dati tramite ajax.

Se si registra la richiesta completa, verificare se il corpo POST è vuoto e, in tal caso, probabilmente sarà un IE meno recente. E non intendo Content-Length: 0, di solito ha un Content-Length che sembra corretto nelle intestazioni, ma non ci sarà letteralmente nulla dopo le intestazioni nella richiesta.

Il problema nel suo insieme è ancora un mistero per me anche perché otteniamo ancora un'eccezione occasionale in cui è presente un corpo POST completo. I nostri nomi utente non cambiano mai e anche le nostre chiavi sono statiche, non ho provato ad aggiungere il debug alla sorgente, se mai riuscirò a farlo riferirò i miei risultati.

0

Ho riscontrato problemi simili con il mio codice anti-falsificazione fabbricato in casa, che è concettualmente molto simile al meccanismo MVC. Principalmente sembra che il problema si verifichi perché i browser moderni sembrano disposti a visualizzare copie cache di pagine specificate come non memorizzate nella cache.

Ho provato tutte le combinazioni delle direttive della no-cache della pagina, ma a volte riesco ancora a visualizzare le pagine memorizzate nella cache.

Ho trovato che una soluzione migliore è quella di agganciare l'evento onbeforeunload per la pagina e cancellare esplicitamente il valore del campo di input nascosto che contiene il valore del token nel DOM.

Se una copia memorizzata nella cache di una pagina viene caricata, sembra contenere il valore del campo di input cancellato. Ho poi test per questo nella funzione pronti documenti e ricaricare la pagina, se necessario:

window.location.reload(true); 

sembra funzionare abbastanza efficace, e ho il sospetto che potrebbe per il codice anti-contraffazione MVC troppo.