In ASP.NET abbiamo due possibilità di trattare le stringhe di connessione. Una è quella di scriverle direttamente nelle pagine dell'applicazione o nel codice interno delle stesse, l'altra è di salvarle all'interno del file di configurazione, il "Web.Config".
La seconda scelta è la più indicata perchè facilità le operazioni di manutenzione ed aumenta le leggibilità del codice.
Le stringhe di connessione in genere contengono informazioni che è bene celare ad occhi indiscreti. Un malintenzionato che ne venisse a conoscenza potrebbe usarle per accedere al database dell'applicazione.
Con ASP.net 2.0 possiamo criptare le stringhe di connessione in fase di programmazione. Questo significa che lo sviluppatore può usare delle classi già pronte per criptare delle parti di codice.
La situazione auspicata è quella di avere a disposizione delle utility in locale, per trasformare le parti di codice più sensibili, in modo da portare online solo codice sicuro, cioè non leggibile e quindi non riproducibile.
Per realizzare l'esempio di questo articolo usiamo il database di esempio PUBS, che si può scaricare dal sito della Microsoft, il VWD (Visual Web Developer) ed il linguaggio C#.
Supponiamo di aver scaricato il nostro database di esempio nella cartella "SQL Server 2000 Sample Databases" sul disco C, come in figura.
Con il VWD apriamo un "New Web Site" e lo chiamiamo "CriptareStringheConnessione" come in figura, in cui abbiamo scelto una localizzazione sul nostro file system ed il linguaggio C#.
Figura 2. Nuovo sito CriptareStringheConnessione
In Design view della "Default.aspx", trasciniamo un GridView
. Configuriamo il GridView
affinchè usi un SqlDataSource
che attinga dal file di database "PUBS.MDF". Possiamo scegliere di visualizzare la tabella "authors" o un'altra tabella, tale scelta non è importante per i nostri scopi.
La situazione che ci si presenta è questa:
La cosa importante è il file di configurazione che è stato automaticamente creato dal VWD e che riportiamo nel listato 1, mettendo in evidenza la sezione "connectionStrings".
Listato 1. Web.Config
<?xml version="1.0"?>
<configuration>
<appSettings/>
<connectionStrings>
<add name="PUBSConnectionString"
connectionString="Data Source=.SQLEXPRESS;
AttachDbFilename="C:SQL Server 2000 Sample DatabasesPUBS.MDF";
Integrated Security=True;
Connect Timeout=30;
User Instance=True"
providerName="System.Data.SqlClient" />
</connectionStrings>
<system.web>
<compilation debug="false" />
<authentication mode="Windows" />
</system.web>
</configuration>
Realizziamo quindi la nostra utility per criptare le stringhe di connessione. Aggiungiamo un nuovo Item di nome "Utility.aspx" e scegliamo di separare il codice interno.
Andiamo nel codice interno della Utility.aspx, contenuto nella Utility.aspx.cs, ed implementiamo i metodi che ci consentono di criptare e decriptare la stringa di connessione del Web.Config.
Cominciamo con il metodo Cripta()
riportato e commentato nel listato 2.
Listato 2. Metodo Cripta()
public void Cripta(string protectionProvider)
{
// apriamo il file di configurazione
Configuration WebConfig = System.Web.Configuration.WebConfigurationManager.OpenWebConfiguration(Request.ApplicationPath);
// specifichiamo la sezione da criptare
ConfigurationSection sezioneProt = WebConfig.Sections["connectionStrings"];
// specifichiamo il protection provider da applicare alla sezione da criptare
sezioneProt.SectionInformation.ProtectSection(protectionProvider);
// salviamo il file di configurazione modificato
WebConfig.Save();
}
Nel metodo Cripta()
, preleviamo il file di configurazione utilizzando il percorso dell'applicazione. Questo non genera ambiguità poichè in una cartella ci può essere un solo file di configurazione. Specifichiamo poi che vogliamo criptare le stringhe di connessione ed il Provider da utilizzare. Infine salviamo il Web.Config criptato.
Ci sono due possibili provider di protezione:
- il
DataProtectionConfigurationProvider
che usa DPAPI di Windows - l'
RSAProtectedConfigurationProvider
che usa l'algoritmo public-key disponibile nella classeRSACryptoServiceProvider
del Framework .NET .
Implementiamo ora il metodo DeCripta()
che riportiamo nel listato 3.
Listato 3. Metodo DeCripta()
public void DeCripta()
{
// apriamo il file di configurazione
Configuration WebConfig = System.Web.Configuration.WebConfigurationManager.OpenWebConfiguration(Request.ApplicationPath);
// specifichiamo la sezione criptata
ConfigurationSection sezioneProt = WebConfig.Sections["connectionStrings"];
// decriptiamo la sezione criptata
sezioneProt.SectionInformation.UnprotectSection();
// salviamo il file di configurazione modificato
WebConfig.Save();
}
Al metodo DeCripta()
non dobbiamo dire quale provider di protezione abbiamo usato in fase di criptazione, in quanto questa informazione è salvata direttamente sul file di configurazione.
Passiamo ora alla Utility.aspx ed implementiamo un RadioButtonList come in figura 4.
Clicchiamo due volte sul RadioButtonList
ed implementiamo il gestore dell'evento OnSelectedIndexChanged
nella parte di codice interno, come nel listato 4.
Listato 4. Gestore dell'evento OnSelectedIndexChanged
protected void RadioButtonList1_SelectedIndexChanged(object sender, EventArgs e)
{
switch (RadioButtonList1.SelectedIndex)
{
case 0:
Cripta("DataProtectionConfigurationProvider");
Messaggio1.Text = "Web.Config criptato con il DataProtection";
break;
case 1:
Cripta("RSAProtectedConfigurationProvider");
Messaggio1.Text = "Web.Config criptato con l'RSA";
break;
case 2:
DeCripta();
Messaggio1.Text = "Web.Config decriptato";
break;
}
}
RadioButtonList1_SelectedIndexChanged
non fa altro che fornire al metodo Cripta
il provider di protezione scelto dallo sviluppatore con il RadioButtonList
presente nell'utility di criptazione oppure chiamare il metodo DeCripta
. Al fine di capire meglio che cosa sta succedendo, abbiamo messo una Label
di informazione, come dovrebbe fare ogni buon programmatore.
Possiamo ora testare la nostra utility. Selezioniamola e premiamo F5. Selezioniamo "Cripta con DataProtectionConfigurationProvider" e se abbiamo configurato il RadioButtonList
per l'AutoPostBack
, otteniamo subito il risultato mostrato il figura 5, in cui un messaggio ci informa di ciò che è successo.
Chiudiamo la nostra applicazione e torniamo al VWD, questo ci chiede se vogliamo ricaricare il Web.Config che è stato modificato all'esterno del suo editor. Diciamogli di si per poter vedere le modifiche. Il file "Web.Config" modificato risulta essere come nel listato 5.
Listato 5. Web.Config con stringa di connessione crifrata
<?xml version="1.0"?>
<configuration>
<appSettings/>
<connectionStrings configProtectionProvider="DataProtectionConfigurationProvider">
<EncryptedData>
<CipherData>
<CipherValue>AQAAANCMnd8BFdERjHoAwE/Cl+sB ... TEUAAAA4YX9e03M+X56k+VUlWepDgrRnDw=
</CipherValue>
</CipherData>
</EncryptedData>
</connectionStrings>
<system.web>
<compilation debug="true"/>
<authentication mode="Windows"/>
</system.web>
</configuration>
Nel listato 5, dove per semplicità abbiamo omesso gran parte dei valori cifrati, possiamo notare l'informazione riguardo al provider di protezione che è stato usato. Non è presente inoltre la chiave di protezione che, nel caso del provider scelto nell'esempio, viene salvata nella Local Security Autority (LSA) di Windows.
A questo punto possiamo testare l'applicazione con il nuovo Web.Config e verificare che funzioni. Possiamo inoltre testare il decriptaggio e l'altro metodo di criptaggio.
Si possono proteggere quasi tutte le sezioni del Web.Config. Per verificare se una sezione è protetta, si può usare la proprietà IsProtected
della classe SectionInformation
. Possiamo ad esempio modificare il nostro codice in modo che operi il criptaggio solo se non è stato già operato e fare un discorso analogo per il decriptaggio.
Listato 6. Metodo Cripta() con il controllo sulla protezione già operata
public void Cripta(string protectionProvider)
{
// apriamo il file di configurazione
Configuration WebConfig = System.Web.Configuration.WebConfigurationManager.OpenWebConfiguration(Request.ApplicationPath);
// specifichiamo la sezione da criptare
ConfigurationSection sezioneProt = WebConfig.Sections["connectionStrings"];
//verifichiamo di non aver già codificato
if (!sezioneProt.SectionInformation.IsProtected)
{
// specifichiamo il protection provider da applicare alla sezione da criptare
sezioneProt.SectionInformation.ProtectSection(protectionProvider);
// salviamo il file di configurazione modificato
WebConfig.Save();
Messaggio1.Text = "Web.Config criptato con " + protectionProvider;
}
else
Messaggio1.Text = "Web.Config già criptato";
}
Se abbiamo un Web.Config in cui le stringhe di connessione sono crifrate, come è il caso del listato 4, e cerchiamo di aggiungere una nuova stringa di connessione in fase di programmazione, essa verrà automaticamente cifrata, come possiamo verificare utilizzando ad esempio il metodo aggiungiStringaConnessione()
riportato nel listato 7.
Listato 7. Metodo aggiungiStringaConnessione()
public void aggiungiStringaConnessione()
{
Configuration WebConfig = System.Web.Configuration.WebConfigurationManager.OpenWebConfiguration(Request.ApplicationPath);
ConnectionStringSettings connStrSettings = new ConnectionStringSettings("NORTHWNDConnectionString", "Data Source=.\SQLEXPRESS;AttachDbFilename="C:\SQL Server 2000 Sample Databases\NORTHWND.MDF";Integrated Security=True;Connect Timeout=30;User Instance=True");
WebConfig.ConnectionStrings.ConnectionStrings.Add(connStrSettings);
WebConfig.Save();
}
Il codice sorgente dell'esempio sviluppato in questo articolo può essere scaricato da qui.