Utilizziamo cookie tecnici e di profilazione (anche di terze parti) per migliorare la tua esperienza su questo sito. Continuando la navigazione accetti l'utilizzo dei cookie; in alternativa, leggi l'informativa e scopri come disabilitarli.

 

Obiettivi:

Comprendere il significato di consistenza e accesso concorrente

Comprendere il concetto di transazione e il suo utilizzo

Comprendere la logica di accesso ai dati

Comprendere i meccanismi di protezione dei dati e di protezione fisica della base di dati

 1       Introduzione

All'interno delle architetture applicative i database ricoprono un ruolo di primaria importanza.

Sempre più numerose (se non addirittura la maggioranza) sono infatti le applicazioni che demandano a una base di dati la gestione delle informazioni, che, visti gli ulti­mi sviluppi tecnologici, possono ormai essere di qualsiasi tipo, dalle immagini alla musica, dall'XML al semplice testo.

Poiché le informazioni sono diventate a buon diritto una delle principali risorse di un'azienda (qualcuno parla dei nostri tempi come "era dell’informazione"), èchiaro che la loro protezione e tutela è diventata una priorità.

Tutto questo rientra nella più generale tematica della sicurezza e della concorrenza nelle basi di dati.

Qui ci occuperemo di fornire una descrizione dei principali concetti che sottostanno a queste tematiche, delle tecniche utilizzate per implementarli e di alcune delle strategie più utilizzate in merito alla sicurezza dei database.

La trattazione cercherà di essere il più possibile "tecnologicamente neutrale": cercheremo quindi di non riferirci a un DBMS (Database Management System)specifico, ma di elencare le caratteristiche comuni a tutti. Il primo aspetto di cui ci occuperemo è quello dell'accesso concorrente ai dati e delle transazioni.

2      Problemi di concorrenza: accesso concorrente e transazioni

Le informazioni utilizzate dalle applicazioni hanno una rilevanza fondamentale in moltissime attività, da quelle commerciali a quelle della pubblica amministrazione, per finire con quelle legate al mondo del divertimento.

Ultimamente stanno conoscendo un grande successo i videogame distribuiti in rete, che consentono a giocatori non fisicamente vicini di interagire tra di loro. Per gestire sistemi di questo tipo vengono mantenute e scambiate moltissime informazioni.

Insieme a questo fenomeno, lo sviluppo tecnologico ha reso la multiutenza delle applicazioni un aspetto abituale. Sono ormai diffuse applicazioni utilizzate da diversi utenti, soprattutto quelle che sono accessibili da web (web application), che hanno necessità di accedere alle informazioni, modificarle ed eliminarle.

È chiaro che rispetto a un'applicazione mono-utente, dove le modifiche e gli accessi ai dati provengono da un'unica fonte, il paradigma della multiutenza rappresenta un notevole incremento in termini di complessità dell'applicazione.

Prima di addentrarci nelle tecniche che consentono la gestione di questa complessità, però, è il caso di trattare un argomento fondamentale: il concetto di consistenza.

2.1      Consistenza

Una base di dati è un contenitore di informazioni atto a descrivere una particolare realtà. Prendiamo in esame, per esempio, una collezione di dischi. Se volessimo creare una base di dati che contenesse tutte le informazioni sulla nostra collezione, dovremmo racchiudere al suo interno elementi come i titoli degli album, gli artisti, l'anno di pubblicazione, il genere e così via.

Il risultato ottenuto con questa operazione è detto modello.

Un modello èuna rappresentazione di una particolare realtà ottenuta mediante un formalismo.

Nel mondo delle basi di dati, il formalismo con cui descriviamo la realtà è un insieme di tabelle e di relazioni tra di esse. Il linguaggio SQLè invece utilizzato per esprimere (e quindi implementare) il formalismo. In questa sede non ci occuperemo in modo dettagliato dei passaggi necessari per la creazione di un modello. Quello che ci interessa è sottolineare che la nostra collezione di dischi può essere modellata sfruttando il formalismo delle basi di dati (entità/relazione).

Continuando a fare riferimento allo stesso esempio, supponiamo di voler creare un servizio di prestito dei nostri CD. Quello che ci serve è un elenco affidabile dei CDche ci consenta di sapere quali sono "fuori", ovvero in prestito, e quali invece sono "in casa". Essendo la nostra collezione molto ampia, decidiamo di affidarci a una base di dati, nella quale indicheremo, tra le altre cose, la posizione dei vari dischi negli scaffali e se sono stati prestati (e non sono quindi presenti) o meno.

Nel momento in cui cominciamo a erogare il nostro servizio, la nostra attenzione sarà principalmente rivolta a segnare accuratamente i prestiti, in modo che non ci siano discrepanze tra quanto contenuto nella base di dati e la situazione reale della nostra collezione.

Supponiamo che a un certo punto venga concesso un prestito senza effettuarne la registrazione nella base di dati. Chiameremo il disco in oggetto D1. D1 non è presente fisicamente nella collezione, ma è invece indicato come presente nella base di dati. Che cosa accadrà quindi se un altro utente dovesse chiedere proprio D1? Secondo le procedure prestabilite, controlleremo nella base di dati, all'interno della quale scopriremo che D1 è presente nella collezione, quindi cercheremo di recuperarlo seguendo le indicazioni di posizionamento contenute nella base di dati. Arrivati però allo scaffale giusto, noteremo che D1, ovviamente, non è presente. Questa situazione di sfasamento tra realtà e modello prende il nome di inconsistenza. A seguito ò possiamo definire la consistenza:

 

Una base di dati si dice consistente se non ci sono discrepanze tra la realtà fisica e il modello che la rappresenta.

Questa definizione ci aiuta a comprendere quanto sia rilevante il fenomeno dell'inconsistenza. Una base di dati che non rispecchia la realtà può comportare seri problemi quando essa è il perno su cui si basa l'erogazione di un servizio, come nel nostro esempio del servizio di prestito CD.

Il problema della consistenza puo' derivare anche da errori nello sviluppo del database e delle applicazioni che lo utilizzano: in particolare nel caso di database non normalizzati puo' accadere che eseguendo una update su un campo di una tabella che non normalizzata ci troviamo in una situazione di inconsistenza: prendiamo ad esempio questo caso:

 

Nome

Cap

Città

Marco

16100

Genova

Filippo

16100

Genova

Elena

20100

Milano

Potrebbero esserci problemi se da una applicazione si potesse cambiare il CAP della tabella senza modificare la città: mettendo 17100 in uno dei record risulterebbe una inconsistenza tra CAP e Città: a quel punto non si potrebbe più sapere quale dei due è il dato attendibile.

 

Estendiamo ora ulteriormente il nostro esempio riguardante i CD. Supponiamo che, per l'elevato numero di richieste, decidiamo di coinvolgere nella nostra piccola attività un'altra persona, che potrà gestire i prestiti avendo anch'essa accesso alla base di dati. Prendiamo in considerazione il solito disco D1 e supponiamo che nello stesso momento due persone vengano a richiederlo: chiaramente uno verrà servito da noi e l'altro dal nostro aiutante. A questo punto che cosa accadrebbe? Nel momento in cui riceveremo la richiesta del primo cliente andremo a cercare il CD nello scaffale, ma contemporaneamente il nostro aiutante farà lo stesso, con l'unica differenza che il database segnalerà che il CDè presente sebbene esso sia stato preso un istante prima. Anche in questo caso assistiamo a un fenomeno di inconsistenza. Si potrebbe obiettare che casi di questo tipo sono rari, se non addirittura sfortunate coincidenze. Se però al posto del nostro piccolo servizio di prestito di dischi prendessimo in considerazione un sito web di vendita on line a catalogo, potremmo facilmente capire guanto siano frequenti problemi di questo tipo.

E chiaro, quindi, che l'implementazione di una serie di controlli, regole e vincoli è di fondamentale importanza nell'ottica del mantenimento della coerenza delle basi di dati.

Nei paragrafi seguenti presenteremo una serie di metodi e concetti aventi in comune il mantenimento della consistenza della base di dati.

2.2      Accesso concorrente

L'accesso concorrente è sicuramente una delle cause principali dei problemi di inconsistenza delle basi di dati.

Come abbiamo potuto vedere nei precedenti semplici esempi, fintanto che alla base di dati accede un solo utente, a meno di particolari malfunzionamenti della base di dati stessa (per esempio, immissioni di dati che non vanno a buon fine, ma che risultano comunque effettuate), la consistenza viene mantenuta senza particolari sforzi o senza la necessità di implementare regole o sistemi che la salvaguardino.

Tuttavia, nel momento in cui gli utenti della base di dati cominciano ad aumentare, i problemi aumentano di pari passo, rendendo la salvaguardia della consistenza un problema di non secondaria importanza.

 

La situazione in cui più utenti accedono a una stessa risorsa nello stesso istante prende il nome di accesso concorrente.

 

È chiaro che, in una condizione di accesso concorrente, le operazioni che effettua un utente sulla base di dati possono influenzare, in qualche modo, le operazioni degli altri utenti che in quel momento stanno avendo accesso alla stessa risorsa. Come nell'esempio del paragrafo precedente, nel quale due utenti accedono alla base di dati modificandone lo stato, esiste il rischio che le modifiche effettuate da un utente influiscano sull'operato di un altro.

Nella figura sottostante è mostrato un caso di accesso concorrente: due utenti cercano di aggiornare gli stessi dati all'interno delle stesse tabelle di un database.


Supponiamo che Utente 1 si trovi a Roma e Utente 2 a Milano e che entrambi, nello stesso momento, abbiano contrassegnato lo stesso CD, l'uno come "venduto" e l'altro come "non venduto". Qual è lo stato del CD, supponendo che entrambe le operazioni siano avvenute nello stesso momento?

Per risolvere i problemi di accesso concorrente sono stati sviluppati sistemi basati su blocchi (lock), per impedire a entità differenti di accedere contemporaneamente alle stesse risorse. Molto genericamente possiamo dire che questi sistemi funzionano come semafori, regolando il "traffico" verso le risorse e i vari turni di accesso da parte delle varie entità. Nei prossimi paragrafi verrà illustrato in dettaglio il funzionamento dei lock e il loro utilizzo all'interno delle operazioni nelle basi di dati.

 

Transazioni

Fino a questo punto abbiamo potuto valutare i rischi legati alle operazioni sui database effettuate da più utenti contemporaneamente. In particolare, le operazioni più "a rischio" sono le operazioni di lettura e scrittura, poiché sono quelle in cui l'utente ha accesso diretto ai dati.

 

L'esempio fornito nel paragrafo dedicato alla consistenza risulta però essere limitato. Un'operazione di scrittura di un dato, infatti, si risolve in genere in pochissimo tempo, riducendo quindi drasticamente la possibilità che un altro utente risenta di problemi di consistenza.

Per introdurre il concetto delle transazioni, quindi, prendiamo in considerazione uno sviluppo ulteriore del nostro esempio. Supponiamo di decidere che il nostro servizio di prestito dischi, ormai cresciuto di popolarità, diventi un servizio di vendita on line di dischi. A questo scopo verrà predisposto un servizio di e-commerce (electroníc commerce, commercio elettronico), composto da un database contenente i dati e un sito web di interfaccia per gli utenti.

Al momento della progettazione del nostro servizio dovremo prevedere una serie di processi, tra cui sicuramente quello di acquisto. Nella definizione del processo di acquisto sarà necessario prendere in considerazione tutte le attività che devono essere svolte contestualmente all'acquisto di un disco da parte di un cliente. Supponiamo di schematizzare questo processo come mostrato nella figura seguente.


Come possiamo notare, questo processo è piuttosto articolato. Per meglio comprenderne la struttura, l'intero processo di acquisto è stato suddiviso in due sottoprocessi:

sottoprocesso di produzione dell'ordine, nel corso del quale l'utente sceglie il prodotto da acquistare, inserisce i propri dati di consegna (indirizzo, nome ecc.) e i dati per il pagamento (per semplicità, consideriamo che il pagamento possa essere effettuato solo mediante carta di credito);

sottoprocesso di gestione dell'ordine, nel corso del quale all'utente non sono richieste ulteriori azioni. La gestione dell'ordine avverrà automaticamente e consisterà nell'aggiornamento del magazzino e in tutte le operazioni antecedenti alla consegna (preparazione etichette di spedizione, imballaggio ecc.).

Tra questi due sottoprocessi c'è fondamentalmente una grande differenza: il primo non va a modificare in alcun modo il magazzino e quindi la disponibilità dei prodotti. Fino a quando l'utente non conferma l'ordine e il suo pagamento non viene autorizzato, il magazzino non è aggiornato. In questo senso, anche se dieci utenti dovessero connettersi nello stesso istante e acquistare lo stesso prodotto, non si verificherebbero problemi rilevanti di consistenza. Il secondo processo invece è cruciale. Nel momento in cui l'utente conferma l'ordine (autorizzandone quindi il pagamento) inizia il processo di gestione che, in questo caso, andrà anche a modificare le giacenze di magazzino, influenzando quindi gli eventuali acquisti di altri utenti collegati. Vediamo di capirne il motivo.

Supponiamo che due utenti, A e B, decidano di acquistare il disco D1 attraverso il nostro servizio di vendita dischi on line. A questo scopo si connetteranno al sito web e percorreranno tutto il sottoprocesso di produzione dell'ordine. Supponiamo a questo punto che, a causa delle elevate richieste del prodotto D1, ne sia rimasta una sola copia a disposizione. Fino al momento della conferma dell'ordine, il prodotto risulta disponibile per entrambi.

Che cosa accadrà al momento della conferma dell'acquisto? Il problema cruciale risiede nella modifica dei dati di magazzino. Nel momento in cui verrà decrementata di una unità la disponibilità di D1 (che diventerà quindi pari a 0), uno degli utenti si troverà in una condizione di inconsistenza, poiché avrà effettuato (e pagato) l'ordine di un prodotto in realtà non più disponibile.

I motivi che possono causare situazioni simili sono semplici. Supponiamo infatti che la validazione del pagamento dell'utente A richieda, per qualche motivo, molto più tempo della corrispondente attività per l'utente B. L'utente B passerebbe prima di A alla fase di modifica della disponibilità di magazzino, causando una situazione di inconsistenza nel momento in cui A arriverà allo stesso punto. Questa situazione è schematizzata nella figura seguente.


Le caratteristiche di una transazione possono essere individuate nelle parole che compongono l'acronimo ACID:

atomicità (Atomícity). Quando si parla di transazione si vuole indicare una serie di operazioni che si eseguono completamente o non si eseguono per niente. Una transazione viene o non viene portata a termine, non esistono dati lasciati a metà o inconsistenti;

consistenza (Consístency). Prima dell'inizio della transazione e una volta conclusa, la base di dati è sempre in uno stato consistente;

isolamento (Isolation). Le operazioni eseguite all'interno della transazione (supponiamo degli aggiornamenti) non modificano i dati visti dall'esterno della transazione, che rimarranno sempre in uno stato consistente finché la transazione non sarà conclusa. Nessuna operazione al di fuori della transazione può quindi vedere i dati in uno stato intermedio;

permanenza o durevolezza (Durability). Una volta che la transazione si è conclusa in modo corretto, i dati sono nuovamente in uno stato consistente e non possono essere ripristinati allo stato precedente.

 

Una transazione conclusa correttamente è un impegno (commit in inglese) a mantenere i dati a prescindere da errori di qualsiasi tipo.

 

Per esempio, se per qualsiasi motivo durante una transazione, come vedremo più avanti, si verifica un errore, se l'operazione si è conclusa con successo siamo certi che tutti i dati sono stati aggiornati correttamente, senza lasciare alcuna operazione in sospeso. Una transazione può altresì non concludersi correttamente (come nel caso dell'utente A nell'esempio precedente): in questo caso la transazione si dice abortita. In caso di errore, quindi, il sistema effettua un ritorno allo stato precedente la transazione (rollback) e non viene effettuato alcun cambiamento nella base di dati.

2.3      Transazioni e accesso concorrente

Se tutte le transazioni avvenissero in modo sequenziale, la base di dati si troverebbe sempre in uno stato consistente. Questa eventualità si verifica tuttavia molto di rado. Supponiamo di dover calcolare, per una banca, il bilancio di un conto corrente e il totale dei bilanci dei conti correnti, sapendo che in ogni istante avvengono operazioni che aumentano (versamenti) o diminuiscono (prelievi, assegni incassati ecc.) l'ammontare all'interno di conti.

Se più transazioni si sovrapponessero in modo non controllato, rischieremmo di ottenere dati non veritieri ma falsati.

Esistono tre tipi principali di anomalie che si possono verificare a causa di accessi concorrenti: perdite di aggiornamenti, letture non riproducibili e letture fantasma.

 

Si verifica una perdita di aggiornamenti (anomalia di tipo scrittura-scrittura) quando, per esempio, due transazioni (T1 e T2) vengono eseguite contemporaneamente.

  • T1 aggiorna un determinato dato X.
  • T2 aggiorna lo stesso dato X.

L'utente A ha inserito come costo per i CD un importo negativo e la base di dati restituisce un errore.

T1 quindi fallisce e la base di dati riporta (rollback) il sistema allo stato consistente anteriore all'inizio della transazione T1.

Anche se la transazione T2 è terminata con successo, visto che la transazione T1 non è andata a buon fine, non verrà contrassegnata.

 

Si verificano letture non riproducibili (anomalia lettura-scrittura) quando, per esempio, due transazioni (T1 e T2) vengono eseguite nello stesso momento.

  • T1 legge un determinato dato X
  • T2 aggiorna lo stesso dato X e si conclude correttamente effettuando il commit.
  • T1 (stessa transazione di prima) legge nuovamente il dato X.

La transazione T1, leggendo nuovamente il dato, lo troverà modificato.

 

Esempio di lettura non riproducibile

La transazione T1 comprende tre operazioni: lettura dei costi da listino del CDdei Foo Fighters, aggiornamento nelle tabelle dei negozi del suo costo e nuova lettura del costo per inserirlo all'interno del sito web e pubblicizzarlo.

La transazione T2 comprende unicamente l'aggiornamento dei costi del CDdei Foo Fighters.

Se T1 legge un costo, per esempio di 20 euro, nel momento in cui l'altra transazione T2 è già conclusa, il costo non sarà più lo stesso (e saranno presenti differenze tra i dati nei negozi e quelli riportati nel sito web!).

 

Si verificano letture fantasma (anomalia scrittura-lettura) quando, per esempio, due transazioni (T1 e T2), eseguite nello stesso momento, avvengono nel modo illustrato di seguito.

  • T1 modifica un dato X.
  • T2 legge il dato X modificato da TI.
  • La transazione T1 fallisce.

 

Esempio di lettura fantasma

La transazione T1 comprende una sola operazione, ovvero si occupa di contrasse­gnare un CD come pagato da un cliente.

La transazione T2 si occupa solo dell'operazione di lettura dei CDpagati da un cliente.

Se T1 contrassegna un CD come pagato e in realtà fallisce, T2 indicherebbe (erroneamente!) che il CD è stato registrato come pagato dal cliente, quando in realtà all'interno della base di dati non è stato salvato.

 

Una delle soluzioni adottate per evitare le anomalie sulla base di dati in caso di operazioni concorrenti sono i lock (a cui abbiamo accennato in precedenza). Esistono due tipi di lock:

 

• lock in lettura, che vengono richiesti dalle transazioni ogniqualvolta devono svolgere operazioni di lettura;

 

• lock in scrittura, che vengono richiesti dalle transazioni ogniqualvolta devono effettuare operazioni in scrittura.

 

Un lock in scrittura è detto anche lock esclusivo poiché, quando una transazione richiede questo tipo di lock, nessun'altra può accedere ai dati.

Un lock in lettura è compatibile con altri lock in lettura (che quindi possono avvenire simultaneamente) ma non è compatibile con lock in scrittura, che quindi devono attendere per poter essere eseguiti.

Ovviamente, se una transazione deve attendere per poter essere eseguita e sono presenti molte transazioni nel sistema, è possibile raggiungere una fase di stallo o di blocco critico, indicata in inglese con il termine dead-lock.

Vediamo un esempio di situazione di stallo.

  • T1 chiede e ottiene un lock in lettura per il dato X1.
  • T2chiede e ottiene un lock in lettura per il dato X2.
  • T1 chiede un lock in scrittura per il dato X2 ma viene messa in attesa perché il dato è ancora bloccato da T2 in lettura.
  • T2 chiede un lock in scrittura per il dato X1 ma viene messa in attesa perché il dato è ancora bloccato da T1 in lettura.

A questo punto nessuna delle due operazioni potrà essere eseguita poiché entrambe sono in attesa di una risorsa posseduta dall'altra.

Per risolvere i dead-lock si può procedere con la terminazione di tutti i processi coinvolti nello stallo, oppure se ne  può terminare uno alla volta fino a sciogliere il nodo. È sempre opportuno prestare molta attenzione nella scelta del processo da terminare, in quanto si potrebbero verificare anomalie di dati.

Il problema maggiore pero' non è tanto quello di risolvere il dead-lock, quanto quello di accorgersi della sua presenza.

2.4      Utilizzo delle transazioni

Tutti i database professionali consentono l'utilizzo delle transazioni per gestire le operazioni, anche se esistono alcune differenze di sintassi per eseguirle.

Di seguito viene presentato un esempio di utilizzo delle transazioni all'interno di uno dei database commerciali più noti, SQL Server 2005.

La sintassi necessaria per iniziare una transazione in SQL Server è la seguente:

 

BEGIN TRANSACTION <nome Transazione>

 

Mentre per concludere la transazione in modo positivo (salvando quindi i dati) si utilizza:

 

COMMIT TRANSACTION <nome Transazione>

 

Per evitare che la transazione venga salvata, si utilizza invece il comando seguente:

 

ROLLBACK TRANSACTION <nome Transazione>

 

Vediamo ora, di seguito, un esempio concreto.

Esempio di utilizzo di transazioni

Per prima cosa, ci occuperemo di creare una semplice tabella contenente due campi, un campo incrementale (ID) e un campo Email,dove andremo a salvare alcune ipo­tetiche e-mail.

Nel listato riportato di seguito viene eseguita una serie di operazioni all'interno della transazione e, in particolare, vengano effettuate la creazione della tabella, l'inseri­mento di un record e l'aggiornamento del record appena inserito.

 

/* Inizio la transazione */
BEGIN TRANSACTION TR1
 
/* Creo la tabella TblEmail con al suo interno i campi Email e Provincia*/
CREATE TABLE TblEmail
   [ID] INT IDENTITY(1,1) NOT NULL,
   [Email] VARCHAR(25) NOT NULL,    
   [Provincia] VARCHAR(2)
 
/* Effettuo un inserimento all'interno della tabella*/
 
INSERT INTO TblEmail (Email, Provincia)
    VALUES (Questo indirizzo email è protetto dagli spambots. È necessario abilitare JavaScript per vederlo.','AR')
 
/* Aggiorno i dati appena inseriti all'interno del database*/
    UPDATE TblEmail
       SET Provincia = 'BO'
       WHERE Email = Questo indirizzo email è protetto dagli spambots. È necessario abilitare JavaScript per vederlo.'
 
/* A questo punto controllo se si sono presentati degli errori durante le operazioni di creazione inserimento o aggiornamento.
Se la variabile @@ERROR e' diversa da zero, allora contiene il codice relativo all'errore (e quindi si e' verificato un errore all'interno della transazione). */
 
IF @@ERROR <> 0
   ROLLBACK TRANSACTION TR1
ELSE
   COMMIT TRANSACTION T1
 
Questa transazione avrà come risultato la creazione della tabella e l'esecuzione cor­retta anche di tutte le successive operazioni (commit).
Vediamo che cosa succede qualora una delle operazioni all'interno della transazio­ne fallisca.
 
/* Inizio la transazione */
BEGIN TRANSACTION TR1
 
/* Creo la tabella TblEmail con al suo interno i campi Email e Provincia */
CREATE TABLE TblEmail
   [ID] INT IDENTITY(1,1) NOT NULL,
   [Email] VARCHAR(25) NOT NULL,
   [Provincia] VARCHAR(2)
 
/* Effettuo un inserimento all'interno della tabella */
INSERT INTO TblEmail (Email, Provincia)
   VALUES (Questo indirizzo email è protetto dagli spambots. È necessario abilitare JavaScript per vederlo.','AR')
 
/* Aggiorno i dati appena inseriti all'interno del database!. Attenzione!
Il seguente blocco di codice generera' un errore in quanto il numero di caratteri del campo provincia e' maggiore di 2. */
UPDATE TblEmail
   SET Provincia = 'BOLOGNA'
   WHERE Email = Questo indirizzo email è protetto dagli spambots. È necessario abilitare JavaScript per vederlo.'
 
/* A questo punto controllo se si sono presentati degli errori durante le operazioni di creazione inserimento o aggiornamento.
Se la variabile @@ERROR e' diversa da zero, allora contiene il codice relativo all'errore (e quindi si e' verificato un errore all'interno della transazione */
IF @@ERROR <> 0
   ROLLBACK TRANSACTION TR1
ELSE
   COMMIT TRANSACTION T1

 

In questo listato la transazione non apporterà alcuna modifica alla base di dati, in quanto durante uno dei passaggi della transazione (in particolare nel corso dell'aggior­namento della tabella) uno dei dati è incompatibile con la lunghezza del campo. In questo caso, nessuna delle operazioni della transazione verrà portata a compimento.

 

È possibile utilizzare le transazioni anche con MySQL a partire dalla versione 5; la sintassi differisce di poco.

Per iniziare una transazione è necessario utilizzare START TRANSACTION (senza specificare il nome della transazione) e aggiungere semplicemente COMMIT o ROLLBACK (senza specificare il nome della transazione) per decidere il tipo di comportamento della transazione.

 

3       Sicurezza delle basi di dati

Quando si parla di database la sicurezza è un argomento prioritario.

Si pensi all'importanza che essa riveste all'interno di ambienti come quelli militare, bancario o delle grandi multinazionali, in cui non solo determinate operazioni possono essere effettuate unicamente da utenti scelti, ma anche la lettura di alcuni dati è consentita a una cerchia ristretta di persone.

Nell'era dell'informazione, pertanto,è fondamentale poter decidere chi può effettuare determinate operazioni e su quali dati.

La sicurezza coinvolge sistemi, procedure e processi atti a salvaguardare i dati da attività non intenzionali, quali utilizzi errati, malfunzionamenti, attacchi esterni e quant'altro.

Si può dire che una base di dati è sicura quando soddisfa i seguenti parametri:

  • regola l'accesso ai dati protetti, per esempio coperti da segreto militare o industriale;
  • evita la modifica o la manipolazione dei dati da parte di utenti non autorizzati;
  • è disponibile, ovvero nel momento in cui deve essere consultata è presente, consistente e coerente.

Vediamo ora in che cosa consiste il controllo dell'accesso ai dati.

3.1    Controllo di accesso ai dati

Il controllo dell'accesso ai dati è un insieme di meccanismi atti a proteggere le risorse all'interno di un'organizzazione e ha lo scopo di impedire che gli utenti possano operare in maniera diretta o indiretta, intenzionale o meno, all'interno di dati senza avere la necessaria autorizzazione.

 

Le regole di accesso vengono realizzate in base a specifiche politiche (per esemio aziendali), attuate mediante meccanismi basati su modelli.

 

All'interno di un'azienda, per esempio, gli utenti del call center potranno solo accedere ai dati dei clienti, il reparto amministrativo potrà visualizzare questi dati e modificarli, i dirigenti potranno visualizzare i relativi importi di fatturazione e così via. In questo caso, le politiche indicano quali sono i permessi minimi per poter accedere a determinati tipi di dati.

 

I diversi tipi di modelli utilizzati per regolamentare l'accesso ai dati sono elencati di seguito.

  • Controllo accessi discrezionale (DAC, Discretionary Access Control.
  • Controllo accessi mandatorio (MAC, Mandatory Access Control ;
  • Controllo accessi basato su ruoli (RBAC, Role Based Access Control) .

 

Il sistema DAC si basa sul concetto che il proprietario di un bene può decidere chi può avere accesso alle proprie risorse. Un'implementazione di questo sistema è costituita dalle ACL (Access Control List), per esempio l'insieme di permessi utilizzato dai sistemi operativi come Windows, dove l'utente con privilegi più alti, per esempio l'amministratore, può scegliere a quali file o cartelle possono accedere gli altri utenti.

All'interno del DAC i permessi vengono quindi assegnati in base all'identità dell'utente che richiede l'operazione.

 

Nel MAC il proprietario non può invece stabilire in completa libertà e autonomia le regole di accesso. In questo caso l'accesso alle risorse viene mediato a seconda del ruolo o delle funzioni di chi lo sta richiedendo. Il modello MAC prevede che a ogni risorsa venga assegnata un'etichetta logica di sicurezza (label , che contraddistingue il livello base che deve avere l'utente per accedere alla risorsa. Questo tipo di autenticazione è in genere utilizzato in ambiente militare e in sistemi di catalogazione.

Per esempio, l'utente Francesco potrebbe accedere a tutte le risorse classificate come Pubbliche, mentre Manuele a quelle classificate come Pubbliche e Private e Mirko a quel­le di Manuele più quelle Confidenziali.

 

Il RBAC è il modello più recente e si basa sul concetto di ruoli. Tramite questo modello è possibile creare ruoli, all'interno dei quali assegnare determinati utenti.

Per ogni ruolo sarà poi indicato il tipo di operazione che esso può eseguire all'interno dei dati.

Per esempio, potrebbe essere presente un ruolo Call Center in cui sono compresi Francesco e Manuele, mentre nel ruolo Administrators sono presenti Matteo e Francesca. Al ruolo Call Center sarà consentito l'accesso in lettura a determinate tabelle di una base di dati, mentre al ruolo Administrators saranno concesse anche le operazioni di modifica.

All'interno del RBAC un utente può anche essere assegnato a più ruoli: Francesco, per esempio, potrebbe avere i ruoli Call Center e Commerciali.

 

Nel paragrafo successivo verranno illustrati i meccanismi di protezione dei dati all'interno delle basi di dati.

3.2      Protezione da accessi non autorizzati

Prima di entrare in una banca ed eseguire un versamento è necessario effettuare alcune operazioni: superare il metal detector, presentarsi dichiarando le proprie generalità all'impiegato e firmare documenti che attestano l'operazione appena eseguita.

Le relazioni che intercorrono tra entità (utenti, applicativi o altro) e le basi di dati seguono le stesse regole di sicurezza.

 

Il primo livello di controllo è definito autenticazione e avviene quando l'utente (per esempio, mediante login e password) tenta di accedere al sistema.

L'autenticazione ha lo scopo di fornire una risposta alla domanda "Chi sei?" e corrisponde, nell'esempio della banca, al momento in cui ci si presenta al cassiere.

 

Una volta ottenuta l'autenticazione inizia il processo di autorizzazione, in cui vengono definite quali sono le risorse a cui l'utente può accedere e quali operazioni è abilitato a eseguire su di esse. L'autorizzazione risponde quindi alla domanda "Che cosa puoi fare?" (in questo caso, possiamo effettuare prelievi, depositi e quant'altro se siamo clienti della banca).

A questo punto l'utente è autenticato e autorizzato a operare nel database, ma per mantenere il livello di protezione elevato è necessario avere la possibilità di cono­scere quali operazioni sono state effettuate dagli utenti.

Questo processo è definito tracciabilità (ma è più noto con il termine inglese log) e tutti i database utilizzati per applicazioni professionali ne consentono l'attivazione. La figura successiva mostra i ruoli delle varie parti coinvolte nel processo di sicurezza. L'amministratore di sicurezza, qui identificato con un ruolo ben preciso, è rappresentato nella realtà da chiunque abbia il potere decisionale sui permessi da assegnare ai singoli utenti e ai gruppi.

L'amministratore si identifica all'interno del sistema (autorizzazione + autenticazione) e imposta i permessi, ovvero la lista di operazioni che possono essere eseguite sulla base delle regole (dette anche assiomi o politiche) stabilite all'interno dell'azienda. L'utente, superato il processo di identificazione a seconda dei permessi assegnati dall'amministratore, potrà accedere ai contenuti e operare su di essi.

 


 

Cerchiamo ora di chiarire il concetto mediante un esempio pratico.

 

Esempio di utilizzo di permessi

All'interno della società Vento Nord spa, Marco Rossi è l'amministratore della sicurezza. Gli viene chiesto di separare nettamente i ruoli tra commerciali, sviluppatori e dirigenti.

Analizzando la struttura aziendale, Marco decide di applicare un modello basato appunto sui ruoli (RBAC); crea, all'interno della base di dati dell'azienda, tre distinti gruppi: Staff Commerciali, Staff Sviluppatori, Staff Dirigenti.

Ogni membro di ciascun reparto viene inserito all'interno del proprio gruppo, tranne Luciano che è al contempo sviluppatore e dirigente (e può essere inserito all'interno di entrambi i gruppi).

Le politiche aziendali sanciscono che i commerciali possano accedere ai dati dei clienti in lettura, ma non possano modificarli e, soprattutto, non possano vederne i numeri di telefono.

Agli sviluppatori è concesso accedere solamente agli ambienti di test, mentre non è possibile accedere in scrittura ai dati di produzione; infine, per i dirigenti è possibile accedere a tutte le basi di dati con il livello più elevato (amministratore).

Basandosi su queste regole, Marco non farà altro che applicare i giusti permessi alle risorse del database dell'azienda, assicurandosi che ogni utente possa fare solamente ciò che le regole sanciscono.

 

In ambienti in cui i dati sono molto importanti (nella ricerca militare, per esempio), oltre a vietare gli accessi diretti tramite modelli di controllo, si ha spesso la necessità di impedire anche l'accesso inferenziale ai dati.

Questo tipo di accesso si verifica quando un utente, non avendo il permesso di accedere a una determinata risorsa, riesce a ottenere comunque l'informazione deducendola da altre. Supponiamo che Mario lavori all'interno di un reparto composto da soli uomini. Se volesse sapere quante sono le donne nel reparto accanto, non avrà bisogno di entrare nel reparto e contarle ma potrà dedurlo sottraendo il numero degli uomini dal numero totale dei lavoratori dell'azienda.

 

3.3    Integrità Fisica delle basi di dati

Tutti i database, per quanto sofisticati e tecnologicamente avanzati possano essere, devono sottostare a regole "fisiche" ed essere contenuti all'interno di un computer, che come tale può subire guasti alla memoria, cali di tensione, danni sui dischi fissi. Come abbiamo visto nei paragrafi precedenti, uno degli aspetti cruciali di un database è il rigoroso rispetto dell'acronimo ACID: le operazioni devono essere effettuate completamente oppure non essere eseguite affatto, e un'azione non deve mai essere compiuta a metà.

Abbiamo visto che all'interno delle transazioni esistono due tipi di esito: abort (fallimento), che comporta un rollback (ritorno alla situazione iniziale), e commit (ovvero il salvataggio di tutta la transazione).

 

Una transazione può comprendere al suo interno anche moltissime operazioni differenti su aree diverse di un database, o persino su vari database. Come è possibile quindi garantire che un'operazione che può durare anche molti secondi riesca a sopravvivere a un blackout elettrico?

I più importanti database professionali utilizzano un sistema di log per mantenere traccia delle operazioni che eseguono e poter ripristinare le informazioni a uno stato consistente nel caso si verifichino disservizi (come, per esempio, un blackout elettrico).

Il log utilizzato dalle basi di dati si chiama giornale (journal) ed è composto dai seguenti elementi:

 

  • record begin: prima che la transazione inizi, un record viene scritto all'interno del giornale;
  • la transazione responsabile (ID, identificativo della transazione);
  • Il  tipo di operazione che verrà eseguita (aggiornamento, tipo di operazione eseguita (aggiornamento, cancellazione ecc.);
  • la nuova e la vecchia versione del dato modificato;
  • l'esito della transazione (commit o abort).
  • Record end (opzionale, puo’ essere svolto dall’esito)

Le modifiche vengono effettuate prima all'interno del log e successivamente all'interno della base di dati.

A tempi determinati, viene eseguito un allineamento dei dati presenti all'interno del log con quanto presente nella base di dati. Questa operazione è detta checkpoint.


Nella figura precedente sono presenti le cinque transazioni seguenti:

 

  • la transazione 1 avviene prima del checkpoint;
  • la transazione 2 inizia prima del checkpoint e si conclude prima del crash;
  • la transazione 3 inizia prima del checkpoint e si conclude dopo il crash;
  • la transazione 4 avviene dopo il checkpoint e si conclude prima del crash;
  • la transazione 5 inizia dopo il checkpoint e si conclude dopo il crash.

 

Quali saranno le transazioni ripristinate e come sarà lo stato del sistema dopo il crash?

Nel momento in cui si ripristina una base di dati dopo un crash, le transazioni non terminate vengono eliminate, mentre quelle terminate devono essere ripetute. Nell'esempio della figura si verificheranno gli eventi seguenti:

  • la transazione 1 viene ignorata, in quanto conclusa correttamente prima del checkpoint;
  • la transazione 2 e la transazione 4 devono essere ripetute;
  • la transazione 3 e la transazione 5, non essendoci risultati relativi alla loro conclusione (prevista dopo il crash), vengono distrutte.

 

Le attività di checkpoint all'interno di un database avvengono sia in maniera automatica, a seconda della quantità di operazioni effettuate (in un database con molte scritture, per esempio, i checkpoint saranno molto frequenti), sia mediante comandi diretti dell'utente (per esempio, il comando Checkpoint o Alter Database).

 

Per ripetere una transazione viene letto il log in avanti, partendo dai dati iniziali e ripercorrendo tutte le modifiche.

Per eliminare una transazione viene letto il log indietro, partendo dai dati finali e riportandoli allo stato consistente precedente l'inizio della transazione.

Questi meccanismi seguono la logica definita dalle parole che compongono l'acronimo ARIES.

 

  • Analisi: vengono scansionati i log in avanti (ossia a partire dal più recente checkpoint) per identificare tutte le transazioni che erano attive e tutte le modifiche non ancora salvate sul disco.
  • Riesecuzione: si eseguono nuovamente tutti gli aggiornamenti sui dati su cui ancora non era stato effettuato il commit, per assicurarsi che quanto era presente nel journal al momento del checkpoint sia effettivamente registrato sul disco.
  • Annullamento: tutte le operazioni di scrittura che erano attive al momento del crash vengono annullate (abort), ripristinando il valore precedente l'aggiornamento grazie ai log. Vengono lavorati i log indietro e l'operazione viene eseguita su tutti i dati fino all'ultimo dato consistente.

 

Una regola fondamentale che ogni database administrator conosce è quella di effettuare una copia di sicurezza (backup). Il backup costituisce l'unica possibilità di ripristinare i dati in caso di errori estremi, come per esempio crash del disco o gravi errori hardware o software.

Tutti i sistemi avanzati di basi di dati consentono di pianificare i backup in giorni e orari stabiliti, oppure di effettuarli manualmente.