Alkampfer's Place

Il blog di Gian Maria Ricci
posts - 659, comments - 871, trackbacks - 80

My Links

News

Gian Maria Ricci Mvp Logo CCSVI in Multiple Sclerosis

English Blog

Tag Cloud

Article Categories

Archives

Post Categories

Image Galleries

I miei siti

Siti utili

Accedere a risorse in maniera più sicura da asp.net

Dopo avere letto il post di Raf, volevo fare una semplice considerazione sul come gestire l'accesso alle risorse da applicazioni asp.net. Spesso si ha infatti la necessità di controllare e filtrare sia il download di file, sia la visualizzazione di documenti che sono presenti nel sito, in sostanza una pagina o un handler asp.net necessitano di accedere ad un file fisico specificato dal chiamante. Ad esempio se si volesse visualizzare in un literal il contenuto di un file testo, si potrebbe inizialmente pensare di scrivere codice del genere.

protected void Page_Load(object sender, EventArgs e) {
   
String filename = Server.MapPath(Request.QueryString["f"].ToString());
   Literal1.Text = Server.HtmlEncode(System.IO.
File.ReadAllText(filename));
}

Che probabilmente è un po' quello che fa il sito di Veltroni, riuscite a vedere il problema? Chiaramente con questo modo io permetto al chiamante di specificare con il parametro "f" in querystring il file da visualizzare e quindi posso specificare qualsiasi file si trovi nella cartella del sito e quindi anche il web.config, il sorgente dei file (se il sito è updatable) etc etc. Questo buco di sicurezza è assolutamente gravissimo, ma come rimediamo alla situazione? Come possiamo permettere al chiamante di specificare un file su disco senza garantirgli potenziale accesso a file non permessi? Una possibile alternativa è la seguente, fare una funzione che accetta una specifica di file e torna il full name del file stesso, ma facendo alcuni controlli

public String GetDocumentFileName(String queryStringValue) {
   
String baseDir = System.Configuration.ConfigurationManager.AppSettings["DocumentDir"];
   
String filename = System.IO.Path.Combine(baseDir, queryStringValue);
   
FileInfo fi = new FileInfo(filename);
   
if (String.Compare(baseDir, 0, fi.FullName, 0, baseDir.Length, 
         
StringComparison.OrdinalIgnoreCase) == 0) {
         
return fi.FullName;
   }
   
throw new SecurityException("Resource Not Allowed");
}

Prima di tutto la directory fisica dove si trovano i documenti è al di fuori della directory del sito, meglio se addirittura messa in una partizione separata dedicata allo scopo. La directory è specificata tramite un setting nel web.config nel mio caso ad esempio ho scelto "D:\temp\". La funzione quindi combina il path base con il path passato per la querystring e trova il nome del file, ad esempio specificando "f=test.txt" il fullname sarebbe "D:\temp\test.txt". A questo punto un utente malintenzionato potrebbe " f=..\altradirectory\altrofile.txt", in sostanza purtroppo il chiamante può utilizzare un nome del file che fa uscire il path al di fuori della cartella impostata come base per i documenti. Per evitare questo la funzione crea un oggetto FileInfo sul filename ottenuto dalla combinazione del path + specifca dell'utente, dal quale si può recuperare tramite la proprietà FullName il vero nome del file senza caratteri ".." di sorta. A questo punto basta effettuare una comparazione ordinale, e quindi indipendente dalla cultura, per controllare che effettivamente il vero nome del file inizi con il nome della directory base utilizzata per mantenere i documenti. In questo modo l'utente anche se riesce a trovare un modo per specificare un path al di fuori della directory che contiene i documenti riceverà una bella SecurityException.

Naturalmente poi il sito deve utilizzare un worker process che giri con le credenziali di un utente ben definito il quale deve avere un accesso minimo al disco, in questo modo anche se qualcuno trova un modo per bypassare il controllo precedente ed uscire dalla directory prefissata il processo non ha comunque diritto di accedere al file.

Tutta questa tecnica ha però una grande falla, il problema è che il chiamante ha la possibilità di specificare in chiaro il nome del file, una scelta migliore è adottare una "White List", in pratica io metto in una tabella di database o in un file xml o quantaltro, la lista di tutti i documenti disponibili indicizzati con una chiave. Nella querystring passerò poi la chiave del documento, in questo modo il chiamante vede un codice che per lui non ha significato e non può specificare un file preciso. Naturalmente non si deve utilizzare una chiave sequenziale come un identity, questo perché il chiamante vede magari "f=136" e prova a scrivere "f=138" potenzialmente tentando l'accesso ad un documento che potrebbe essergli precluso, per essere ancora più sicuri bisogna quindi generare chiavi casuali, un guid può andare bene, ma meglio utilizzare la RNGCryptoServiceProvider, che in quanto a casualità e sicurezza è sicuramente migliore :D.

RNGCryptoServiceProvider provider = new RNGCryptoServiceProvider();
Byte[] key = new Byte[16];
provider.GetBytes(key);
Literal1.Text = 
BitConverter.ToString(key);

In questo modo mi sono generato una chiave random lunga 16 byte (posso farla lunga quanto voglio) che posso utilizzare in maniera sicura nella sua rappresentazione stringa in una querystring. Ora il chiamante vede un parametro del tipo "f=F718E82F6D47320C4C506AD4AD9D3875" e sfido chiunque a tentare di trovare un altro valore valido che si riferisca ad un file realmente esistente :D

Alk.

 

 

Print | posted on venerdì 5 ottobre 2007 16:49 | Filed Under [ .NET ]

Comments have been closed on this topic.

Powered by:
Powered By Subtext Powered By ASP.NET