Autore: Tiziana Loporchio
Già dalla versione 1.x di ASP.NET, abbiamo utilizzato il ViewState , una funzionalità built-in che automaticamente rende permanenti i dati relativi ai campi e ai controlli nella pagina, senza doverli nè richiedere nè immettere manualmente durante i round trip al server (cosa che invece ricordiamo avveniva con il buon vecchio ASP).
Chi ha utilizzato appieno questa caratteristica però, si è da subito scontrato con la quantità di dati che vengono trasferiti negli HTTP Headers durante i vari postback della pagina e che spesso contribuiscono a rallentarne la visualizzazione.
La problematica è ancora più evidente nei mobile devices per la limitata larghezza di banda e la non sufficiente capacità di memoria necessaria ad archiviare grandi quantità di ViewState.
La mole di dati ed il conseguente rallentamento non costituiscono però il solo problema.
A complicare il tutto ci si mettono i Firewall che, come caratteristica di default e per serie questioni di security che analizzeremo, spesso non accettano i campi HTML "Hidden" che archiviano proprio i valori di ViewState, a volte troncandone la stringa passata nell'header HTTP, a volte nascondendone il valore, generando così il messaggio di errore "the viewstate is invalid for this page and might be corrupted".
In questo articolo analizzeremo le soluzioni adottate mettendo a confronto ASP.NET 1.x e 2.0.
Il ViewState e potenziali rischi di sicurezza.
Archiviare le informazioni della pagina utilizzando opzioni client side, ci permette di non utilizzare risorse server side. Questo seppur ottimizzando le performance, non garantisce la security.
Le informazioni passate nel campo nascosto possono essere visualizzate nella sorgente HTML della pagina, creando una potenziale problematica di sicurezza tale che, pur archiviando il dato con encoding base64 ed in un formato hashed tramite una MAC (Machine Authentication Code) key, lo stesso può essere alterato o intercettetato da utenti malintenzionati.
Il firewall come causa dell’errore.
Spesso chi riceve il messaggio di errore "the viewstate is invalid for this page and might be corrupted" però, non sa che la causa potrebbe essere un Firewall o Proxy che sia.
Nel momento in cui la quantità di dati diventa considerevole nel campo Hidden, alcuni proxy e Firewall, avendo impostata la quantità massima dei data accettati nei campi nascosti proprio per non incorrere in potenziali rischi di sicurezza (un esempio è l’ “Hidden-Field Tampering” che agevola il cosiddetto “Web Server Spoofing”), generano l’eccezione, troncando il valore di ViewState negli HTTP Headers oppure inibendo il passaggio del valore stesso o nella maggior parte dei casi, alterandolo.
Al postback della pagina, ASP.NET si accorge di questo tramite l’esecuzione del rehashing dei dati di ViewState, verificando la mancanza di corrispondenza con quello archiviato nella pagina.
E’ in questo momento che l'eccezione generata , spesso non gestita, restituisce un messaggio poco esplicativo : "the viewstate is invalid for this page and might be corrupted".
Consiglio, a questo proposito, un semplicissimo ma efficace tool freeware che installa una Explorer Bar in grado di attivare in Internet Explorer la visualizzazione degli HttpHeaders passati dalle nostre pagine ASPX e di controllarne appieno le variabili durante i nostri test.
ASP.NET 1.x e 2.0 : soluzioni a confronto
La soluzione in ASP.NET 1.x
Per la versione ASP.NET 1.x la soluzione adottata è tipicamente quella di eseguire l’override di due metodi SavePageStateToPersistenceMedium e LoadPageStateFromPersistenceMedium responsabili della gestione del ViewState.
L’override ci permetterà di salvare il ViewState in qualsiasi altro oggetto che non sia un campo Hidden come per esempio in cache, in sessione, o nel database.
Per questo esempio utilizzeremo una variabile di sessione.
Dopo esserci assicurati di avere un oggetto viewstate non nullo, proseguiremo con il salvataggio persistente dei dati nella variabile di sessione.
Verrà fatta istanza di un oggetto di classe LosFormatter che ha il compito di eseguire la serializzazione del viewstate.
LosFormatter presente nel namespace System.Web.UI utilizza il metodo Serialize per trasformare un Limited Object Serialization (LOS) formatter, in un valore di ViewState formattato e convertito in Base64, così da essere serializzato in uno Stream ed inserito nella variabile di sessione invece che, come avviene di solito, in un campo Hidden.
Di seguito il codice:
[C#]
protected override void SavePageStateToPersistenceMedium(object viewState)
{
if( viewState==null) return;
LosFormatter formatter = new LosFormatter();
StringWriter writer = new StringWriter();
try
{
formatter.Serialize( writer, viewState );
Session["ViewState"] = writer.ToString();
}
catch
{
throw new HttpException("Invalid view state");
}
finally
{
writer.Close();
}
}
Passo successivo, è naturalmente l’override del metodo LoadPageStateFromPersistenceMedium() per caricare il ViewState dalla variabile di sessione creata con il metodo precedente, SavePageStateToPersistenceMedium() e deserializzarlo.
A questo scopo, chiameremo il metodo Deserialize dell’oggetto di tipo LosFormatter, per deserializzare il valore contenuto nella variabile di sessione e restituirlo alla pagina.
Di seguito il codice:
[C#]
protected override object LoadPageStateFromPersistenceMedium()
{
object viewStateObj = null;
LosFormatter formatter = new LosFormatter();
object sessionViewState = Session["ViewState"];
try
{
viewStateObj = formatter.Deserialize(sessionViewState.ToString());
}
catch
{
throw new HttpException("Invalid view state");
}
return viewStateObj;
}
L’esecuzione di questo codice stamperà ancora il campo hidden __ViewState nella pagina ma sarà vuoto.
Nota: Anche se ci sarà codice da scrivere, non andrà inserito in ogni pagina, ma basterà creare una classe contente gli overrides che sarà poi ereditata dalle nostre pagine.
La soluzione in ASP.NET 2.0
Con la versione 2.0 di ASP.NET le cose certamente ci vengono rese più facili .
Non servirà più implementare del codice, ma basterà impostare una nuova proprietà dell’oggetto Page.
E' stata infatti introdotta una nuova caratteristica in ASP.NET 2.0: il ViewState Chunking.
In che cosa consiste ?
Se la quantità di dati del ViewState diventa eccessiva, mediante il “chunking”, i dati verranno automaticamente divisi in quantità inferiori e inseriti in più campi form "Hidden", ognuno dei quali contiene un valore con una dimensione inferiore rispetto a quella specificata nella proprietà MaxPageStateFieldLength che definisce la dimensione massima (in byte) consentita nel campo "Hidden".
Tale impostazione permetterà ai firewall di non ritenere il contenuto pericoloso, quindi di non alterarlo e di mostrare correttamente lo stato dei controlli senza generare l'eccezione.
Se l'impostazione, invece, dovesse rimanere quella di default, a valore -1, non verrà ovviamente fatta la suddetta suddivisione in multipli blocchi di dati (“chunks”) con il conseguente rischio di incorrere nell’eccezione come descritto precedentemente nell’articolo.
Come impostare la proprietà MaxPageStateFieldLength
Possiamo impostare la proprietà all'interno dei files di configurazione “machine.config” o “web.config” nella sezione <pages> come segue :
<configuration>
<system.web>
<pages MaxPageStateFieldLength="1000" … />
</system.web>
</configuration>
Il risultato HTML quindi, per un ViewState di 2000 bytes sarà diviso in due “chunks” ognuno da 1000 bytes come segue:
<input type="hidden" name="__VIEWSTATEFIELDCOUNT" value="2" />
<input type="hidden" name="__VIEWSTATE" value =".." />
<input type="hidden" name="__VIEWSTATE1" value =".." />
Conclusioni
ASP.NET 2.0 a differenza delle precedenti versioni implementa finalmente una soluzione built-in per la gestione dell’interazione tra Firewall e nostre pagine web rispettando i criteri di sicurezza; ASP.NET 1.x, infatti, ci costringeva ad implementare del codice server side per salvare i valori di viewstate in altri oggetti in modo da ovviare al problema.
Non solo in questo, ASP.NET 2.0, ha trovato delle soluzioni a problematiche presenti nelle precedenti versioni ma di questo potremo parlare nei prossimi articoli.