ASP.NET

Utilizzo dell'autenticazione basata su moduli mediante SQL Server 2000

In questa pagina
ObiettiviObiettivi
Ambito di applicazioneAmbito di applicazione
Utilizzo del moduloUtilizzo del modulo
RiepilogoRiepilogo
Conoscenze necessarieConoscenze necessarie
Creazione di un'applicazione Web con una pagina di accessoCreazione di un'applicazione Web con una pagina di accesso
Configurazione dell'applicazione Web per l'autenticazione basata su moduliConfigurazione dell'applicazione Web per l'autenticazione basata su moduli
Sviluppo di funzioni per generare un hash e un valore saltSviluppo di funzioni per generare un hash e un valore salt
Creazione di un database di account utenteCreazione di un database di account utente
Utilizzo di ADO .NET per archiviare i dettagli degli account nel databaseUtilizzo di ADO .NET per archiviare i dettagli degli account nel database
Autenticazione delle credenziali utente rispetto al databaseAutenticazione delle credenziali utente rispetto al database
Test dell'applicazioneTest dell'applicazione
Altre risorseAltre risorse

Obiettivi

Il modulo consente di:

Creare un'applicazione Web che utilizza l'autenticazione basata su moduli per autenticare gli utenti per SQL Server.

Archiviare e convalidare le credenziali utente mediante gli hash delle password.

Ambito di applicazione

Le informazioni contenute in questo modulo sono valide per i seguenti prodotti e tecnologie:

Microsoft Windows® XP o Windows 2000 Server con Service Pack 3 e sistemi operativi successivi

Microsoft .NET Framework versione 1.0 con Service Pack 2 e versioni successive

Microsoft Visual Studio® 1.0 .NET e versioni successive

Microsoft Visual C#® .NET

SQL Server 2000 con Service Pack 2 e versioni successive

Utilizzo del modulo

Per trarre il massimo vantaggio dal modulo:

È necessario aver acquisito esperienza nell'utilizzo di Visual C# .NET e Visual Studio .NET.

È necessario aver acquisito esperienza nello sviluppo di applicazioni Web mediante l'utilizzo di ASP.NET.

È necessario aver acquisito esperienza nell'utilizzo di SQL Server e nella creazione di tabelle SQL Server.

È necessario aver acquisito esperienza nell'utilizzo di ADO.NET per accedere a SQL Server.

Occorre poter accedere a un'istanza di SQL Server utilizzabile per verificare l'applicazione (non deve trattarsi di un sistema di produzione).

Leggere il modulo 12 "Protezione dell'accesso ai dati" di questa guida. Vi sono incluse informazioni dettagliate sull'accesso protetto al database, sulla creazione e l'archiviazione dei valori hash delle password e sulla protezione dagli attacchi mediante inserimento di codice SQL non autorizzato.

Leggere "Procedura : Utilizzo dell'interfaccia DPAPI (archivio del computer) da ASP.NET". Vi sono incluse informazioni dettagliate sulla modalità di utilizzo di DPAPI per memorizzare in modalità protetta le stringhe di connessione a SQL Server.

Leggere "Procedura : Creazione di oggetti GenericPrincipal mediante l'autenticazione basata su moduli". Vi sono incluse informazioni dettagliate sulla costruzione di un ticket di autenticazione basata su moduli che include i dettagli sui ruoli di un utente.

Riepilogo

L'autenticazione basata su moduli di ASP.NET consente agli utenti di identificarsi immettendo le credenziali (nome utente e password) in un modulo Web. Quando l'applicazione Web riceve queste credenziali, può autenticare l'utente controllando la combinazione delle credenziali rispetto a un'origine dati.

In questo modulo viene descritto come archiviare le credenziali utente in modalità protetta in Microsoft® SQL Server™ utilizzando l'hash delle password, nonché come autenticare gli utenti rispetto a un database di account contenuto in SQL Server.

Conoscenze necessarie

In relazione all'archiviazione in modalità protetta delle credenziali utente occorre considerare due concetti chiave:

Archiviazione di digest di password. Per motivi di sicurezza, è consigliabile non archiviare le password in formato testo non crittografato nel database. In questo modulo viene descritto come creare e archiviare un hash unidirezionale della password di un utente anziché la password stessa. Questo approccio è preferibile all'archiviazione di una versione crittografata della password dell'utente in quanto consente di evitare i problemi di gestione della chiave associati alle tecniche di crittografia.
Per assicurare una maggiore protezione e ridurre il rischio associato agli attacchi al dizionario, nel metodo descritto in questo modulo viene assegnato alla password un valore salt, ovvero un numero casuale generato a livello di crittografia, prima di creare il relativo hash.

Importante: l'unico svantaggio, quando non si archiviano le password nel database, è rappresentato dal fatto che, se l'utente dimentica la password, non è possibile recuperarla. E’ quindi consigliabile che l'applicazione utilizzi i suggerimenti per le password e li archivi nel digest di password all'interno del database.

Convalida dell'input dell'utente. Quando l'input dell'utente viene passato ai comandi SQL, ad esempio come rappresentazione formale di stringa nelle istruzioni di confronto o di corrispondenza dei modelli, è necessario prestare particolare attenzione alla convalida dell'input, Questo per verificare che i comandi risultanti non contengano errori di sintassi e anche per impedire che un pirata informatico possa causare l'esecuzione non autorizzata di comandi SQL nell'applicazione. La convalida del nome utente specificato durante un processo di accesso è particolarmente importante, in quanto il modello di protezione dell'applicazione dipende completamente dalla possibilità di autenticare gli utenti in modo corretto e sicuro.

Per ulteriori informazioni sulla convalida dell'input dell'utente per i comandi SQL e le funzioni di convalida, vedere "Attacchi mediante inserimento di codice SQL non autorizzato" nel modulo 12, "Protezione dell'accesso ai dati".

Creazione di un'applicazione Web con una pagina di accesso

Questa procedura consente di creare una semplice applicazione Web Visual C# contenente una pagina di accesso in cui l'utente può inserire il nome utente e la password.

Per creare un'applicazione Web con una pagina di accesso

1.

Avviare Visual Studio .NET e creare una nuova applicazione Web ASP.NET Visual C# denominata FormsAuthSQL.

2.

Utilizzare Esplora soluzioni per rinominare WebForm1.aspx come Logon.aspx.

3.

Aggiungere a Logon.aspx i controlli elencati nella tabella 1 per creare un semplice modulo di accesso.

Tabella 1: Controlli di Logon.aspx

Tipo di controlloTestoID

Etichetta

Nome utente

-

Etichetta

Password

-

Casella di testo

-

txtUserName

Casella di testo

-

txtPassword

Pulsante

Registrati

btnRegister

Pulsante

Accedi

btnLogon

Etichetta

-

lblMessage

La pagina Web sarà simile a quella illustrata nella figura 1.

Modulo Web della pagina di accesso

Figura 1
Modulo Web della pagina di accesso

4.

Impostare la proprietà TextMode di txtPassword su Password.

Configurazione dell'applicazione Web per l'autenticazione basata su moduli

Questa procedura consente di modificare il file web.config dell'applicazione in modo da configurare quest'ultima per l'autenticazione basata su moduli.

Per configurare l'applicazione Web per l'autenticazione basata su moduli

1.

Utilizzare Esplora soluzioni per aprire il file web.config.

2.

Individuare l'elemento <authentication> e modificare l'attributo mode impostandolo su Forms.

3.

Aggiungere l'elemento <forms> che segue come figlio dell'elemento <authentication> e impostare gli attributi loginUrl, name, timeout e path nel modo seguente:

<authentication mode="Forms"> 
  <forms loginUrl="logon.aspx" name="sqlAuthCookie" timeout="60" path="/"> 
  </forms> 
</authentication> 

4.

Aggiungere l'elemento <authorization> riportato di seguito sotto l'elemento <authentication>. In questo modo, l'accesso all'applicazione verrà consentito solo agli utenti autenticati. L'attributo loginUrl dell'elemento <authentication>, definito in precedenza, reindirizzerà le richieste non autenticate alla pagina logon.aspx.

<authorization>  
  <deny users="?" /> 
  <allow users="*" /> 
</authorization> 

Sviluppo di funzioni per generare un hash e un valore salt

Questa procedura consente di aggiungere due metodi all'applicazione, uno per generare un valore salt casuale e uno per creare un hash in base alla password specificata e al valore salt.

Per sviluppare funzioni per generare un hash e un valore salt

1.

Aprire Logon.aspx.cs e aggiungere le istruzioni using riportate di seguito all'inizio del file, sotto le istruzioni using esistenti.

using System.Security.Cryptography; 
using System.Web.Security; 

2.

Aggiungere alla classe WebForm1 il metodo statico riportato di seguito per generare un valore salt casuale e restituirlo come stringa codificata Base 64.

private static string CreateSalt(int size) 
{ 
  // Generate a cryptographic random number using the cryptographic 
  // service provider 
  RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider(); 
  byte[] buff = new byte[size]; 
  rng.GetBytes(buff); 
  // Return a Base64 string representation of the random number 
  return Convert.ToBase64String(buff); 
} 

3.

Aggiungere il metodo statico riportato di seguito per generare un valore hash in base alla password specificata e al valore salt.

private static string CreatePasswordHash(string pwd, string salt) 
{ 
  string saltAndPwd = String.Concat(pwd, salt); 
  string hashedPwd =  
        FormsAuthentication.HashPasswordForStoringInConfigFile( 
                                             saltAndPwd, "SHA1"); 
  return hashedPwd; 
} 

Creazione di un database di account utente

Questa procedura consente di creare in SQL Server un nuovo database di account utente contenente contiene una singola tabella degli utenti e una stored procedure utilizzata per eseguire query al database utente.

Per creare un database di account utente

1.

Scegliere Microsoft SQL Server dal menu Programmi, fare clic su Query Analyzer, quindi connettersi al computer SQL Server locale.

2.

Immettere lo script SQL riportato di seguito. Tenere presente che, alla fine dello script, è necessario sostituire "LocalMachine" con il nome del computer in uso.

USE master 
GO 
-- create a database for the security information 
IF EXISTS (SELECT * FROM   master..sysdatabases WHERE   
name = 'UserAccounts') 
  DROP DATABASE UserAccounts 
GO 
CREATE DATABASE UserAccounts 
GO 
USE UserAccounts 
GO 
CREATE TABLE [Users] ( 
  [UserName] [varchar] (255) NOT NULL , 
  [PasswordHash] [varchar] (40) NOT NULL , 
  [salt] [varchar] (10) NOT NULL, 
  CONSTRAINT [PK_Users] PRIMARY KEY  CLUSTERED  
  ( 
    [UserName] 
  )  ON [PRIMARY]  
) ON [PRIMARY] 
GO 
-- create stored procedure to register user details 
CREATE PROCEDURE RegisterUser 
@userName varchar(255), 
@passwordHash varchar(40), 
@salt varchar(10) 
AS 
INSERT INTO Users VALUES(@userName, @passwordHash, @salt) 
GO 
-- create stored procedure to retrieve user details 
CREATE PROCEDURE LookupUser 
@userName varchar(255) 
AS 
SELECT PasswordHash, salt  
FROM Users 
WHERE UserName = @userName 
GO 
-- Add a login for the local ASPNET account 
-- In the following statements, replace LocalMachine with your  
-- local machine name 
exec sp_grantlogin [LocalMachine\ASPNET] 
-- Add a database login for the UserAccounts database for the 
ASPNET account 
exec sp_grantdbaccess [LocalMachine\ASPNET] 
-- Grant execute permissions to the LookupUser and RegisterUser stored 
procs 
grant execute on LookupUser to [LocalMachine\ASPNET] 
grant execute on RegisterUser to [LocalMachine\ASPNET] 

3.

Eseguire la query per creare il database UserAccounts.

4.

Chiudere Query Analyzer.

Utilizzo di ADO .NET per archiviare i dettagli degli account nel database

Questa procedura consente di modificare il codice dell'applicazione Web per archiviare il nome utente specificato, l'hash di password generato e il valore salt nel database.

Per utilizzare ADO .NET per archiviare i dettagli degli account nel database

1.

Tornare a Visual Studio .NET e fare doppio clic sul pulsante Registrati nel modulo Web per creare un gestore eventi ButtonClick.

2.

Aggiungere al metodo il seguente codice:

string salt = CreateSalt(5); 
string passwordHash = CreatePasswordHash(txtPassword.Text,salt); 
try 
{ 
  StoreAccountDetails( txtUserName.Text, passwordHash, salt); 
} 
catch(Exception ex) 
{ 
  lblMessage.Text = ex.Message; 
} 

3.

Aggiungere l'istruzione using riportata di seguito all'inizio del file, sotto le istruzioni using esistenti.

using System.Data.SqlClient; 

4.

Aggiungere il metodo StoreAccountDetails mediante il codice riportato di seguito, che utilizza ADO .NET per eseguire la connessione al database UserAccounts e consente di archiviare il nome utente, l'hash di password e il valore salt nella tabella Users.

private void StoreAccountDetails( string userName,  
                                  string passwordHash,  
                                  string salt ) 
{ 
  // See "How To Use DPAPI (Machine Store) from ASP.NET" for information  
  // about securely storing connection strings. 
  SqlConnection conn = new SqlConnection( "Server=(local);" + 
                                          "Integrated Security=SSPI;" + 
                                          "database=UserAccounts"); 
  SqlCommand cmd = new SqlCommand("RegisterUser", conn ); 
  cmd.CommandType = CommandType.StoredProcedure; 
  SqlParameter sqlParam = null; 
  sqlParam = cmd.Parameters.Add("@userName", SqlDbType.VarChar, 255); 
  sqlParam.Value = userName; 
  sqlParam = cmd.Parameters.Add("@passwordHash ", SqlDbType.VarChar, 40); 
  sqlParam.Value = passwordHash; 
  sqlParam = cmd.Parameters.Add("@salt", SqlDbType.VarChar, 10); 
  sqlParam.Value = salt; 
  try 
  { 
    conn.Open(); 
    cmd.ExecuteNonQuery(); 
  } 
  catch( Exception ex ) 
  { 
    // Code to check for primary key violation (duplicate account name) 
    // or other database errors omitted for clarity 
    throw new Exception("Exception adding account. " + ex.Message); 
  } 
  finally 
  { 
    conn.Close(); 
  }  
}

Autenticazione delle credenziali utente rispetto al database

Questa procedura consente di sviluppare un codice ADO .NET per eseguire la ricerca del nome utente specificato nel database e convalidare la password indicata, in base alla corrispondenza degli hash di password.

Nota: a questo punto, in vari scenari di autenticazione basata su moduli in cui si utilizza l'autorizzazione .NET basata sui ruoli, è possibile anche recuperare dal database i ruoli ai quali l’utente appartiene. Tali ruoli possono essere poi utilizzati allo scopo di generare un oggetto GenericPrincipal da associare alle richieste Web autenticate per motivi di autorizzazione .NET.

Per ulteriori informazioni sulla creazione di un ticket di autenticazione basata su moduli che include i dettagli sui ruoli di un utente, vedere "Procedura – Creazione di oggetti GenericPrincipal mediante l'autenticazione basata su moduli" nella sezione relativa ai riferimenti di questa guida.

Per autenticare le credenziali utente rispetto al database

1.

Tornare a Logon.aspx.cs e aggiungere il metodo dell'helper privato VerifyPassword come indicato nel seguente codice:

private bool VerifyPassword(string suppliedUserName,  
                            string suppliedPassword ) 
{  
  bool passwordMatch = false; 
  // Get the salt and pwd from the database based on the user name. 
  // See "How To: Use DPAPI (Machine Store) from ASP.NET," "How To: Use 
  DPAPI  
  // (User Store) from Enterprise Services," and "How To: Create a DPAPI  
  // Library" for more information about how to use DPAPI to securely 
  store  
  // connection strings. 
  SqlConnection conn = new SqlConnection( "Server=(local);" +  
                                          "Integrated Security=SSPI;" + 
                                          "database=UserAccounts"); 
  SqlCommand cmd = new SqlCommand( "LookupUser", conn ); 
  cmd.CommandType = CommandType.StoredProcedure; 
  SqlParameter sqlParam = cmd.Parameters.Add("@userName",  
                                             SqlDbType.VarChar, 255); 
  sqlParam.Value = suppliedUserName; 
  try 
  { 
    conn.Open(); 
    SqlDataReader reader = cmd.ExecuteReader(); 
    reader.Read(); // Advance to the one and only row 
    // Return output parameters from returned data stream 
    string dbPasswordHash = reader.GetString(0); 
    string salt = reader.GetString(1); 
    reader.Close(); 
    // Now take the salt and the password entered by the user 
    // and concatenate them together. 
    string passwordAndSalt = String.Concat(suppliedPassword, salt); 
    // Now hash them 
    string hashedPasswordAndSalt =        
               FormsAuthentication.HashPasswordForStoringInConfigFile( 
                                               passwordAndSalt, "SHA1"); 
    // Now verify them. 
    passwordMatch = hashedPasswordAndSalt.Equals(dbPasswordHash); 
  } 
  catch (Exception ex) 
  { 
    throw new Exception("Execption verifying password. " + ex.Message); 
  } 
  finally 
  { 
    conn.Close(); 
  } 
  return passwordMatch; 
} 

Test dell'applicazione

Questa procedura consente di eseguire il test dell'applicazione. Eseguire la registrazione di un utente, che porterà all’aggiunta del nome utente, dell'hash di password e del valore salt alla tabella Users nel database UserAccounts. Quindi si dovrà eseguire l'accesso dello stesso utente per assicurare il corretto funzionamento delle routine di verifica della password.

Per eseguire il test dell'applicazione

1.

Tornare al modulo di accesso e fare doppio clic sul pulsante Accedi per creare un gestore dell’evento ButtonClick.

2.

Aggiungere il codice riportato di seguito al gestore evento ButtonClick Accedi per chiamare il metodo VerifyPassword e visualizzare un messaggio in cui viene indicato se il nome utente e la password specificati sono validi o meno.

bool passwordVerified = false; 
try 
{ 
   passwordVerified = VerifyPassword(txtUserName.Text,txtPassword.Text); 
} 
catch(Exception ex) 
{ 
  lblMessage.Text = ex.Message; 
  return; 
} 
if (passwordVerified == true ) 
{ 
  // The user is authenticated 
  // At this point, an authentication ticket is normally created 
  // This can subsequently be used to generate a GenericPrincipal 
  // object for .NET authorization purposes 
  // For details, see "How To: Use Forms authentication with  
  GenericPrincipal  
  // objects 
  lblMessage.Text = "Logon successful: User is authenticated"; 
} 
else 
{ 
  lblMessage.Text = "Invalid username or password"; 
} 

3.

Scegliere Genera soluzione dal menu Genera.

4.

In Esplora soluzioni fare clic con il pulsante destro del mouse su logon.aspx, quindi scegliere Visualizza nel browser.

5.

Immettere un nome utente e una password, quindi fare clic su Registrati.

6.

Utilizzare SQL Server Enterprise Manager per visualizzare il contenuto della tabella Users. Verrà visualizzata una nuova riga per il nuovo nome utente insieme all'hash di password generato.

7.

Tornare alla pagina Web di accesso, immettere nuovamente la password, quindi fare clic su Accedi. Verrà visualizzato il messaggio "Logon successful: User is authenticated".

8.

A questo punto immettere una password non valida, lasciando invariato il nome utente. Verrà visualizzato il messaggio "Invalid username or password".

9.

Chiudere Internet Explorer.

Altre risorse

Per ulteriori informazioni, vedere le seguenti risorse:

"Procedura - Utilizzo dell'interfaccia DPAPI (archivio del computer) da ASP.NET"

"Procedura - Creazione di oggetti GenericPrincipal mediante l'autenticazione basata su moduli"

"Attacchi mediante inserimento di codice SQL non autorizzato" nel modulo 12, "Protezione dell'accesso ai dati"


**
In questo articolo
**