v2004 06 vbj60

68

Upload: turing-club

Post on 07-Jun-2015

476 views

Category:

Documents


1 download

TRANSCRIPT

Page 1: v2004 06 vbj60
Page 2: v2004 06 vbj60
Page 3: v2004 06 vbj60

E D I T O R I A L E

N. 60 - Novembre/Dicembre 2004 VBJ 3

Le communitye CodeZone

Scrivo questa pagina al ritorno dall’evento Connect a Barcellona, dove Microsoft ha invitato gli animatori delle principali community e siti Web dedicati alle tecnologie

e ai linguaggi di programmazione .NET. Sono stati due giorni molto intensi e molto interessanti, soprattutto per la possibilità di confrontarsi sulla nuova iniziativa sulle community che Microsoft sta portando avanti da qualche tempo. Immagino che qualcuno di voi abbia già sentito parlare di Co-deZone, per gli altri ecco un piccolo riassunto. Si tratta di una specie di portale+motore di ricerca che i programmatori .NET potranno usare per arrivare facilmente agli articoli più interessanti su queste tecnologie, tra quelli prodotti appunto dai numerosi siti Web e community sparsi per il mondo. La cosa interessante è che ci saranno tanti portali CodeZone, uno per ciascuna area geografica o lingua. Potete trovare maggiori informazioni a http://www.codezone.info e potete persino abbonarvi ad una rivista abbastanza ben fatta (gratuita, ma in inglese).L’iniziativa è molto interessante e quasi certamente destinata al successo. Perché quasi? Non certo perché a Microsoft mancano i mezzi per imporre CodeZone al mondo degli sviluppatori. Il motivo che ho per dubitare (ma solo un pochino...) ha un nome ben pre-ciso: Google. Ce la farà CodeZone ad imporre la sua specificità e guadagnare le simpatie degli sviluppatori che da anni oramai si affidano alla completezza di Google? Nessuno lo può sapere, ma sarà interessante vedere cosa succederà. Di certo è la prima volta che la più grande software house del mondo deve combattere un osso così duro, e il fatto che la Borsa continui a premiare Google non può che alzare il livello della contesa.

Termino questa pagina con una piccola novità (per voi) ma grande (per il sottoscritto). Quando leggerete questo editoriale sarà già attivo www.dotNet2TheMax.it, la versione italiana del sito VB2TheMax, che ho fondato nel ’99 e a cui hanno collaborato alcuni tra i migliori autori ed esperti italiani e non solo (ecco spiegata la mia partecipazione all’evento Connect). Sul nuovo sito troverete tantissimi tip e articoli e stiamo anche preparando una sezione speciale dedicata ai programmatori VB6 che vogliono migrare a VB.NET.

Arrivederci sulla rete.

Francesco [email protected]

o n l i n e . i n f o m e d i a . i tn. 60 - novembre/dicembre 2004

bimestrale - anno decimo

Direttore Re spon sa bi leMarialetizia Mari ([email protected])

Direttore EsecutivoFran ce sco Balena ([email protected])

Technical EditorAlberto Falossi ([email protected])

Managing EditorRenzo Boni ([email protected])

CollaboratoriMarco Bellinaso, Daniele Bochicchio,Lorenzo Braidi, Gianluca Cannalire,

Dino Esposito, Massimiliano Mariconda,Davide Mauri, Paolo Pialorsi,Ingo Rammer, Marco Russo,

Fabio Scagliola, Lorenzo Vandoni

Direzione Natale Fino ([email protected])

Marketing & Advertising Segreteria: 0587/736460

[email protected]

AmministrazioneSara Mattei

([email protected])

GraficaManola Greco ([email protected])

Technical BookLisa Vanni ([email protected])

Segreteria Enrica Nassi

([email protected])

StampaTIPOLITOGRAFIA PETRUZZI

Citta’ di Castello (PG)

Ufficio AbbonamentiTel. 0587/736460 - Fax 0587/732232e-mail: [email protected]

www.infomedia.it

Gruppo Editoriale Infomedia srlVia Valdera P., 116 - 56038 Ponsacco (PI) Italia

Tel. 0587/736460 - Fax 0587/[email protected]

Sito Web www.infomedia.it

Manoscritti e foto originali anche se non pub bli ca ti, non si restituiscono. È vietata la ri pro du zio ne

anche parziale di te sti e immagini.

Si prega di inviare i comunicati stampa e gli inviti stampa per

la redazione all’indirizzo: [email protected]

Visual Basic Journal è una rivista diGruppo Editoriale Infomedia S.r.l. Via Valdera P, 116 Ponsacco - Pisa.

Registrazione presso il Tribunale di Pisa n. 20/1999

Page 4: v2004 06 vbj60

SOMMARIO

4 VBJ N. 60 - Novembre/Dicembre 2004

N.60

N O V E M B R E / D I C E M B R E

7Vulnerabilità del codiceAnalisi dei casi più comuni di vulnerabilità delle applicazioni software.

di Paolo Pialorsi

La sicurezza del codice managedVediamo alcune delle principali caratteristiche del Framework .NET rivolte alla sicurezza del codice e delle applicazioni.

di Paolo Pialorsi

Applicazioni sicure con IIS 6 e ASP .NETL’accoppiata IIS 6 e ASP.NET garantisce la possibilità di creare e far funzionare in maniera sicura appli-cazioni web con pochi e semplici sforzi. Anche se entrambi i sistemi sono già sicuri appena installati, è necessario un ulteriore tocco per garantire alle nostre applicazioni un elevato livello di sicurezza.

di Daniele Bochicchio

Sviluppare senza essere amministratoriSviluppare applicazioni senza essere amministratori è importante per la sicurezza e per scrivere appli-cazioni eseguibili con il minimo dei privilegi necessari. Vediamo cosa c’è da sapere su Windows e Visual Studio .NET e quali tool sono utili per affrontare questo scenario.

di Marco Russo

SPECIALE

13

Programmare Excel da Visual Basic .NETAnalisi di tre soluzioni per trasferire dati a Excel usando Visual Basic .NET e le classi del .NET framework

di Fabio Scagliola

Servizi NT con VB .NET (seconda parte)Caso reale di realizzazione e implementazione di servizi Windows con .NET

di Gianluca Cannalire

Grafici dinamici in ASP .NET Vale la pena di sprecare tempo per creare grafici da zero? Molto meglio utilizzare una libreria altamente personalizzata. Vediamo tutti i pregi di .netCHARTING

di Dino Esposito

BEGINNER

ENTERPRISE

31

38

45WEB

20

25

Page 5: v2004 06 vbj60

Applicazioni scalabili in pratica“Two is meglio che one” diceva una nota pubblicità. In questa puntata daremo dei consigli pratici per progettare e imple-mentare un’applicazione scalabile.

di Ingo Rammer

L'accesso ai dati nei linguaggi object-orientedTutti i linguaggi object-oriented devono fare i conti con la necessità di gestire dati in formato relazionale

di Lorenzo Vandoni

ARCHITECT'S CORNER

SOFTWARE ENGINEERING

50

56

Editoriale 3

Posta 54a cura di Alberto Falossi

Recensione libri 61

.NET Tools 62a cura di Davide Mauri

RUBRICHE

Page 6: v2004 06 vbj60
Page 7: v2004 06 vbj60

7N. 60 - Novembre/Dicembre 2004 VBJ

Vulnerabilità del codice

di Paolo Pialorsi

DIFENDERSI DAGLI HACKER

Quando si parla di sicurezza delle applicazioni e del codice ci si riferisce al fatto che le applicazioni de-vono essere progettate e realizzate considerando

e prevenendo i casi più diffusi di attacco. Questo articolo ha l’obiettivo di fornire una panoramica dei principali e più diffusi casi di vulnerabilità, insieme ai consigli per evitare di sviluppare applicazioni che siano in tal senso attaccabili.

Buffer OverrunÈ sicuramente il più famoso tipo di attacco e non poteva-

mo non parlarne in un articolo come questo. Riguarda tipi-camente le applicazioni sviluppate con linguaggi che con-sentono la gestione diretta delle aree di memoria come C e C++. È mediamente diffi cile da realizzare nei confronti di applicazioni sviluppate in Visual Basic o in ambienti di ese-cuzione con memoria gestita (come .NET e Java) anche se in taluni casi, quando queste applicazioni si appoggiano a librerie esterne scritte per esempio in C/C++, è comunque possibile essere vulnerabili. Si basa sul fatto che l’accesso diretto e non controllato alla memoria può consentire a un hacker di sovrascriverne delle aree. Esistono diverse tipolo-gie di buffer overrun, una delle più diffuse e che prenderemo come esempio è quella sulle variabili di stack (Figura 1). Nei linguaggi come il C le variabili stringa non sono altro che se-quenze di byte in memoria, terminate dal carattere ASCII 0. Pensiamo a una procedura defi nita come la seguente:

Cvoid foo(const char* input)

{

char buf[10];

strcpy(buf, input);

}

La variabile buf può contenere al mas-simo 10 byte. Del codice invocherà foo, che riceverà in ingresso un puntatore a caratteri chiamato input, che copiamo con la funzione strcpy dentro alla varia-bile buf. Cosa accade se input è di di-mensione maggiore di 10 byte? La fun-zione strcpy non svolge alcun control-lo sulla dimensione di input e di buf, il nostro codice nemmeno. L’eccedenza sarà copiata in memoria oltre lo spazio allocato nello stack di foo per la varia-bile buf. Per come funziona l’esecuzio-ne del codice in memoria, nella parte fi nale dello stack allocato per l’esecu-zione di foo, cioè dopo buf, avremo l’in-dirizzo di ritorno della procedura. Se il contenuto di input è suffi cientemente lungo potrà sovrascrivere questo indi-rizzo di ritorno, cambiando l’esecuzio-ne dell’applicazione. Un trucco come questo, eseguito ai danni di un servizio del sistema operativo, che tipicamente è eseguito con un buon livello di privi-legi, consente di eseguire del codice molto pericoloso. I più famosi attac-chi ai sistemi Windows hanno spesso sfruttato dei buffer overrun di IIS o di altri servizi molto diffusi, per cambia-re l’esecuzione del codice ed esegui-re delle escalation di privilegi.

La soluzione a questo problema è controllare sempre la dimensione dei buffer prima di usarli, in particola-re con funzioni a rischio (strcpy, strn-cpy, CopyMemory, MultiByteToWide-Char). Con l’ultima versione di compi-latore C++ di Microsoft si può utilizza-re l’opzione /GS che aiuta a individua-re i buffer overrun (anche se di per sé non è una protezione sicura al 100%). Inoltre possiamo utilizzare delle funzio-

Analisi dei casi più comuni di vulnerabilità delle applicazioni software.

Paolo Pialorsi è un consulente e autore specializzato nello sviluppo di Web Service e soluzioni Web con il Framework .NET di Microsoft. Lavora nell’omonima società Pialorsi Sistemi S.r.l. e fa parte del gruppo DevLeap. Può essere contattato via email: [email protected].

Page 8: v2004 06 vbj60

8 VBJ N. 60 - Novembre/Dicembre 2004

Questo codice, oltre che poco elegante, è anche decisa-mente insicuro! Finché sul nostro sito navigano utenti nor-mali è tutto ok. Io cerco “birra rossa” e la query risultante sarà “ SELECT * FROM Prodotti WHERE Descrizione = ‘bir-ra rossa’ ”. Ma cosa accade se la ricerca viene utilizzata da un hacker, che magari ne capisce un po’ di codice SQL? Potrebbe cercare “qualcosa’ OR ‘1’ = ‘1” che inserito nel nostro codice produrrebbe la seguente query:

SQLSELECT * FROM Prodotti WHERE Descrizione = ‘qualcosa’ OR ‘1’

= ‘1’

Il risultato sarebbe la lista di tutti i prodotti del nostro sito Web di commercio elettronico, perchè ‘1’ = ‘1’ è una condizione sempre vera. Poco male, direte, tanto se quei prodotti li vendo l’hacker non scopre nulla di nuovo. In questo caso è proprio così, ma pensiamo a un caso di-verso: una pagina di login che utilizza i campi txtUserID e txtPassword per autenticare un utente. Provo a ipotizzare del codice che mi capita di vedere abbastanza spesso:

VBScript<%

Dim sQuery ‘As String

sQuery = “SELECT * FROM Utenti WHERE UserID = ‘”

+ Request(“txtUserID”) + “’ AND Password = ‘” +

Request(“txtPassword”) + “’”

‘ Ottengo I dati all’interno di un ADODB.Recordset

If Not rs.EOF Then

‘ Se ho almeno un record significa che l’utente esiste

End If

%>

Applicando lo stesso ragionamento di prima, in questo caso un valore intenzionalmente modifi cato del campo txtUserID o txtPassword consente a un hacker di acce-dere al sistema anche senza password! Ma come può un hacker sapere che dietro la nostra pagina Web c’è proprio del codice di quel tipo? È più semplice di quello che sembra. Molto spesso una delle parti più trascurate di un’applicazione è la gestione degli errori, che a vol-te è addirittura inesistente. La prima cosa che un hac-ker può fare è scrivere dei valori non troppo casuali che facciano andare in errore il motore SQL, nel caso in cui il nostro codice si limiti a concatenare le stringhe per co-struire le query. Per esempio può mettere nella casella txtUserID un valore come: “qualcosa’ SELECT”. Ovvia-mente concantenando le stringhe otterrebbe un errore simile al seguente:

Errore VBScriptMicrosoft OLE DB Provider for SQL Server (0x80040E14)

Line 1: Incorrect syntax near ‘ AND Password = ‘.

DIFENDERSI DAGLI HACKER

ni per le stringhe “sicure”, disponibili nel fi le di inclusione strsafe.h. Infi ne, conviene sempre controllare tutti gli indici delle matrici prima di usarle, così come le lunghezze mas-sime dei path di fi le e cartelle. Se poi è possibile, conviene passare a codice interamente gestito (.NET).

SQL InjectionCon questa espressione ci si riferisce agli attacchi rivol-

ti a database, tipicamente esposti da applicazioni Web, portati a termine sfruttando un errato o insuffi ciente con-trollo dell’input dell’utente da parte di noi programmato-ri. Si tratta per altro di una delle vulnerabilità ancora oggi più diffuse e utilizzate per violare la sicurezza di un ser-ver e delle reti alle loro spalle.

È doveroso dire che attacchi di questo tipo hanno qua-le unica causa una scarsa attenzione da parte del pro-grammatore, non esistono infatti ragioni “tecnologiche” che giustifi chino simili vulnerabilità. Vediamo di cosa si tratta. Pensiamo a una classica applicazione Web che consente agli utenti di ricercare dei prodotti in un sito di commercio elettronico.

La pagina di ricerca potrebbe fornire una casella di te-sto, chiamiamola txtRicerca, e un pulsante di ricerca, cmdCerca. Alla pressione di cmdCerca sul server verrà eseguito del codice che, concatenando a mano i pezzi di codice SQL, costruisce una query che rappresenta la ricerca da eseguire sul database, come mostrato nel se-guente esempio:

VBScript<%

Dim sQuery ‘As String

sQuery = “SELECT * FROM Prodotti WHERE Descrizione = ‘” +

Request(“txtRicerca”) + “’”

%>

Fi gu ra 1 Schema di un buffer overrun su stack

Page 9: v2004 06 vbj60

Scrivi il codice che conta.

Al resto ci pensa Visual Studio .NET 2003

Visual Studio .NET 2003 riduce notevolmente la quantità di

codice da scrivere, lasciandoti libero di concentrarti sulle cose

più importanti.

Leader, il maggior distributore italiano di videogiochi, ha

sviluppato un’applicazione di Sales Force Automation in sei

mesi/uomo: “Visual Studio .NET 2003 ci ha garantito un notevole

risparmio dei tempi di sviluppo, grazie alla ridotta curva di

apprendimento del nuovo linguaggio C# e all’immediatezza

dell’ambiente visuale. L’applicazione sviluppata ha incrementato

l’effi cienza dei nostri processi di vendita: ora è suffi ciente un

Tablet PC per avere la completa disponibilità in tempo reale

delle informazioni di prodotto, fornire preventivi

personalizzati al cliente e gestire gli ordini.”

Per scoprire come Visual Studio .NET 2003 può aiutarti

a concentrarti sulle cose più importanti, visita il sito

microsoft.com/italy/vstudio/value/

Oggi le tue idee possono diventare ancora più grandi.

© 2

004

Mic

roso

ft. T

utti

i dir

itti

rise

rvat

i. Tu

tti i

mar

chi r

egis

trat

i cit

ati s

ono

di p

rop

riet

à d

elle

ris

pet

tive

so

ciet

à.

004910-VisualBasic-205x275 1 1-09-2004, 17:12:50

Page 10: v2004 06 vbj60

10 VBJ N. 60 - Novembre/Dicembre 2004

Da questo errore potrebbe capire che stiamo proprio concatenando i pezzi di testo senza verifi carli.

L’aspetto ancor più grave di questa vulnerabilità è che, una volta riscontrata, spesso è possibile ottenere l’acces-so non solo alla tabella oggetto della query ma, compo-nendo opportunamente le stringhe, anche ad altre tabel-le di dati, eseguendo per esempio delle UNION. Come riesce un hacker a conoscere i nomi delle tabelle e delle colonne da richiedere al nostro server tramite SQL Injec-tion? Spesso il modo più semplice è chiederle proprio al database stesso. Pensando a un SQL Server come ser-ver di database, nessuno vieta di interrogare le tabelle di sistema per sapere quali sono le tabelle e le loro colon-ne. I gradi di libertà che lasciamo a un hacker sono legati a quanto abbiamo messo in sicurezza la nostra soluzio-ne software. Se per esempio stiamo utilizzando il data-base con un utente ad alto livello di privilegi, non voglio dire l’utente SA di SQL Server (quello ormai dovremmo aver imparato tutti a non usarlo e a mettergli una pas-sword!), ma comunque un utente dbo... beh, probabil-mente siamo fritti! L’utente dbo può tutto sul database corrente, quindi l’hacker, dopo essersi preso i nostri dati, può anche decidere di cancellarli con una bella DELETE o DROP TABLE inserita via SQL Injection in una query. Se poi l’utente con cui gira il servizio del server di data-base (non pensiamo solo a SQL Server) è per esempio LOCAL_SYSTEM, che è il default, l’hacker avrà modo di arrivare anche sul sistema operativo che ospita il server di database e farsi una bella escalation di privilegi sul-la macchina…

È importante capire che questi problemi non sono le-gati ad ASP o a SQL Server, ma sono più in generale le-gati ad applicazioni scritte male! Se scrivete male un sito Web in PHP+MySQL o JSP+Oracle o ASP.NET+SQL non cambia nulla: prima o poi qualcuno potrà farvi il servi-zio completo!

Ok, vediamo allora come ci si deve comportare per non essere vulnerabili agli attacchi di questo tipo. Innanzitut-to è consigliabile utilizzare utenti con privilegi minimi, così se anche ci dovessero violare l’applicazione non avranno a disposizione tutti gli strumenti di un SA o di un dbo o di LOCAL_SYSTEM. Poi dobbiamo abituarci a scrivere query parametriche o stored procedure parametriche, smettendo di concatenare le stringhe SQL nel codice. Nel caso del lo-gin avremmo dovuto scrivere codice simile al seguente:

VBScript<%

Dim sQuery ‘As String

sQuery = “SELECT * FROM Users WHERE UserID = ? AND Password =

?”

Dim cmd ‘As ADODB.Command

Set cmd = Server.CreateObject(“ADODB.Command”)

cmd.CommandText = sQuery

cmd.Parameters.Append cmd.CreateParameter(“@UserID”, adVarChar,

adParamInput, 50, Request(“txtUserID”))

cmd.Parameters.Append cmd.CreateParameter(“@Password”,

adVarChar, adParamInput, 50, Request(“txtPassword”))

‘ [...] Ottengo I dati all’interno di un ADODB.Recordset

If Not rs.EOF Then

‘ Se ho almeno un record significa che l’utente esiste

End If

%>

Anche in ASP.NET possiamo utilizzare delle query pa-rametriche nel modo seguente:

C#String query = “SELECT * FROM Users WHERE UserID = @UserID AND

Password = @Pwd”;

SqlConnection cn = new SqlConnection(“...”);

SqlCommand cmd = new SqlCommand(query, cn);

cmd.Parameters.Add(“@UserId”, SqlDbType.VarChar, 50).Value =

Request[“txtUserID”];

cmd.Parameters.Add(“@Pwd”, SqlDbType.VarChar, 50).Value =

Request[“txtPassword”];

SqlDataReader dr = cmd.ExecuteReader();

// Leggo i dati ottenuti...

Inoltre è sempre opportuno verifi care l’input dell’utente. Molti pensano che questi problemi siamo solo ricollegabili a parametri testuali e che quindi la soluzione al problema sia semplicemente fare una sostituzione degli apici con doppi apici su tutte le stringhe, prima di concatenarle al codice SQL. Non dimentichiamo mai che gli hacker sono in media più furbi di noi :-) o quanto meno più “smanet-toni”. Controllare le stringhe non basta, anche gli input numerici, le date o qualunque altro input possono esse-re utilizzati. Alla peggio un hacker può farsi a mano una paginetta di submit verso il nostro sito, con la quale può camuffare i dati da inviare in POST. Se utilizzate un da-tabase server che supporta le stored procedure, poi, una soluzione ancora più comoda è quella di sfruttare le sto-red procedure parametriche (a patto di non concatena-re le stringhe all’interno della stored procedure!): in que-sto modo potrete unire al vantaggio dei parametri, che di fatto impediscono l’injection, anche un incremento me-dio delle prestazioni. Infi ne, è sempre opportuno dare al-l’utente corrente solo i permessi minimi sulle tabelle: su un sito di pubblicazione in sola lettura, per esempio, è inutile avere anche il diritto di UPDATE o di DELETE (sa-rebbero utili solo all’hacker di turno).

DIFENDERSI DAGLI HACKER

Page 11: v2004 06 vbj60

11N. 60 - Novembre/Dicembre 2004 VBJ

Cross Site ScriptingSi tratta di una vulnerabilità legata al mondo Web e ba-

sata nuovamente sul fatto che spesso gli sviluppatori si fi dano troppo dei loro utenti e non ne controllano l’input. Molto spesso nei siti Web capita di inserire dei dati all’in-terno di campi form, per la ricerca o per la registrazione, e di vedere questi stessi dati riproposti in fasi successi-ve. Per esempio, se eseguiamo una ricerca di solito la pagina con il risultato ci ricorda anche quale era il testo da noi cercato. Ora proviamo a scrivere invece di “birre rosse” il seguente testo: “<b>birre rosse</b>”. In HTML abbiamo scritto il testo in grassetto. Se la pagina con il risultato della ricerca ci ripropone il testo birre rosse in grassetto e non “<b>birre rosse</b>” allora siamo in presenza di un sito che potrebbe rendersi vulnerabile al Cross Site Scripting. Proviamo infatti a scrivere “birre ros-se <script>window.alert(‘Cross Site Scripting’);</script>” e a premere nuovamente il pulsante di ricerca ... sorpre-si? Vi troverete di fronte a una fi nestra con il messaggio “Cross Site Scripting”. Queste prime prove ci permetto-no di capire che il programmatore che ha scritto il codice della pagina di ricerca prende i dati in input da parte del-l’utente e li mette sulla pagina senza fare alcun controllo. Fino a qui non c’è nulla di sconvolgente. Ora pensiamo al fatto che molto spesso i siti Web offrono la possibilità di registrarsi e di farsi riconoscere. Pensiamo a un sito di commercio elettronico che registra la nostra carta di cre-dito e ci riconosce sfruttando un cookie. A questo punto proviamo a cercare “birre rosse <script>window.alert(document.cookie);</script>” su quel sito. Se il sito è vulnerabile al Cross Site Scripting vedremo sullo scher-

mo il nostro cookie. Anche in questo caso, fi nché siamo noi a leggere il nostro cookie la situazione pare non es-sere grave. Ma se quel codice anziché mostrare con un alert il cookie lo inviasse al sito di un hacker? Un hacker può inviare migliaia di email a utenti della rete, tanto gli hacker la banda di rete non la pagano mai :-) a quanto pare, presentandole come email spedite dal sito affetto da Cross Site Scripting e chiedendo agli utenti di pre-mere un link, per ottenere qualche offerta o regalo. Die-tro a quel link ci sarà una pagina reale del sito che ac-cetta Cross Site Scripting. Nel link ci sarà anche del co-dice javascript inserito nella query string. Questo codice leggerà il vostro cookie per inviarlo a un sito dell’hacker (ecco perchè si dice “Cross Site”) che a questo punto potrà farsi credere voi e potrà fare shopping con la vo-stra carta di credito. Carino vero?

Anche in questo caso la prima misura da adottare è non fi darsi mai dell’input ottenuto dagli utenti e, se deside-riamo riscriverlo in output, conviene sempre farne prima un HTML Encode.

VBScript<% Response.Write Server.HtmlEncode(Request(“UserName”)) %>

C#Response.Write(Server.HtmlEncode(Request[“UserName”]));

Inoltre possiamo decidere di aggiungere ai cookie che rilasciamo un attributo “HttpOnly” che ne impedirà l’ac-cesso da parte del codice JavaScript, rendendo di fatto inutile un attacco di questo tipo.

CanonicalizationCon questo termine ci si riferisce a quella famiglia di at-

tacchi che si basano sull’uso di indirizzi di fi le o risorse contraffatti o che si rivolgono ad applicazioni che utiliz-zano l’input dell’utente per generare o fornire fi le. Han-no fatto storia, nel recente passato, le email false fi rma-te da hacker che si facevano credere Microsoft e che raccomandavano di installare applicazioni o fi x disponi-bili a indirizzi che sembravano essere quelli di Microsoft, ma che in realtà celavano gli indirizzi dei server utilizza-ti dagli hacker.

Infatti un indirizzo come il seguente http://www.microsoft.com:[email protected]/ a prima vista sembra corrispondere a www.microsoft.com, in realtà il vero indirizzo è dopo il carattere @, quindi www.devleap.com, mentre ciò che precede la chioccio-la è solo la coppia nome utente e password, separati dai due punti (:). Questo problema ha richiesto addirittu-ra una fi x di Internet Explorer che ha disabilitato il sup-porto a questo tipo di indirizzi.

Ma nella Canonicalization rientrano anche i casi di at-tacco in cui le applicazioni richiedono agli utenti dei nomi

DIFENDERSI DAGLI HACKER

Fi gu ra 2 Evitare la navigazione sui percorsi superiori a quelli in cui è ospitata l’applicazione Web

Page 12: v2004 06 vbj60

12 VBJ N. 60 - Novembre/Dicembre 2004

espliciti di fi le o da fornire o da salvare sul server. Pensia-mo a un’applicazione Web sviluppata con ASP.NET nella quale si chiede all’utente di fornire un nome di fi le da uti-lizzare per salvare il suo carrello prodotti. Dietro le quinte la nostra applicazione utilizza quel nome per creare un fi le nella cartella relativa ./carrelli dell’applicazione Web. Trovo decisamente insicuro e generalmente errato salva-re fi le su disco nelle applicazioni Web, ancor di più quan-do è l’utente fi nale a scegliere i nomi dei fi le, ma ciò non toglie che vi siano parecchie applicazioni in produzione su Internet che consentano di farlo. Ebbene, in un caso come quello appena descritto un utente potrebbe forni-re come nome di fi le “../web.confi g” ottenendo quale ri-sultato quello di sovrascrivere il fi le web.confi g dell’appli-cazione, bloccandola. Oppure, nel caso di un download di risorse, potrebbe tentare di spostarsi sul fi le system usando percorsi relativi (come . e ..) riuscendo così a sca-ricare fi le per i quali non è autorizzato.

La soluzione a problemi di questo tipo è quella di im-postare correttamente e in modo rigido i permessi sul-le ACL (Access Control List) del fi le system, evitare con l’apposita opzione di IIS la navigazione sui percorsi su-periori a quelli in cui è ospitata l’applicazione (“Enable Parent Paths” disabilitato, Figura 2) e se possibile evi-tare in assoluto di richiedere agli utenti percorsi espliciti di fi le o risorse fi siche.

Denial of ServiceSono attacchi organizzati e fi nalizzati a interrompere

l’erogazione di un servizio o di un’applicazione. Di so-lito non sono realizzati per caso, ma sono rivolti a degli obiettivi precisi e scelti. Questi attacchi consistono nel saturare le risorse (rete, memoria, CPU, ecc.) di un siste-ma al fi ne di renderlo non disponibile. A volte lo scopo fi nale è solo quello di neutralizzare un servizio, altre vol-te il servizio viene neutralizzato per poi sostituirvisi con un servizio fi ttizio.

Come programmatori possiamo solo fare del nostro me-glio per evitare che le nostre applicazioni siano vulnera-bili a questo tipo di attacchi, senza dimenticare comun-que che anche siti Web autorevoli e importanti in pas-sato sono stati messi in ginocchio da questo tipo di at-tacchi, a volte portati a termine da ragazzi poco più che adolescenti. A noi programmatori conviene per esempio monitorare sempre il consumo di risorse e riciclare auto-nomamente i servizi qualora consumino più di una deter-minata percentuale di soglia. Per un servizio che normal-mente consuma il 5% della CPU un consumo del 90% è una situazione sospetta e conviene non permettergli di arrivare a un simile livello, meglio fermarlo e riavviar-lo prima. Nel nostro codice possiamo cercare di arginare queste situazioni evitando di creare indiscriminatamen-te oggetti, thread e risorse in generale, ma magari crea-re dei pacchetti (pool) di risorse, di dimensione massi-

ma prefi ssata. Avere 1000 thread che lavorano in paral-lelo a volte è peggio che non averne 100 che lavorano e una coda di attività da svolgere non appena vi saranno dei thread liberi per farlo. Un tipico esempio che faccio in questo caso è quello relativo alla gestione degli erro-ri. Pensiamo a un’applicazione Web che ad ogni errore manda una email al supporto prodotto in modalità sin-crona, cioè utilizzando lo stesso thread che esegue la richiesta in errore. L’invio di una email richiede un mini-mo di tempo per risolvere l’indirizzo del server di posta e per il dialogo SMTP. Un hacker potrebbe sfruttare questo fatto per “ingozzare” il nostro server di richieste che ge-nerano errori, consapevole del fatto che un errore con-suma molte più risorse di una pagina normale, potendo così saturare prima il nostro servizio.

Se noi utilizzassimo un thread o un pool di thread, di-verso da quello predefi nito di .NET, sul quale accodare le richieste di invio di email, le notifi che di errore non sa-rebbero più un’attività pesante per i singoli thread che evadono le richieste, ma diventerebbero un’attività che viene svolta in background, quando ci sono risorse per farlo. Inoltre non ha senso mandare continuamente email tutte uguali che descrivono uno stesso errore. Oltre un certo limite è meglio mettersi in “regime di protezione” per evitare di intasare il server di posta, oltre che la ca-sella, del supporto prodotto.

ConclusioniLe vulnerabilità del codice sono numerose e in questo

articolo abbiamo sfi orato le più frequenti e diffuse. In bi-bliografi a trovate alcuni testi e link per approfondire e in-tegrare l’argomento.

In generale occorre essere consapevoli del fatto che scrivere codice sicuro non è un’attività né semplice, né tantomeno banale. Non bisogna mai assumere di essere più furbi degli hacker e non si devono mai inventare del-le soluzioni fatte in casa. Scrivere codice sicuro richiede tempo, attenzione e risorse. Come ho letto qualche tem-po fa sul sito della Università di Salerno: “Scrivere codi-ce sicuro è lodevole, ma irrimediabilmente costoso”. Se vogliamo essere sviluppatori e consulenti seri dobbiamo mettere a budget questo costo, perchè l’alternativa è fare gli azzeccagarbugli.

Bibliografi a[1] Michael Howard, David LeBlanc - “Writing Secure

Code, Second Edition”, Microsoft Press, 2002[2] Frank Swiderski, Window Snyder - “Threat Modeling”,

Microsoft Press, 2004[3] Jason Bock, Tom Fischer, Nathan Smith, Pete

Stromquist - “.NET Security”, APress, 2002

Riferimenti[4] http://msdn.microsoft.com/security/

DIFENDERSI DAGLI HACKER

Page 13: v2004 06 vbj60

13N. 60 - Novembre/Dicembre 2004 VBJ

La sicurezzadel codice managed

di Paolo Pialorsi

Le applicazioni sviluppate con il Framework .NET di Microsoft si dicono “managed” in quanto la loro esecuzione è “gestita” dal motore di esecuzione del

Framework stesso, cioè dal Common Language Runtime (CLR). Tra i vantaggi offerti dal CLR vi è il fatto che ci viene messa a disposizione un’infrastruttura di sicurezza nativa, in molti casi automatica e per noi trasparente. Vediamo in questo articolo alcune delle caratteristiche più importanti orientate alla sicurezza del codice managed.

Gestione della memoriaUna delle prime caratteristiche che il Framework .NET ci

offre è la gestione automatica della memoria, sia in termini di allocazione che di rilascio delle risorse. Grazie a questa possibilità i classici problemi di sicurezza quali buffer over-run, overfl ow su numeri o indici e simili, diventano tecnica-mente impossibili da realizzare. Pensiamo al buffer over-run su stack, ottenuto sovraccaricando il contenuto di una stringa, come mostrato nell’articolo precedente.

Con .NET le stringhe sono oggetti che vengono allocati in memoria nel momento in cui vengono assegnate. Alla loro variazione di contenuto avremo una riallocazione, in una nuova area di memoria, dello spazio necessario a conte-nere il nuovo valore.

Per come ho appena descritto la situazione, è impossi-bile utilizzare una stringa .NET per eseguire un attacco di tipo buffer overrun. Nel caso in cui poi vogliamo costrui-re dinamicamente delle stringhe di dimensione massima prefi ssata, esiste la classe StringBuilder che di lavoro fa proprio questo:

C#// Dichiaro uno StringBuilder per una

stringa da 1 a 10 caratteri

StringBuilder bld = new StringBuilder(1,

10);

for (Int32 c = 0; c < 10; c++) {

bld.Append(“a”);

}

bld.ToString();

Nell’esempio appena visto, un even-tuale tentativo di scrivere più di 10 caratteri all’interno dello StringBuil-der solleverebbe un’eccezione (Argu-mentOutOfRangeException).

Il gestore di memoria del CLR è inol-tre in grado di controllare l’accesso alle singole aree di memoria, consen-tendoci di isolare eventuali assem-bly in esecuzione all’interno di uno stesso processo, sfruttando il con-cetto di Application Domain. Possia-mo pensare agli AppDomain come a dei sotto-processi, completamen-te isolati e protetti tra loro, eseguiti all’interno di un unico processo del sistema operativo. Tutte le volte che abbiamo l’esigenza di eseguire del codice managed con privilegi parti-colari o isolandolo dal resto del con-testo applicativo, possiamo creare un AppDomain ed eseguire al suo inter-no il codice. In questo modo avre-mo pieno isolamento tra i vari livel-li di accesso al sistema. Il motore di ASP.NET sfrutta proprio il concetto

Vediamo alcune delle principali caratteristiche del Framework .NET rivolte alla sicurezza del codice e delle applicazioni.

Paolo Pialorsi è un consulente e autore specializzato nello sviluppo di Web Service e soluzioni Web con il Framework .NET di Microsoft. Lavora nell’omonima società Pialorsi Sistemi S.r.l. e fa parte del gruppo DevLeap. Può essere contattato via email: [email protected].

DIFENDERSI DAGLI HACKER

Page 14: v2004 06 vbj60

14 VBJ N. 60 - Novembre/Dicembre 2004

Innanzitutto si deve creare un’istanza di IsolatedStora-geFile, in questo esempio si richiede uno storage locale in base all’utente e all’assembly corrente (GetUserStore-ForAssembly). Ottenuto un riferimento allo storage pos-siamo creare fi le, directory, stream di byte su fi le, ecc. Nell’esempio creiamo un IsolatedStorageFileStream, per scrivere sul disco virtuale un fi le di testo puro. Per impo-stazione predefi nita le applicazioni managed che scari-chiamo da Internet non hanno accesso, per motivi di si-curezza, al fi le system fi sico delle nostre macchine, ma possono tranquillamente utilizzare l’Isolated Storage. Esi-ste anche un tool a riga di comando (STOREADM.EXE) che permette di gestire ed eventualmente cancellare gli Isolated Storage creati.

Strong nameQuando creiamo un progetto con Visual Studio .NET

o con qualsiasi editor di codice .NET e poi lo compilia-mo, otteniamo come risultato uno o più fi le .EXE, .DLL o .NETMODULE, che rappresentano degli assembly o dei moduli di un assembly. In .NET l’assembly può essere defi nito come “unità minima di deployment” e in pratica rappresenta uno o più fi le, che costituiscono una parti-colare versione di una porzione di codice. Dal punto di vista del deployment, cioè del rilascio del codice, con .NET non esiste nulla di più piccolo rispetto all’assembly. Questo signifi ca che al minimo potremo gestire l’installa-zione e il versioning degli assembly, ma non dei singoli fi le di cui sono costituiti.

Ogni volta che generiamo un assembly, di solito com-pilando un progetto con Visual Studio .NET, otteniamo un assembly mono-modulo, cioè costituito di un solo fi le. Durante la compilazione possiamo decidere di associa-re all’assembly una serie di informazioni come il nome, la cultura, la versione e volendo anche una fi rma digitale basata sul suo contenuto, sfruttando un algoritmo a chiavi asimmetriche (chiave pubblica e chiave privata). Queste informazioni in Visual Studio .NET si trovano nei fi le au-togenerati con nome AssemblyInfo.cs o AssemblyInfo.vb. Come impostazione predefi nita un assembly autogenera-to da Visual Studio .NET è dotato solo di nome e cultura neutrale. Possiamo però modifi care il fi le AssemblyInfo.* e inserire anche altre informazioni:

C#using System.Reflection;

using System.Runtime.CompilerServices;

[assembly: AssemblyTitle(“Applicazione dimostrativa”)]

[assembly: AssemblyCompany(“DevLeap”)]

[assembly: AssemblyProduct(“ClassLibraryDemo”)]

[assembly: AssemblyCopyright(“(C) Paolo Pialorsi, 2004”)]

[assembly: AssemblyCulture(“it-IT”)]

[assembly: AssemblyVersion(“1.0.0.1”)]

DIFENDERSI DAGLI HACKER

di AppDomain per eseguire diverse applicazioni Web al-l’interno di uno stesso processo, garantendo così l’iso-lamento completo tra le aree di memoria assegnate alle singole applicazioni.

Isolated storageSempre a proposito di isolamento il Framework .NET

ci offre la possibilità di utilizzare un fi le system virtuale, chiamato Isolated Storage, che consente di salvare fi le anche ad applicazioni non autorizzate ad accedere al disco fi sico della macchina client.

Più avanti in questo articolo, quando parleremo di Code Access Security, le cose risulteranno più chiare, per ora assumiamo che l’Isolated Storage sia un disco virtuale, sul quale un nostro applicativo managed può lavorare come se fosse un disco vero.

Inoltre rispetto a un disco reale vi è il fatto che pos-siamo defi nire delle quote e associare il fi le system a singoli utenti e/o applicazioni (assembly), per un’even-tuale condivisione di informazioni. Per utilizzarlo dob-biamo fare riferimento alle classi del namespace System.IO.IsolatedStorage nel modo seguente:

C#IsolatedStorageFile isoFile = IsolatedStorageFile.GetUserStore

ForAssembly();

using(IsolatedStorageFileStream fs = new IsolatedStorageFile

Stream(“doc.txt”, FileMode.OpenOrCreate, FileAccess.Write,

FileShare.None)) {

String testo = “testo da scrivere”;

fs.Write(System.Text.Encoding.ASCII.GetBytes(testo), 0,

testo.Length);

}

isoFile.Close();

Fi gu ra 1 L’applicazione di amministrazione di NET Framework

Page 15: v2004 06 vbj60

15N. 60 - Novembre/Dicembre 2004 VBJ

[assembly: AssemblyDelaySign(false)]

[assembly: AssemblyKeyFile(@”d:\keys.snk”)]

In questo esempio si dichiarano delle voci puramente in-formative come il titolo, l’azienda produttrice, il nome del prodotto e il copyright e delle informazioni particolari come la versione (1.0.0.1), la cultura (it-IT) e un fi le (keys.snk) da utilizzare per fi rmare digitalmente l’assembly.

Il fi le keys.snk a cui si riferisce il codice di esempio può essere generato con il tool a riga di comando SN.EXE nel modo seguente:

Command Prompt di Visual Studio .NETSN.EXE –k Keys.snk

dove –k indica di generare una coppia di chiavi e il secon-do parametro è il nome del fi le che conterrà le chiavi.

Se compiliamo un assembly che presenta tutte que-ste informazioni (nome, cultura, versione e fi rma digita-le), esso si dirà dotato di uno “strong name”. Potremmo tradurre questa espressione in “nome solido” o “nome sicuro”, in realtà trovo che suoni molto meglio la defi ni-zione inglese, anche se il suo signifi cato è proprio quel-lo di dare solidità e sicurezza a un assembly. Tutti gli as-sembly dotati di strong name infatti sono verifi cati e ve-rifi cabili rispetto alla fi rma digitale applicatagli.

Gli assembly con strong name possono essere instal-lati nella stessa cartella delle singole applicazioni che li usano, in questo caso si dicono assembly locali, oppu-re nella Global Assembly Cache (GAC) e si dicono as-sembly condivisi.

Se realizziamo un’applicazione .NET (client del compo-nente) che utilizza un assembly locale (componente ser-ver) con strong name, il motore del CLR, prima di cari-care in memoria l’assembly che rappresenta il compo-nente server, ne verifi cherà l’integrità rispetto alla fi rma digitale. Se qualcuno dovesse manomettere il contenu-to dell’assembly rispetto a quando l’applicazione client è stata compilata, il CLR se ne accorgerà, bloccando-ne il caricamento.

Se invece utilizziamo un assembly condiviso, quindi in-serito nelle GAC, la verifi ca dell’integrità dell’assembly non sarà ripetuta a ogni caricamento, ma sarà eseguita solo all’atto dell’inserimento nella GAC. La GAC infatti di default è accessibile in modifi ca solo ed esclusivamente a utenti membri del gruppo Administrators, quindi si assume che gli amministratori non manomettano intenzionalmente gli assembly rilasciati. Da questo ultimo concetto si evince un’altra ragione (se non ve n’erano già abbastanza!) per cui è di fondamentale importanza non sviluppare codice utilizzando un utente con diritti amministrativi.

Se poi un hacker dovesse manomettere un assembly e successivamente ri-fi rmarlo con una nuova fi rma, basa-ta su una chiave privata diversa, il CLR si accorgerebbe

comunque della manomissione in quanto all’interno del client è presente non solo il riferimento all’assembly, ma anche il riferimento alla coppia di chiavi utilizzate origi-nariamente per fi rmare l’assembly. Questa informazione è detta “publicKeyToken” e nuovamente, se non c’è cor-rispondenza tra il publicKeyTone del client e quello del server, il CLR blocca il caricamento dell’assembly segna-lando la manomissione.

Code Access SecurityQuando il CLR carica in memoria un assembly svolge

una serie di verifi che fi nalizzate a decidere quali permessi di esecuzione concedergli. Queste verifi che sono basate su una serie di prove (evidence) che consentono al CLR di capire quale sia la provenienza dell’assembly (per es. PC locale, internet, intranet, ecc.), se ha uno strong name o meno, se è fi rmato con Authenticode, ecc. Sulla base di queste prove gli assembly vengono quindi associati a dei gruppi (Code Group) ai quali poi sono associati dei permessi (Permission Set). I Code Group sono suddivisi in diversi livelli di Policy:

• Enterprise: defi nisce le politiche di sicurezza a livello di intera infrastruttura di rete (enterprise)

• Machine: dichiara le politiche relative al PC corrente• User: descrive le politiche di sicurezza per l’utente

corrente• AppDomain: confi gurabile da codice a livello di AppDo-

main

Questi livelli sono confi gurati su ogni sistema con .NET installato e sono gestibili sia dal prompt dei comandi, uti-lizzando il tool CASPOL.EXE (CASPOL = Code Access Security Policy) sia - più comodamente - utilizzando un tool amministrativo con interfaccia grafi ca. Quest’ultimo tool si chiama “Microsoft .NET Framework 1.1 Confi gu-

DIFENDERSI DAGLI HACKER

Fi gu ra 2 La gestione dei permessi facenti parte di un Permission Set

Page 16: v2004 06 vbj60

16 VBJ N. 60 - Novembre/Dicembre 2004

ration” (Figura 1) e consente di creare, all’interno dei pri-mi tre livelli di policy, dei Code Group e Permission Set personalizzati.

Vediamo quindi come ragiona il CLR per decidere i per-messi da associare a un assembly.

Gli assembly sono associati ai Code Group in base a condizioni di appartenenza (Membership Conditions) come le seguenti:

• Application Directory: tutti gli assembly caricati dalla stessa cartella o da una cartella fi glia di quella in cui è stato caricato l’assembly principale

• Hash: l’hash code dell’assembly• Publisher: il produttore dell’assembly, individuato tra-

mite un certifi cato Authenticode• Site: il site di provenienza• Strong name: il publicKeyToken del produttore ed

eventualmente il nome e la versione esplicite dell’as-sembly

• URL: la URL di provenienza o porzione di essa• Zone: la zona di provenienza del codice (My Compu-

ter, Local Intranet, Internet, Trusted Sites, Untrusted Sites)

• Custom: criteri di appartenenza personalizzati

I permessi di cui sono costituiti i Permission Set pos-sono essere scelti tra quelli predefi niti di .NET oppure essere permessi personalizzati, che possiamo creare e confi gurare noi stessi.

Alcuni dei permessi più utilizzati e predefi niti del Fra-mework .NET sono (Figura 2):

• File IO: consente di defi nire le regole di accesso al fi le system, in termini di Read, Write, Append e Brow-se di fi le e risorse, eventualmente consentendo an-che di impostare dei fi ltri sui path ai quali si conce-de l’accesso. Potremmo cioè concedere un accesso completo al path C:\Temp e in sola lettura al path C:\Documenti.

• Isolated Storage File: abilita l’accesso all’Isolated Sto-rage (vedi sezioni precedenti) indicando anche la quo-ta massima consentita.

• Registry: consente di abilitare l’accesso in varie mo-dalità (Read, Write, Create) alle chiavi del registro di sistema.

• Security: permette di confi gurare alcuni permessi qua-li l’esecuzione di codice, l’utilizzo di .NET Remoting, la serializzazione di oggetti, le chiamate a codice un-managed, ecc.

• User Interface: consente di esporre un’interfaccia utente a fi nestre o meno, controllando anche il tipo di messaggi Windows che si possono ricevere e l’ac-cesso alla Clipboard.

• SQL Client: abilita l’accesso a server di dati Micro-

soft SQL Server, eventualmente impedendo l’uso di server SQL con connessioni a password nulla.

Infi ne i Code Group hanno due fl ag che permettono ri-spettivamente di dichiarare l’esclusività dei loro Permis-sion Set per gli assembly che vi appartengono e la non ridefi nibilità dei Permission Set che descrivono, a livelli di Policy più bassi.

Il caricamento di un assemblyVi siete mai chiesti perchè un programma sviluppato con

.NET, se eseguito da un disco di rete anzichè dal PC lo-cale, può cambiare comportamento e in alcuni casi ad-dirittura non si avvia nemmeno, restituendoci un errore di sicurezza? Vediamo insieme, in questa sezione, il per-chè di questo comportamento.

Al caricamento dell’assembly il CLR legge le sue eviden-ce e stabilisce i Code Group, di ogni livello di Policy, ai quali appartiene. Per ogni livello di Policy poi viene fatta l’unione dei Permission Set associati ai Code Group cui appartiene l’assembly.

Nel caso di Code Group con il fl ag di esclusività, verrà preso solo il suo Permission Set.

Il risultato di questa prima fase è, per ogni livello di Po-licy, un insieme di permessi, quelli ottenuti dall’unione dei Permission Set.

A questo punto il CLR esegue l’intersezione di questi tre insiemi di permessi, per ricavare i reali permessi da associare all’assembly.

Lo so, sembra un esame di Algebra del primo anno di Ingegneria :-), ma cerchiamo di capire con un esempio concreto.

Consideriamo un assembly (di nome ServiceProxy), di tipo class library, con le seguenti caratteristiche:

• URL di provenienza: http://www.devleap.com/• Strong name publicKeyToken: 714cc0ec76570e52

Ipotizziamo che venga caricato in memoria da un altro assembly che rappresenta un’applicazione Console (Ser-viceClient).Immaginiamo poi che nel nostro sistema siano confi gu-rati i seguenti Code Group a livello di macchina:

• CodeGroupDevLeapSiteMachine: vi appartengono tutti gli assembly che provengono dalla URL http://www.devleap.com/. È associato al seguente Permis-sion Set:

• File IO: sola lettura su C:\Program Files\DevLeap e basta

• Isolated Storage File: permesso di accesso con quota di 20Kb

• CodeGroupDevLeapSNMachine: vi appartengono tutti gli assembly che hanno uno strong name con il pu-

DIFENDERSI DAGLI HACKER

Page 17: v2004 06 vbj60

17N. 60 - Novembre/Dicembre 2004 VBJ

blicKeyToken ricavato dalla coppia di chiavi di Dev-Leap. È associato al seguente Permission Set:

• File IO: controllo completo sul path C:\Temp

Il CLR analizzando queste informazioni saprà che l’as-sembly ServiceProxy appartiene a entrambi i CodeGroup, quindi i suoi permessi, relativamente al solo livello di po-licy di tipo Machine, saranno:

• File IO: sola lettura su C:\Program Files\DevLeap e basta

• Isolate Storage File: permesso di accesso con quota di 20Kb

• File IO: controllo completo sul path C:\Temp

Cioè appunto l’unione dei Permission Set dei singo-li Code Group del livello Machine ai quali ServiceProxy appartiene. Ora ipotizziamo che esista a livello di Policy Enterprise un Code Group, defi nito dall’amministratore di rete, come il seguente:

• CodeGroupDevLeapSiteEnterprise: vi appartengono tutti gli assembly che provengono dalla URL http://www.devleap.com/. È associato al seguente Permis-sion Set:

• Isolated Storage File: permesso di accesso con quota di 20Kb

Quindi il nostro ServiceProxy apparterrà anche a que-sto Code Group.

Il livello di Policy relativo all’utente corrente conside-riamolo dotato di un solo Code Group, che comprende qualunque assembly, con associato un unico Permission Set che consente tutto a tutti (FullTrust).

Si tratta tra l’altro della impostazione predefi nita. Il CLR dovrà a questo punto intersecare i permessi di ciascun livello di Policy, ottenendo come risultato il seguente per-messo:

• Isolated Storage File: permesso di accesso con quo-ta di 20Kb

Infatti questo è l’unico permesso in comune a tutti e tre i livelli di Policy. L’intersezione è l’operazione insiemistica corretta da eseguire in questo contesto, per evitare che un utente o un PC possano avere permessi più ampi di quelli che la macchina o le regole di sicurezza della rete gli concederebbero. Cosa accade a un assembly, come quello dell’esempio precedente, che non ha il permes-so di accedere al disco fi sico del PC, ma solo al disco virtuale (Isolated Storage), qualora tentasse di aprire un fi le, per esempio su C:\temp?

Una SecurityException ci informerà del problema, nel caso in cui si tenti esplicitamente di eseguire un’opera-zione non consentita.

Verifi ca e richiesta di permessiFinchè siamo noi a creare e installare le nostre appli-

cazioni non dovremmo avere problemi a darci o farci as-segnare i permessi necessari al corretto funzionamento del nostro codice. Quando però il software che svilup-piamo viene reso disponibile a numerosi utenti, magari pacchettizzato, abbiamo l’esigenza di rilevare l’esisten-za o meno di permessi per noi necessari, per evitare che le nostre applicazioni vadano in crash (sollevando una SecurityException) nel momento in cui cercano di svolgere operazioni non consentite. Possiamo assolve-re a questa esigenza utilizzando due differenti tecniche tra loro alternative.

Il primo modo di lavorare prevede l’utilizzo di classi che descrivono permessi, cioè classi che implementano l’in-terfaccia IPermission, chiedendo al CLR, per esempio tramite l’apposito metodo Demand dell’interfaccia, se il particolare permesso è concesso o meno.

C#FileIOPermission fp = new FileIOPermission(FileIOPermission

Access.Read, @”C:\TEMP\INFO.TXT”);

try {

fp.Demand();

}

catch (SecurityException secEx) {

// Se rilevo una SecurityException

// significa che non ho il permesso

// di accedere il file

Console.WriteLine(secEx.Message);

}

Nell’esempio precedente si richiede il permesso di ac-cesso in lettura al fi le C:\TEMP\INFO.TXT. Si noti il fatto che la richiesta è inclusa in un blocco try...catch al fi ne di interecettare l’eventuale SecurityException. Il funzio-namento di tutti i metodi Demand di tutti i permessi pre-vede infatti lo scatenarsi di una SecurityException, se la richiesta del permesso fallisce. Quella appena vista si dice verifi ca imperativa dei permessi.

Se compiliamo un assembly

provvisto di nome, cultura,

versione e firma digitale,

esso si dirà dotato di uno

“strong name”

DIFENDERSI DAGLI HACKER

Page 18: v2004 06 vbj60

18 VBJ N. 60 - Novembre/Dicembre 2004

In alternativa all’approccio imperativo possiamo utilizzare la sicurezza cosiddetta dichiarativa, che prevede l’utilizzo di attributi .NET, derivati dalla classe base CodeAccess-SecurityAttribute. In questo secondo caso sarà il motore del CLR a eseguire per conto nostro la verifi ca del per-messo da noi dichiarato tramite attributo, al momento del caricamento dell’assembly o della classe, piuttosto che all’esecuzione di un particolare metodo. Anche in que-sto caso, se il permesso è negato, avremo una Securi-tyException da intercettare e gestire.

C#static void Main(string[] args) {

try {

LeggiFile();

}

catch (SecurityException secEx) {

Console.WriteLine(secEx.Message);

}

}

[FileIOPermission(SecurityAction.Demand, Read=@”C:\TEMP\

INFO.TXT”)]

static void LeggiFile() {

//...

}

Come si vede nell’esempio, il metodo LeggiFile è deco-rato con l’attributo FileIOPermission, che informa il CLR del fatto che per essere eseguito deve avere accesso in lettura al solito fi le C:\TEMP\INFO.TXT.

Un aspetto di Code Access Security interessante e da non sottovalutare è il fatto che i permessi, così come le classi per il loro utilizzo dichiarativo e/o imperativo, sono personalizzabili ed estendibili.

Possiamo cioè creare all’interno delle nostre applica-zioni .NET una nostra logica di sicurezza, basata su per-messi personalizzati e classi *Permission e *Permissio-nAttribute personalizzate.

Stack di chiamata e FullTrustL’ultimo aspetto da valutare, in questa breve carrel-

lata sulle funzionalità di sicurezza di .NET, riguarda il caso in cui un hacker tenti di utilizzare un nostro as-sembly, autorizzato a svolgere determinate operazioni in quanto nostro.

In sostanza a un hacker potrebbe venire la tentazione di creare una sorta di “Cavallo di Troia” che sfrutti i per-messi concessi ai nostri assembly per i suoi scopi.

Fortunatamente per come funziona il motore di au-torizzazione del CLR, in caso di chiamate annidate, il CLR non si limita a verifi care che l’assembly corrente abbia i permessi per eseguire una determinata opera-zione, ma ripercorre a ritroso l’intero stack di chiama-

ta, verifi cando che tutti gli assembly nello stack abbia-no quel permesso.

Se e solo se tutti gli assembly nello stack hanno il per-messo di eseguire una determinata operazione, allora quest’ultima sarà autorizzata (Figura 3 ), altrimenti avre-mo la solita SecurityException.

Qualora poi avessimo un assembly che deve sempre e comunque poter eseguire una determinata operazione, per la quale è autorizzato, a prescindere dai permessi dei chiamanti, possiamo richiedere - assumendocene tutti i rischi - che venga disattivata la verifi ca a ritroso nello stack di chiamata, per un particolare permesso.

Ecco un esempio:

C#public void LeggiFile() {

FileIOPermission fp = new FileIOPermission(FileIOPermission

Access.Read, @”C:\TEMP\INFO.TXT”);

fp.Assert();

// ...

FileIOPermission.RevertAssert();

}

In questo caso il metodo LeggiFile potrà accedere al fi le C:\TEMP\INFO.TXT, qualora ne abbia il permesso, anche se dovesse essere invocato da un oggetto che non ha quel permesso, perchè il metodo Assert spegne la verifi ca dello stack per quel particolare permesso (Fi-leIOPermission), con quella confi gurazione (Read su C:\TEMP\INFO.TXT).

Fi gu ra 3 Schema dell’esame dello stack di chiamata durante la verifica dei permessi

DIFENDERSI DAGLI HACKER

Page 19: v2004 06 vbj60

19N. 60 - Novembre/Dicembre 2004 VBJ

Si noti che viene richiamato anche un metodo statico FileIOPermission.RevertAssert che abilita nuovamente la verifi ca del permesso sullo stack di chiamata.

Una chiamata al metodo Assert ha effetto solo fi no alla fi ne dell’esecuzione del metodo che la contiene, tuttavia per avere il livello massimo di sicurezza con-viene disabilitare la verifi ca di un particolare permesso sullo stack di chiamata, solo per il tempo strettamen-te necessario.

Sempre a proposito della verifi ca dei permessi sull’in-tero stack di chiamata, si presenta un'ulteriore situa-zione interessante.

Gli assembly inseriti nella GAC, dal momento che sono dotati di strong name e che solo gli utenti del gruppo Ad-ministrators possono gestirli, vengono automaticamente dotati di un Permission Set illimitato (FullTrust).

Da un lato questo aspetto deve farci rifl ettere sull’op-portunità o meno di mettere un assembly nella GAC, dall’altro pone un problema: per impostazione predefi -nita un assembly non può essere utilizzato da altri as-sembly con un livello di trust più basso del suo.

Questo fatto è ancora una volta dovuto alla volontà di evitare che un hacker tenti di sfruttare componenti al-trui a proprio vantaggio.

Nel caso in cui si voglia quindi utilizzare un assem-bly inserito nella GAC, invocandolo con altri assembly che contrariamente a lui non siano dotati di FullTrust, per esempio perchè non hanno uno strong name e non sono inseriti nella GAC, dobbiamo esplicitamente au-torizzarli.

Per farlo si utilizza un attributo .NET che si chiama Al-lowPartiallyTrustedCallersAttribute che va esplicitato nel-l’assembly da inserire nella GAC.

Nel fi le AssemblyInfo.* dovremmo inserire una riga come la seguente:

C#[assembly:AllowPartiallyTrustedCallers]

Per evitare poi che chiunque possa caricare il nostro as-sembly dalla GAC e invocarne gli oggetti, possiamo uti-

lizzare l’attributo StrongNameIdentityPermissionAttribute, che prevede la possibilità di dichiarare esplicitamente da quali assembly accettiamo di essere invocati, applican-dolo alle classi e/o ai metodi da proteggere.

In questo caso però gli assembly chiamanti devono avere almeno uno strong name, per poter essere identifi cati.

C#[StrongNameIdentityPermission(SecurityAction.LinkDemand,

PublicKey=”024048...”)]

public class Utility {

//...

}

Non abbassiamo mai la guardiaQuando si parla di sicurezza si dice spesso che il

massimo livello di sicurezza di un’applicazione è dato dal suo punto più debole.

Prestiamo molta attenzione a questo concetto e ricor-diamoci che anche se il Framework .NET è decisamente più sicuro rispetto agli ambienti di sviluppo preceden-ti, ogni volta che utilizziamo dei componenti unmana-ged come DLL sviluppate in C++ o le API di Windows, possiamo perdere alcune delle sicurezze che .NET ci offre. Per esempio se passiamo una stringa da .NET a una API e per caso questa API è soggetta a problemi di buffer overrun, sapere che in .NET i buffer overrun non sono possibili ci servirà a poco... l’hacker tenterà di attaccarci sfruttando la vulnerabilità della API.

Teniamo inoltre presente il fatto che questi meccani-smi di sicurezza non si sostituiscono ai criteri di sicu-rezza nativi di Windows, ma si integrano con essi.

La cosa migliore è sfruttare tutte le possibilità a no-stra disposizione per realizzare soluzioni il più possi-bile sicure. Infi ne non dimentichiamo mai di costrui-re le nostre applicazioni affi nché richiedano i privilegi minimi sul sistema. Un utente appartenente al gruppo degli Administrators infatti sarà in grado di spegnere completamente la sicurezza del CLR, rendendo inuti-li tutti i nostri sforzi.

Bibliografi a[1] Jason Bock, Tom Fischer, Nathan Smith, Pete

Stromquist - “.NET Security”, APress, 2002

Riferimenti[2] http://msdn.microsoft.com/security/[3] http://www.devleap.com/Default.aspx?IdCategoria=

SECURITY[4] http://www.devleap.com/SchedaArticolo.aspx?IdAr

ticolo=10150[5] http://msdn.microsoft.com/msdnmag/issues/04/04/

SecurityBriefs/default.aspx[6] http://blogs.msdn.com/shawnfa/

Quando il CLR carica in me-

moria un assembly svolge

una serie di verifiche finalizza-

te a decidere quali permessi

di esecuzione concedergli

DIFENDERSI DAGLI HACKER

Page 20: v2004 06 vbj60

20 VBJ N. 60 - Novembre/Dicembre 2004

Applicazioni sicure con IIS 6 e ASP .NET

di Daniele Bochicchio

DIFENDERSI DAGLI HACKER

Spesso quando si parla di sicurezza, si pensa a strani e complicati esorcismi, trucchi o danze propiziato-rie da eseguire per aggiungere alle nostre applica-

zioni web quello che è in realtà uno dei punti di partenza e dei requisiti imprescindibili di ogni software.

La sicurezza in realtà si compone di poche e semplici re-gole da seguire, sia in fase di sviluppo che in fase di defi -nizione dei requisiti delle nostre applicazioni.

Nel caso del web, infatti, ogni singola richiesta che arriva al web server è potenzialmente dannosa e maligna, fi no a prova contraria: dobbiamo quindi cominciare ad imparare a schermare il nostro lavoro dall’esterno, per fare in modo che sia a prova di attacco e che gli eventuali dati trattati siano al sicuro da occhi indiscreti.

La sicurezza viene da lontanoDobbiamo prestare attenzione, in realtà, ai diversi ambiti

in cui la sicurezza va applicata. Possiamo scrivere applica-zioni sicure dal punto di vista applicativo, ma poi farle gira-re su un server non confi gurato a dovere per infi ciarne co-munque la bontà. Dunque prima di analizzare alcune tecni-che utili per scrivere applicazioni web sicure, dobbiamo as-sicurarci che la piattaforma sulla quale andiamo ad instal-lare le nostre applicazioni non sia da meno.

La sicurezza di un sistema infat-ti si può suddividere in questi ulte-riori punti:

• sicurezza della rete fi sica: fi rewall hardware, politiche di ac-cesso alle porte TCP/IP e conse-guenti fi ltri sul traffi co;

• sicurezza del web server e del da-tabase server: adeguata confi gura-zione, in base alle best practices;

• sicurezza a livello applicativo: scrivere codice (ASP.NET e T-SQL) che non presenti punti di ingresso per chi cerca di attaccarci.

In questo articolo ci soffermeremo in modo particolare sugli aspetti del secondo punto, che riguardano nello specifi co IIS 6, e su alcuni “trucchi” da poter sfruttare per rendere più si-cure le nostre applicazioni basate su ASP.NET. Potrete trovare ampie trat-tazioni della sicurezza a livello appli-cativo negli altri articoli di questo nu-mero della rivista.

La sicurezza è essere proattivi: proteggiamo il web server

Essere proattivi vuol dire prevedere eventuali pericoli che possono esse-re causati in maniera volontaria o ca-suale nella nostra applicazione. Un’ap-plicazione che si blocca (per qualsiasi motivo) non deve in alcun modo in-fl uire sulle altre presenti sullo stesso

L’accoppiata IIS 6 e ASP.NET garantisce la possibilità di creare e far funzio-nare in maniera sicura applicazioni web con pochi e semplici sforzi. Anche se entrambi i sistemi sono già sicuri appena installati, è necessario un ulteriore tocco per garantire alle nostre applicazioni un elevato livello di sicurezza.

Daniele Bochicchio è il content manager di ASPItalia.com, community che si occupa di ASP.NET, Classic ASP e Windows Server System. Si occupa di consulenza e formazione, specie su ASP.NET, e scrive per diverse riviste e siti. È Microsoft .NET MVP, un riconoscimento per il suo impegno a supporto delle community e per l’esperienza maturata negli anni. Il suo blog è all’indirizzo http://blogs.aspitalia.com/daniele/

Page 21: v2004 06 vbj60

21N. 60 - Novembre/Dicembre 2004 VBJ

Meno privilegi, più robustezzaIIS 6 gira sfruttando “local service” come utente anzi-

ché “network service”, potendo contare dunque su pri-vilegi decisamente inferiori.

Se c’è un problema realmente legato ad IIS è la confi -gurazione delle ACL di Windows, ovvero dei privilegi di accesso al fi le system.

Appare ovvio che meno privilegi un processo ha, più robusta risulterà l’intera piattaforma.

La regola che vige in questi casi è quella di impostare sul server i permessi in modo che ogni singola applica-zione possa accedere solo ai fi le che sono strettamente necessari e sicuramente in modo che non possa asso-lutamente arrivare a fi le di sistema.

Questa semplice regola molte volte è ignorata anche in ambienti di hosting condiviso, dove ogni utente ha ac-cesso ai fi le degli altri, in barba alla privacy (oltre che al buon senso).

Per fare in modo che ognuno possa accedere solo a ciò che gli è concesso, è suffi ciente creare per ogni ap-plicazione un utente, da inserire in un gruppo specifi co chiamato ad esempio “Web Users”, ed impostare sulle directory che compongono l’applicazione l’accesso solo a quell’utente.

Successivamente dovremo dire ad IIS 6 di utilizza-re come utente per quell’applicazione l’utente appena creato. Si può trovare un’insieme di schermate che spie-gano meglio il procedimento in Figura 1 e 2. Si arriva a questa maschera dalle proprietà dell’application pool, alla voce Identity.

Il risultato sarà che l’accesso alle risorse del fi le sy-stem (oltre che alle altre risorse del sistema) sarà effet-tuato proprio con questo utente, rendendo impossibile per una pagina scritta male (o i cui parametri siano for-zati ad arte) l’accesso a risorse contenute al di fuori dal-lo spazio riservato all’applicazione.

Cosa può fare IIS 6 per noi: gli application poolDal punto di vista della sicurezza, un ruolo fondamen-

tale è giocato anche dagli application pool, che sono un po’ il concetto di web application (o virtual directory) di IIS 5 rivisto in una chiave differente, dove all’interno di un application pool possono essere presenti più web ap-plication che condividono certi parametri.

IIS 5 ha un’architettura profondamente diversa rispetto a quella di IIS 6. In pratica è tutto racchiuso in un solo processo, inetinfo.exe, che si occupa di gestire i proces-si necessari all’esecuzione delle richieste, l’amministra-zione del web server ed il controllo sullo stato di salu-te dello stesso.

È chiaro che se un sito web porta ad un crash di inetinfo.exe, il relativo sistema di controllo va in crash con il servizio stesso, rendendo di fatto non funzionante il sistema di controllo.

DIFENDERSI DAGLI HACKER

web server. Può sembrare un’eventualità remota ed un concetto del tutto scontato, ma se state usando IIS 5 (e quindi Windows 2000 Server) con molta probabilità una situazione del genere è tutt’altro che lontana da realizzar-si. Ci sono metodi per evitare che ciò accada se state an-cora utilizzando IIS5, ma sono solo palliativi. Un approccio che può funzionare, ma che porta ad un consumo mag-giore di risorse, è la modalità di isolamento alta, così chia-mata per via della possibilità, in questo stato, di isolare il processo che causa problemi rispetto agli altri. Tuttavia si tratta solo di un palliativo temporaneo. IIS 5 nasce in un periodo profondamente differente rispetto ad IIS 6, di cui ci occuperemo a breve, e quindi risente di alcune man-canze a livello architetturale che fortunatamente la versio-ne 6 ha colmato in toto.

Tanto per cominciare, “IIS 6 è secure by default”, che vuol dire, molto semplicemente, che è sicuro anche appena in-stallato. A differenza di IIS 5, non viene installato se non esplicitamente chiesto dall’amministratore del server, e non viene abilitata di default nessuna delle estensioni esterne. Questo vuol dire che appena installato, il web server si li-miterà ad elaborare solo le semplici pagine HTML e che il supporto per ASP.NET, ASP, WebDav e Front Page Exten-sions va attivato esplicitamente ed a parte, dalla console di gestione di IIS. Questo è un vantaggio perché ci permette di rendere funzionanti solo quelle caratteristiche che sono strettamente necessarie, evitando di tenere attive funzio-nalità, come WebDav, che ci espongo al rischio di attac-chi da remoto. In caso di motore disabilitato, la richiesta produrrà un errore 404 (risorsa non trovata).

È da notare che l’errore 404 si avrà anche in presenza del fi le richiesto sul disco fi sso: se il motore associato è disabilitato, non ci sarà verso per il web server di fornire quel fi le al client.

Fi gu ra 1 Per ogni application pool va specificato un re- lativo utente di Windows da utilizzare. In figura la maschera che mostra come cambiarlo

Page 22: v2004 06 vbj60

22 VBJ N. 60 - Novembre/Dicembre 2004

IIS 6 invece si basa su 3 processi separati, ciascuno dei quali assolve a compiti specifi ci ([1]).

In particolare il processo responsabile per la buona sa-lute degli application pool è W3SVC, che si occupa di far partire i worker process e di monitorarne lo stato. Questo vuol dire che se un application pool ha problemi, IIS 6 è in grado di isolarlo e terminarne l’esecuzione, se neces-sario, senza intaccare minimamente gli altri application pool (e quindi le altre applicazioni) presenti.

Tra l’altro viene effettuato un recovery automatico in caso di consumo eccessivo di risorse, come CPU o memoria (parametro impostabile dall’amministratore), in caso di blocco del processo per un numero di secondi stabilito e cosa molto importante, anche in caso di buffer overfl ow, togliendo quindi una potenziale porta di ingresso.

Gli altri due processi, non meno importanti in senso as-soluto ma di minor interesse per quanto riguarda la sicu-rezza, sono http.sys e W3Core:

• HTTP.SYS, come l’estensione stessa suggeri-sce, è un driver in kernel mode, che si occupa sempli-cemente di prendere le richieste che arrivano allo stack di rete di Windows Server 2003 e di smistarle, attraver-so W3Core, al worker process per l’esecuzione. Il fatto che http.sys sia nella kernel area anziché nella user area non rappresenta un pericolo, perché è un processo che di fatto non esegue codice, ma smista solo richieste e risposte sullo stack di rete.• W3Core, si occupa dell’hosting vero e proprio

dei worker process (sono quei processi w3wp.exe che possiamo notare con il task manager).

Cosa possiamo fare noi per IIS 6Che IIS 6 sia sicuro è ormai, a circa un anno e mezzo

dalla sua uscita uffi ciale, un dato di fatto.Lo dimostra anche la realtà: in questi 18 mesi i bug

scoperti per IIS 6 non hanno paragone con le preceden-ti versioni. Sono semplicemente zero.

Tutto dipende dal fatto, con buona probabilità, che IIS 6 è stato riscritto completamente rispetto ad IIS 4 o 5 per essere, prima di tutto, un web server sicuro, affi dabile e dalla garantite prestazioni.

Per rendere però ancora più sicuro il nostro web server dobbiamo metterci del nostro, evitando alcuni comporta-menti ed osservando alcune semplici regole.

Partiamo da ciò che bisogna sempre evitare. Di sicu-ro, come già detto, una cattiva pratica è quella di usare un utente per tutti gli application pool (e quindi per tutte le web application). Vanno impostati i permessi in modo tale che non sia possibile sfruttare problemi delle appli-cazioni per arrivare a leggere il contenuto del disco. Allo stesso modo, è consigliabile tenere i siti in una partizio-ne separata, in modo che eventuali tentativi di forzatu-ra dei parametri non permettano facilmente l’accesso al disco di sistema.

È poi ovviamente consigliato non tenere attivi motori non utilizzati (come WebDav) e servizi non sfruttati (come POP3, SMTP o NNTP). Il sistema più semplice per veri-fi care che non lo siano è mostrato in Figura 3. Come in tutto ciò che riguarda IIS, ci si arriva seguendo la voce “Internet Information Services” all’interno di “Administra-tive tools” nel pannello di controllo.

È poi un atto quasi dovuto disattivare il default web site e l’administrative web site. Infi ne IIS 6 non andrebbe usa-to su domain controller, per evitare di compromettere un server che ricopre un ruolo centrale all’interno di una rete basata su Windows.

Di particolare interesse per la serie “cose da fare asso-lutamente”, c’è la buona regola di tenere sempre da par-te gli eseguibili CGI, in modo che il permesso di esecu-zione venga dato solo ad un certo percorso, e l’elimina-zione del permesso Execute per l’utente anonimo sulla directory %windir%.

Ed ovviamente vige la regola di tenere sempre aggior-nato, con patch e quant’altro, il server.

Protezione contro il code injection in ASP.NET 1.1Nelle altre pagine della rivista avrete certamente trova-

to spunti di rifl essioni su questa tecnica.Senza soffermarci ulteriormente sugli aspetti specifi ci di

questo attacco, cerchiamo di capire invece cosa ASP.NET 1.1 fa di default per noi, in modo da poter valutare, even-tualmente, gli ambiti in cui possiamo muoverci per per-sonalizzarne l’effi cacia.

Nella versione 1.1 (ed attenzione che questo controllo non viene effettuato con la 1.0) di ASP.NET c’è dunque

DIFENDERSI DAGLI HACKER

Fi gu ra 2 Più di un sito web può appartenere ad un application pool, anche se è consigliabile specificarne uno differente per ogni sito, in modo da isolare l’accesso alla risorse

Page 23: v2004 06 vbj60

23N. 60 - Novembre/Dicembre 2004 VBJ

un controllo built-in, che può essere disattivato con la proprietà validateRequest della direttiva @Pages (o del-l’analoga proprietà nel web.confi g su base di tutta l’ap-plicazione), che in breve controlla che all’interno di coo-kie, querystring o campi inviati tramite il metodo post, non si verifi chino queste condizioni:

• presenza del carattere “<” seguito da un carattere al-fanumerico, oppure da “!”;

• presenza della sequenza “&#”, per prevenire l’in-serimento di codici particolari, legati ad entities (X)HTML;

• presenza del termine “script”, seguita da spazi o vir-gole, per evitare inserimenti di codice Javascript;

• presenza del termine “expression”, per evitare injec-tion di codice Javascript attraverso la forma “style=a:expression(javascript)”;

• presenza del termine “on” preceduto da spazi, seguito da caratteri alfanumerici, seguito da spazi e dal sim-bolo “=”, per prevenire injection di codice come “on-Click=”.

Se uno di questi controlli non viene passato, viene scatenata un’eccezione di tipo HttpRequestValidatio-nException.

Di default purtroppo non c’è modo di visualizzare una pagina personalizzata che spieghi all’utente il motivo dell’errore, visto che ASP.NET lo considera una norma-le eccezione non gestita e per questo viene invocata la pagina di errore personalizzata o viene mostrato il soli-to messaggio.

Di fatto l’utente non è in grado di capire se l’errore è stato provocato da qualcosa che ha fatto o dalla nostra applicazione.

Un sistema per evitare questa incertezza consiste nella creazione di un semplice HttpModule (vedi [2]).

Gli HttpModule sono delle classi particolari che per-mettono di intercettare gli eventi di un’applicazione ASP.NET.

Nel nostro caso l’evento che sfrutteremo è ovviamen-te OnError della classe HttpApplication.

C#Private void OnError (Object o, EventArgs e) {

// verifica tipo di errore

Exception ex = Server.GetLastError();

// intercetta solo l’errore specifico

if (ex.GetType().ToString() == “System.Web.HttpRequest

ValidationException”) {

Response.Redirect(“injection.aspx”);

}

}

La pagina injection.aspx mostrerà un testo specifi co che avverte l’utente del problema, migliorando quindi allo stes-so tempo l’usabilità della nostra applicazione.

È utile notare che questo controllo non mette al riparo da XSS (Cross Site Scripting) o SQL-injection, dunque è solo un primo scudo, ma vale sempre la pena di effettuare successivamente dei controlli personalizzati sull’input.

Nascondere gli errori e metterli in cassaforteQuante volte vi è capitato di arrivare su un sito con

un’immagine oggettivamente antiestetica, come quel-la mostrata in Figura 4?

Oltre alla bruttezza (ed alla poca cura che chi ha svi-luppato quel sito ha messo nel proprio lavoro), c’è da considerare anche la pericolosità che un errore così dettagliato può offrire all’utente.

Non è raro che venga mostrato, in certi ambiti e con il debug attivato, il pezzo di codice che ha causato l’er-rore, che magari può offrire all’utente un meccanismo per forzare la nostra applicazione.

Spesso il motivo per cui viene rimossa la pagina per-sonalizzata risiede nel fatto che se c’è bisogno di fare “debug” in tempo reale su un sito che è già in produ-zione, non ci sono altre strade percorribili con quello che ASP.NET ci offre.

Di default possiamo infatti impostare una pagina d’er-rore personalizzata che viene visualizzata sempre, mai o solo se si accede alla pagina dallo stesso indirizzo IP sul quale gira.

Molto spesso invece l’intervento è effettuato da un indirizzo IP remoto, che magari è sempre fi sso e non cambia, e può tornare utile aggiungere un ulteriore op-zione a quelle offerte all’interno del web.confi g, per po-

DIFENDERSI DAGLI HACKER

Fi gu ra 3 Si può notare come sia semplice disattivare il supporto per un motore non richiesto. In figura WebDav, ad esempio, è disattivato

Page 24: v2004 06 vbj60

24 VBJ N. 60 - Novembre/Dicembre 2004

ter specifi care un ulteriore indirizzo IP a cui dare ac-cesso all’errore completo.

La soluzione in questo caso è, ancora una volta, l’uso di un HttpModule che banalmente verifi chi se la richiesta arriva dall’IP specifi cato all’interno del web.confi g.

Solo in questo caso il fl usso della risposta sarà blocca-to e sarà stampato a video, sfruttando il codice che se-gue, lo stesso identico errore che ASP.NET mostra quan-do gli errori personalizzati sono disabilitati.

C#private void OnError(object o, EventArgs e)

{

HttpContext Context = HttpContext.Current;

// ricava indirizzo IP locale e in web.config

string localIP = Context.Request.UserHostAddress;

string localIPconfig = ConfigurationSettings.AppSettings

[“localIP”];

if (localIP == localIPconfig)

{

Context.Response.Clear();

HttpException ex = (HttpException)Context.Server.GetLast

Error();

// errore specifico o generico

Context.Response.Write((ex.GetHtmlErrorMessage()!= null)?

ex.GetHtmlErrorMessage():

String.Concat(“<pre>”,

Context.Server.GetLastError().ToString(),

“</pre>”)

);

// ripulisco gli errori

Context.ClearError();

// blocco l’esecuzione del resto

Context.Response.End();

}

}

Perché il tutto abbia un senso, ancora una volta andre-mo ad intercettare l’evento OnError della classe HttpAppli-cation, costruendo un HttpModule che se ne occupi [3], oppure aggiungendo più semplicemente questo controllo all’HttpModule che abbiamo creato in precedenza.

Una volta compilata la classe, l’assembly generato va inserito nella directory /bin/ dell’applicazione (o in GAC) e va registrato il relativo riferimento all’HttpModule all’in-terno del web.confi g.

Il codice può essere ovviamente modifi cato, in modo che il controllo non venga fatto solo sull’IP, ma ad esempio sfruttando la Forms Authentication di ASP.NET, in base al role associato all’utente [4].

Con questo semplice accorgimento abbiamo dunque reso il nostro lavoro ancora più sicuro.

ConclusioniCome si può notare, la sicurezza è un argomento dav-

vero semplice, che spesso si risolve più che altro nell’ap-plicazione del buon senso, oltre che di qualche trucco imparato con il tempo (o con letture a tema).

Nel caso specifi co, sia IIS 6 che ASP.NET ci danno un’ottima mano, considerato che partire su una piatta-forma robusta permette di arrivare più facilmente a quello

che è un prerequisito del quale non ci si può dimenticare: la sicurezza.

Riferimenti[1] Gli application pool di IIS 6 – Da-niele Bochicchiohttp://www.aspitalia.com/articoli/win2003/iis6_application_pool.aspx[2] HttpModule e HttpHandler – Danie-le Bochicchiohttp://blogs.aspitalia.com/daniele/post53.aspx[3] #528 - Visualizzare l’errore esteso di ASP.NET in base all’indirizzo IP di con-nessione – Daniele Bochicchiohttp://www.aspitalia.com/liste/usag/script.aspx?ID=528 [4] Autenticazione di ASP.NET: For-ms Authentication con roles - An-drea Zani http://www.aspitalia.com/articoli/aspplus/formauthroles.aspx

DIFENDERSI DAGLI HACKER

Fi gu ra 4 Un anonimo ma molto dettagliato errore può contenere molte infor- mazioni utili per chi tenta di attaccare la nostra applicazione!

Page 25: v2004 06 vbj60

25N. 60 - Novembre/Dicembre 2004 VBJ

Sviluppare senzaessere amministratori

di Marco Russo

DIFENDERSI DAGLI HACKER

Scrivere software è un’operazione complessa che ri-chiede al programmatore un controllo più o meno completo sulla macchina che sta utilizzando.

Questa esigenza, unita a un errato luogo comune secondo cui molte attività (come il debug) richiedono un utente am-ministratore, contribuisce ad avere una percentuale altissi-ma di sviluppatori che usano quotidianamente un utente con privilegi amministrativi per fare tutte le operazioni, dalla na-vigazione in internet alla lettura della posta all’installazione dei tool di cui ha bisogno. La mia personale statistica, ba-sata su sondaggi empirici fatti in conferenze e corsi tenuti nell’ultimo anno, valuta questa maggioranza nel 90-95% dei programmatori. Bene, è ora di cambiare queste brutte abi-tudini! Già, perché usare quotidianamente un account am-ministratore è pericoloso per un utente normale, ma per un programmatore è addirittura sbagliato. Accusare in questo modo una percentuale così alta di colleghi è volutamente provocatorio, ma ci sono delle buone ragioni per dirlo: ve-diamo quali sono e come fare per cambiare abitudini.

Perché è giusto farloLa sicurezza è un problema che va affrontato da tanti punti

di vista. Quello che ci interessa esaminare in questo articolo è la necessità di eseguire il codice con il minimo dei privilegi necessari, così da limitare i danni in caso di errore o attacco. Un classico esempio è IIS: se l’utente che esegue il servizio

è LocalSystem un’eventuale vulnerabi-lità può consentire l’accesso a tutte le risorse del sistema, mentre se l’uten-te ha i diritti minimi per eseguire l’ap-plicazione web, ma non può accedere ad altre risorse, i danni derivati da un attacco sono più limitati. Analogamen-te, un utente amministratore che apre l’allegato a una mail o naviga sul web può installare (magari inavvertitamente) qualsiasi software sul suo PC e ha i di-ritti di modifi care tutti i fi le del sistema operativo e l’intero registry. Cosa c’en-trano i programmatori in tutto questo? Prima di tutto, anche noi siamo uten-ti e in quanto tali dovremmo adottare dei comportamenti sicuri. In secon-do luogo, il software che realizziamo deve essere progettato per poter es-sere eseguito con il minimo dei privilegi necessari: può sembrare un’esigenza banale, ma se quotidianamente si usa un utente con privilegi amministrativi, come si può essere sicuri di scrivere del codice che sarà utilizzabile anche da utenti normali? Certo, si potrebbe usare un utente di test con cui effettua-re tutte le verifi che del caso, ma nulla è più istruttivo che mettersi nei panni dell’utente comune e vedere che tipo di esperienza può dover subire.

Usare un utente non amministrato-re per un programmatore ha un dupli-ce vantaggio: consente di adottare un comportamento più sicuro in qualità di utente e permette di comprende-

Sviluppare applicazioni senza essere amministratori è importante per la sicurezza e per scrivere applicazioni eseguibili con il minimo dei privilegi necessari. Vediamo cosa c’è da sapere su Windows e Visual Studio .NET e quali tool sono utili per affrontare questo scenario.

Marco Russo è un consulente specializzato nello sviluppo di applicazioni con .NET Framework e nella realizzazione di soluzioni di Business Intelligence. Autore, docente, speaker a conferenze, fa parte del gruppo DevLeap. Può essere contattato via email: [email protected]. Mantiene un blog all’indirizzo: http://blogs.devleap.com/marco.blog.

Page 26: v2004 06 vbj60

26 VBJ N. 60 - Novembre/Dicembre 2004

runas /user:administrator program.exe

L’uso nidifi cato di RunAs permette la creazione di una fi nestra con un utente locale che, però, accede in rete con un access token del dominio (presupponendo che l’uten-te amministratore locale della macchina non sia anche un utente del dominio). Questa combinazione è importantis-sima quando si lavora in rete, anche soltanto per instal-lare una patch disponibile su un server remoto.

runas /user:administrator “runas /netonly /user:DOMAIN\username

cmd.exe”

Avviare un’applicazione con queste tecniche è scomo-do (ogni volta viene richiesta la password dell’ammini-stratore), ma il suo uso dovrebbe essere un’eccezione e non la regola. Purtroppo in un primo momento sembra l’unica soluzione per avviare tutti quei programmi che, con un utente non amministratore, si rifi utano di partire senza nemmeno fornire una chiara spiegazione dell’er-rore che si verifi ca.

Molti a questo punto desistono e tornano nel vituperato gruppo amministrativo, ma sicuramente chi legge questo articolo non vorrà gettare la spugna tanto presto. Il più delle volte il problema di un’applicazione che non par-te con un utente normale è attribuibile a uno scorretto uso di Registry o fi le su disco. Per capire come risolvere i problemi è necessario comprenderne la causa e quin-di trarre le prime lezioni su come scrivere un programma eseguibile con il minimo dei privilegi.

All’installazione di Windows il registry e i fi le su disco sono protetti con delle ACL (Access Control List) che defi niscono chi può fare che cosa. Nel Registry tutte le voci all’interno di HKEY_LOCAL_MACHINE sono visibili in lettura da tutti gli utenti, ma sono modifi cabili solo dal gruppo di amministratori (come si può vedere in Figu-ra 1). Per vedere le ACL associate al registry è neces-sario usare Regedit su Windows XP e Windows 2003, mentre su Windows NT 4 e Windows 2000 si usa Rege-dt32. Sul disco tutti i dati nelle directory di sistema (tipi-camente c:\windows o c:\winnt) e dei programmi (come c:\Programmi o c:\Program Files) e i fi le nella root (c:\) sono visibili in lettura a tutti gli utenti e modifi cabili solo dagli utenti nei gruppi Administrators e Power Users. No-tare che far parte del gruppo Power Users non è molto diverso, in termini di mancanza di sicurezza, dall’appar-tenere al gruppo di amministratori, quindi per il nostro ra-gionamento è ugualmente sconsigliabile associare il pro-prio utente a tale gruppo.

Quando un’applicazione si rifi uta di partire con un uten-te normale, ma funziona regolarmente se avviata da un amministratore, nel 99% dei casi il motivo è che tenta un’operazione non autorizzata sul registry o sul disco. Se le applicazioni fossero scritte seguendo fedelmente i

DIFENDERSI DAGLI HACKER

re prima e meglio gli errori da evitare (anche osservando quelli dei programmi che non sono scritti così bene).

Primo ostacolo: l’esperienza da utenteSe modifi care il proprio utente rimuovendolo dal grup-

po di amministratori non presentasse degli effetti col-laterali, questo articolo non avrebbe ragione di esiste-re. L’esperienza iniziale di un utente qualsiasi (non solo di un programmatore) è normalmente traumatica. Dopo aver abbandonato il gruppo degli onnipotenti (gli ammi-nistratori), al nuovo logon vi accorgerete di avere mol-te limitazioni:

• Non potete installare applicazioni.• Non potete installare plug-in e ActiveX su Internet

Explorer.• Non potete aggiornare le applicazioni esistenti.• Alcune applicazioni già installate non si possono più

avviare o presentano dei problemi di funzionamento.

Ce n’è abbastanza per desistere da subito. La maggior parte dei problemi si possono aggirare con “RunAs…” (SHIFT + clic col tasto destro sull’icona di un’applicazio-ne) che consente di avviare un processo utilizzando un utente differente, chiedendone l’autenticazione.

Il programma è avviato con un access token (informa-zioni che descrivono l’utente) diverso da quello con cui si stanno eseguendo le altre applicazioni.

Un’alternativa all’interfaccia utente è l’uso del coman-do RunAs, che consente di creare dei batch che ese-guono applicazioni, pannelli di controllo o console am-ministrative:

Fi gu ra 1 Per default un utente ha diritti di sola lettura su

HKEY_LOCAL_MACHINE

Page 27: v2004 06 vbj60

27N. 60 - Novembre/Dicembre 2004 VBJ

dettami delle tante linee guida e dei requisiti per ottenere il logo Windows non ci sarebbe molto da dire, purtrop-po il mondo reale non segue le regole e dobbiamo an-che considerare che molte applicazioni nascono su Win-dows 9x, dove in pratica non esiste il concetto di utente come lo intendiamo oggi e quindi tutti possono fare tut-to ovunque sul disco…

Senza pretendere di essere esaustivi, riassumiamo al-cuni degli errori commessi più di frequente, ripassando le regole che violano:

• Non si può scrivere su fi le e directory di sistema (come c:\windows): alcune applicazioni creano e/o scrivo-no dei fi le .INI o dei fi le che dovrebbero lasciare una traccia dell’avvenuto uso del programma in una cer-ta data, come sistema di controllo della scadenza di una versione demo.

• Non si può scrivere su fi le e directory contenuti nella directory dei programmi (come c:\programmi): molte applicazioni usano dei fi le contenuti in queste cartelle come fi le di dati dell’applicazione, per esempio crea-no un fi le di confi gurazione che viene modifi cato se l’utente cambia le impostazioni del programma; per queste necessità esistono delle directory apposite.

Va notato che a volte l’errore è veramente banale: se un’applicazione ha bisogno di leggere un fi le ma al mo-mento dell’apertura non specifi ca che essa avviene in modalità read-only, il sistema operativo genera un erro-re (il classico access denied) già in fase di apertura del fi le; questo spiega lo sconcerto di chi cerca inutilmen-te nel proprio sorgente delle funzioni di scrittura su fi le senza trovarne. Attenzione perciò a specifi care sempre i privilegi minimi anche quando si apre una risorsa (un fi le, una connessione di rete, una porta di comunicazione), ri-chiedendo solo i servizi minimi indispensabili per la fun-zionalità richiesta successivamente. Se il fi le deve inve-ce essere modifi cabile, è necessario metterlo in una di-rectory adeguata: all’interno della cartella C:\Documents and Settings esistono directory diverse per ogni utente all’interno delle quali sono memorizzati i documenti per-sonali, la confi gurazione del menu Start e dati specifi -ci per l’utente delle applicazioni installate. È importan-te ricordare che tutte le directory “speciali” come que-ste devono essere ottenute con funzioni apposite come SHGetFolderPath.

Se il programma è nostro, quindi, correggiamo l’errore, usiamo la tecnica giusta, ricompiliamo e possiamo usare l’applicazione con un utente normale. Sviluppando senza essere amministratori ci si accorgerà molto presto di pro-blemi simili e il costo necessario a risolvere il problema sarà bassissimo: più un bug invecchia, più la sua elimi-nazione diventa costosa (il caso peggiore è quando arri-va fi no all’utente fi nale).

Quando però il programma non è nostro e non abbia-mo il codice sorgente, ci sono poche alternative. Se sia-mo fortunati, c’è una versione più recente che è scritta meglio; se siamo sfortunati, dobbiamo tenerci l’applica-zione scritta male.

Per evitare di dover ricorrere a RunAs tutte le volte, possiamo modifi care (con un utente amministratore!) le ACL della zona incriminata, registry o disco che sia. Ma come sapere cosa toccare? Una tattica un po’ grosso-lana è quella di individuare il nodo principale sul regi-stry e la directory dell’applicazione sul disco e modifi -care le ACL per tutti i fi le e tutte le directory (o le chiavi di registry) contenute, assegnando il privilegio di scrittu-ra anche a tutti gli utenti che vogliamo abilitare all’uso del programma.

Un approccio migliore, però, consiste nel limitare la modifi ca delle ACL ai soli punti in cui ciò è indispensa-bile: per esempio il singolo fi le (o chiave di registry) og-getto del tentativo di modifi ca. Il problema è capire cosa toccare.

Analizzare il comportamento dei programmiIl vantaggio di avere i sorgenti di un programma è che

si può avviare il debugger e aspettare che succeda qual-cosa, magari controllando quale chiamata genera inizial-

DIFENDERSI DAGLI HACKER

Fi gu ra 2 Impostazione di uno shortcut equivalente a un RunAs automatico per ogni avvio

Page 28: v2004 06 vbj60

28 VBJ N. 60 - Novembre/Dicembre 2004

mente un errore che porta all’interruzione ed eventual-mente all’uscita dall’applicazione. Se però non si dispone di queste informazioni (o non si ha il tempo e/o la capa-cità di addentrarsi nella giungla di codice scritto da ter-zi), una strategia forse addirittura migliore è quella di os-servare l’applicazione dall’esterno utilizzando degli stru-menti adatti.

RegMon e FileMon sono due utility scritte da Mark Russinovich e Bryce Cogswell (e disponibili su www.sysinternals.com) che consentono di tracciare tut-te le chiamate alle API di Windows responsabili, rispetti-vamente, dell’accesso al registry e ai fi le. Per usare que-ste utility è necessario essere amministratori, perché in-stallano dinamicamente un driver che intercetta le chia-mate API Win32 interessate.

Questi strumenti consentono di defi nire dei fi ltri (è uti-le fi ltrare solo le chiamate fatte dal processo analizzato) e di osservare, tra le altre cose, l’esito della chiamata e i diritti di accesso richiesti.

Per esempio, un programma che tenti di aprire in lettura/scrittura un fi le di confi gurazione genera una chiamata Open che fallisce; analizzando il nome del fi le e i diritti richiesti sarà facile individuare l’entità minima (il fi le) a cui apportare modifi che della ACL (si concederà il diritto di modifi ca al singolo utente o al gruppo Users). Per l’ac-cesso al registry la tecnica è analoga anche se gli stru-menti usati sono diversi.

Dal punto di vista della sicurezza il fatto di aumentare i privilegi di accesso ad alcuni fi le posti in zone “critiche” del disco non va contro i principi che stiamo cercando

di seguire. Semplicemente, questo è il male minore: un eventuale attacco può danneggiare alcune risorse dell’ap-plicazione attaccata ma non va a intaccare altre risorse o altre applicazioni della stessa macchina.

Certo, la soluzione ideale resta quella di correggere il software, ma fi no a che ciò non avviene si cerca di limi-tare i potenziali danni.

La mia esperienza è che i primi due giorni sarete molto arrabbiati con chi scrive male i programmi perché tutto il ciclo di attività per individuare i problemi descritti e ri-solverli è piuttosto lungo e noioso (tolti i primi due casi che potreste trovare divertenti, visto che si tratta di una cosa nuova). Questa arrabbiatura è però un’utile lezione per imparare a scrivere meglio il proprio software.

Visual Studio .NETTerminato il primo periodo di adattamento come uten-

te non amministratore, l’ostacolo successivo consiste nel capire come usare Visual Studio .NET senza limitazioni particolari. In generale, l’utente sviluppatore dovrebbe ap-partenere a questi gruppi locali:

• Users• Debugger Users• VS Developers

Come abbiamo già detto, l’utente non deve apparte-nere ai gruppi Administrators e Power Users. Per lo svi-luppo e il debug di applicazioni Web sono necessarie al-tre impostazioni che vedremo più avanti. Alcune attivi-tà, però, restano necessariamente prerogativa degli am-ministratori:

• Registrazione di componenti COM (regsvr32/regasm)

• Installazione di assembly nella GAC (gacutil)• Installazione di componenti .NET nel catalogo COM+

(regsvcs)

In questi casi si è obbligati a rinunciare alla funzionalità dei post-build step di Visual Studio .NET, effettuando la registrazione a mano con un utente amministratore.

Per effettuare il debug l’utente deve avere dei diritti par-ticolari, che abbiamo già ottenuto assegnando l’utente al gruppo Debugger Users (in realtà ciò che conta è il dirit-to SeDebugPrivilege associato all’access token, ma Vi-sual Studio aggiorna già le policy assegnando tale dirit-to al gruppo Debugger Users).

.NET impone però un’ulteriore limitazione rispetto al de-bug di un processo eseguito con un utente diverso: per fare ciò è necessario essere amministratori (non è così per le applicazioni unmanaged).

La limitazione non è percepibile per le applicazioni Win-dows Forms eseguite dall’interno dell’IDE di Visual Studio,

Fi gu ra 3 Impostazione dell’Application Pool con l’utente usato per sviluppare: le directory virtuali corri- spondenti alle applicazioni web sviluppate do- vranno essere assegnate a questo Application Pool (che va creato manualmente).

DIFENDERSI DAGLI HACKER

Page 29: v2004 06 vbj60

29N. 60 - Novembre/Dicembre 2004 VBJ

ma coinvolge chi sviluppa dei servizi o delle applicazioni Web e chi vuole fare debug in remoto. La cosa migliore, in questi casi, è quella di eseguire l’applicazione con lo stesso utente con cui si fa il debug. Se il debug avvie-ne in remoto, l’utente usato deve avere il privilegio Se-DebugPrivilege anche sulla macchina remota (dove, se non c’è Visual Studio, il gruppo Debugger Users non è stato creato, quindi bisogna modifi care le policy a mano) e deve essere amministratore della macchina remota se l’applicazione è di tipo .NET (managed).

Applicazioni WebPer lo sviluppo e il debug di applicazioni Web vale la

pena fare un discorso a parte, perché entrano in gioco diversi fattori.

Per quanto riguarda lo sviluppo, la creazione di un’ap-plicazione ASP.NET da Visual Studio può presentare un problema se si usano le Front Page Server Extension anziché il fi le share [1]: poiché il default è la modalità di connessione fi le share, questo non è un bug così noto. L’appartenenza al gruppo VS Developers garantisce i di-ritti necessari per la creazione di una Virtual Directory in IIS, ma in caso contrario (per es. server web remoto) è sempre possibile creare la Virtual Directory con un uten-te che ha privilegi suffi cienti (per es. amministratore) per poi creare il relativo progetto in Visual Studio con l’uten-te normale.

Per il debug di un’applicazione ASP.NET bisogna con-siderare due aspetti: l’utente con cui gira l’applicazione e i diritti sulle directory usate da ASP.NET.

L’utente si può modifi care impostando in IIS 5 (Win-dows XP) il tag processModel di ASP.NET (notare la pas-sword in chiaro, che si può cifrare con l’utility aspnet_setreg [2]):

<processModel enable=”true”

userName=”DOMAIN\username”

password=”pwd”

/ >

Con IIS6 (Windows 2003), invece, si agisce creando un Application Pool: ogni Virtual Directory è assegnata a un

Application Pool, consentendo così di differenziare gli utenti per applicazioni ASP.NET diverse.

L’utente assegnato all’Application Pool (il nostro uten-te, se vogliamo fare debug) deve appartenere al gruppo IIS_WPG perché sia utilizzabile con ASP.NET. In Figura 3 è visibile la confi gurazione di un Application Pool creato per fare debug (l’utente associato è lo stesso usato per sviluppare e fare debug).

In Figura 4 è evidenziato come si associa una virtual directory di IIS a un Application Pool (in questo caso De-buggable App).

L’utente usato deve avere anche il diritto di accedere in lettura e scrittura alle directory usate da ASP.NET, come la directory in cui sono generati gli assembly a partire dalle pagine ASPX (Temporary ASP.NET Files, per cui il gruppo IIS_WPG ha già Full Control). Il ricorso a Filemon consente di individuare eventuali problemi di questo tipo, in genere causati da componenti o librerie esterne usa-te dall’applicazione web (in particolare se usate compo-nenti COM).

Su MSDN è disponibile un documento [3] che descri-ve molto bene i passi operativi necessari per impostare le confi gurazioni che abbiamo analizzato in questo arti-colo rispetto all’uso di Visual Studio .NET con un utente non amministratore.

Applicazioni Visual Basic 6Se, anche solo per necessità, sviluppate ancora con

Visual Basic 6, purtroppo dovete fare i conti con il fatto che VB6 non è stato minimamente pensato per svilup-pare senza essere amministratori; più che l’ambiente di sviluppo, poi, molti problemi li creano i tanti com-

Nelle impostazioni avanzate delle proprietà di uno shortcut

è possibile specificare la richiesta di credenziali diverse per

ogni esecuzione, evitando di dover cercare il menu RunAs

tutte le volte.

La Figura 2 mostra questa configurazione sullo shortcut

di FileMon.

Ri qua dro 1 Esecuzione con credenziali diverse

Fi gu ra 4 Associazione di una directory virtuale all’Appli- cation Pool usato per il debug delle applicazioni

DIFENDERSI DAGLI HACKER

Page 30: v2004 06 vbj60

30 VBJ N. 60 - Novembre/Dicembre 2004

ponenti di cui è dotato VB6. Il problema è in parte ag-girabile con i sistemi già descritti, usando Filemon e Regmon per capire dove modifi care l’accesso a par-ti del disco e del registry, ma va detto che a volte la colpa (di sviluppare con utenti amministratori) non è tutta degli sviluppatori ma anche di chi gli ha dato le brutte abitudini...

ConclusioniUsare Windows e svilupparci con un utente non-ammi-

nistratore è doveroso. L’esperienza da utenti non ammi-nistratori aumenta la consapevolezza dei comportamenti che vanno evitati per scrivere applicazioni utilizzabili con il minimo dei privilegi.

Purtroppo non è semplice e immediato come potreb-be e dovrebbe essere. In futuro (con Longhorn, la pros-sima versione di Windows) ci saranno molte novità per migliorare la situazione, ma negli anni che ci separano da questo traguardo non ci si può permettere di conti-nuare a seguire comportamenti a rischio.

Per partire con il piede giusto è necessario capire qua-li sono le problematiche più frequenti e come modifi ca-re (per il minimo indispensabile) i diritti di directory e re-gistry necessari a evitare il ricorso al RunAs, che resta indispensabile per alcune attività di tipo amministrativo. Strumenti come Filemon e Regmon sono preziosissimi per individuare queste criticità.

Riferimenti[1] Bug di Visual Studio, KB833896, http://

support.microsoft.com/default.aspx?scid=kb;EN-US;833896

[2] Uso di aspnet_setreg, KB329290, http://support.microsoft.com/default.aspx?scid=kb;EN-US;329290

[3] Developing Software in Visual Studio .NET with Non-Administrative Privileges, http://msdn.microsoft.com/library/en-us/dv_vstechart/html/tchDevelopingSoftwareInVisualStudioNETWithNon-AdministrativePrivileges.asp

DIFENDERSI DAGLI HACKER

Page 31: v2004 06 vbj60

31N. 60 - Novembre/Dicembre 2004 VBJ

Programmare Excel da Visual Basic .NET

di Fabio Scagliola

Questo articolo descrive tre soluzioni diverse per trasferire dati a Excel usando Visual Basic .NET e le classi del .NET framework.

La prima soluzione usa il modello a oggetti di Excel per scrivere nelle celle del foglio di lavoro, come si farebbe attraverso l’interfaccia grafi ca del programma, e crea un grafi co basato sui dati trasferiti.La seconda soluzione crea un fi le di testo separato da tabulazioni che Excel possa aprire come se fosse una cartella di lavoro.

La terza soluzione crea un XML Spreadsheet. Tutte usano le classi del namespace System.Data (per accedere ai

dati), System.IO (per scrivere nel fi le di testo) e System.Xml (per manipolare i documenti XML).In particolare il codice dei listati presuppone le direttive seguenti.

Imports System

Imports System.Data

Imports System.Data.SqlClient

Imports System.IO

Imports System.Xml

Imports System.Xml.Xsl

I datiLe tre soluzioni estraggono dati dal

database Northwind su SQL Server 2000 eseguendo la stored procedure CustOrdersDetail.

Dopo aver assegnato il numero di un ordine al suo unico parametro OrderID, la stored procedure restituisce i dettagli di quell’ordine.

Per eseguirla, tutte le soluzioni usano ADO .NET e in particolare oggetti SqlConnection e SqlCommand. Poi le prime due useranno un SqlDataReader, e la terza una coppia DataAdapter/DataSet.

Il codice seguente, comune alle tre soluzioni, crea un oggetto SqlConnection per collegarsi al database e un oggetto SqlCommand per eseguire la stored procedure.

Dim cn As New SqlConnection(“DATA

SOURCE=(local)\INSTANCE1;INITIAL

Analisi di tre soluzioni per trasferire dati a Excel usando Visual Basic .NET e le classi del .NET framework

Fabio Scagliola (MCSD, MCSE, MCT) fornisce “software e consulenza in materia di informatica” in giro per il mondo. Torna spesso a Milano per insegnare nelle aule di Talento Education & Training

BEGINNER

Fi gu ra 1 Aggiungere un riferimento alla libreria COM di Excel

Page 32: v2004 06 vbj60

32 VBJ N. 60 - Novembre/Dicembre 2004

di prodotti. Per maggiori informazioni su ADO.NET si veda [1].

Programmare il modello a oggetti di ExcelI programmatori VB usano il modello a oggetti di Excel

(e di tutte le applicazioni Offi ce) con maggiore naturalezza dei programmatori C#. Progettato per Visual Basic for Applications, il modello include molti metodi che accettano argomenti facoltativi (supportati solo da VB).

Mentre i programmatori VB possono usare named argument per assegnare valori solo agli argomenti necessari, i programmatori C# devono assegnare a ogni argomento facoltativo il valore Type.Missing o il valore predefi nito di quell’argomento. Inoltre, dal momento che C# non supporta proprietà con argomenti (indexer a parte), i programmatori devono invocare i relativi metodi accessori. Per esempio il metodo get_Range sostituisce la proprietà Range.

Considerate queste differenze, la prima soluzione (Listato 1) usa il modello a oggetti di Excel per scrivere nelle celle del foglio di lavoro come si farebbe attraverso la sua interfaccia grafi ca. Il codice presuppone di aver aggiunto al progetto un riferimento alla Microsoft Excel 10.0 Object Library (Figura 1).

Prima di tutto si apre il collegamento al database con il metodo Open dell’oggetto SqlConnection. Poi si esegue la stored procedure con il metodo ExecuteReader dell’oggetto SqlCommand per ottenere un oggetto SqlDataReader.

A questo punto entra in scena Excel. Il codice seguente avvia Excel, lo rende visibile e crea una nuova cartella di lavoro vuota.

Dim xl As Excel.Application = New Excel.Application

xl.Visible = True

CATALOG=Northwind;USER ID=sa;PASSWORD=password”)

Dim cm As New SqlCommand(“CustOrdersDetail”, cn)

cm.CommandType = CommandType.StoredProcedure

Dim OrderID As SqlParameter = cm.Parameters.Add(“@OrderID”,

SqlDbType.Int)

OrderID.Value = 11077

Dopo aver assegnato il valore opportuno alla proprietà CommandType dell’oggetto SqlCommand (per indicare il tipo di comando che sta per essere eseguito), il codice crea un oggetto SqlParameter per assegnare al parametro della stored procedure il numero dell’ordine che contiene il maggior numero

BEGINNER

Li sta to 1 Programmare il modello a oggetti di Excel

Dim cn As New SqlConnection(“DATA SOURCE=(local)\INSTANCE1;INITIAL CATALOG=Northwind;USER ID=sa;PASSWORD

=password”)Dim cm As New SqlCommand(“CustOrdersDetail”, cn)cm.CommandType = CommandType.StoredProcedureDim OrderID As SqlParameter = cm.Parameters.Add(“@OrderID”, SqlDbType.Int)OrderID.Value = 11077Try cn.Open() Dim dr As SqlDataReader = cm.ExecuteReader() Dim xl As Excel.Application = New Excel.Application xl.Visible = True Dim wb As Excel.Workbook = xl.Workbooks.Add() Dim ws As Excel.Worksheet = wb.ActiveSheet ws.Cells(1, 1) = “Product name” ws.Cells(1, 2) = “Unit price” ws.Cells(1, 3) = “Quantity” ws.Cells(1, 4) = “Discount” ws.Cells(1, 5) = “Total price” ws.Range(“A1:E1”).Font.Bold = True Dim RowIndex As Int32 = 2 While dr.Read() ws.Cells(RowIndex, 1) = dr(“ProductName”) ws.Cells(RowIndex, 2) = dr(“UnitPrice”) ws.Cells(RowIndex, 3) = dr(“Quantity”) ws.Cells(RowIndex, 4) = dr(“Discount”) ws.Cells(RowIndex, 5) = dr(“ExtendedPrice”) RowIndex += 1 End While ws.Range(“B2:B” & RowIndex & “;E2:E” & RowIndex).NumberFormat = “€ #,##0.00” Dim ch As Excel.Chart = NuovoGrafico(ws.Range(“A1:A26;E1:E26”), Excel.XlChartType.xlColumnClustered, “Cu-

stomer Orders Detail”) wb.SaveAs(“soluzione1.xls”) xl.Quit() dr.Close()Catch ex As Exception Console.WriteLine(ex)Finally cn.Close()End Try

Fi gu ra 2 Il risultato del Listato 2

Page 33: v2004 06 vbj60

33N. 60 - Novembre/Dicembre 2004 VBJ

Dim wb As Excel.Workbook = xl.Workbooks.Add()

Dim ws As Excel.Worksheet = wb.ActiveSheet

Per creare una nuova cartella di lavoro vuota, si invoca il metodo Add dell’oggetto Workbooks senza assegnare un valore al suo unico argomento facoltativo Template (che serve a creare una nuova cartella di lavoro partendo da un modello). Il metodo restituisce un oggetto Workbook, la cui proprietà ActiveSheet contiene un riferimento al foglio di lavoro attivo, ovvero il primo dei tre fogli della cartella di lavoro vuota.

Per lavorare con le celle del foglio di lavoro si usa un oggetto Range, che rappresenta una singola cella o un intervallo di celle.

Sia la proprietà Range sia la proprietà Cells restituiscono un oggetto Range. La proprietà Range permette di riferirsi a un intervallo di celle con la sintassi usata normalmente da Excel. La proprietà Cells permette di riferirsi a una singola cella come a un elemento di un array bidimensionale, indicando gli indici (numerati a partire da 1) di riga e di colonna. La proprietà Range accetta due argomenti: i riferimenti alle due celle agli estremi opposti dell’intervallo.

Si possono anche indicare i due riferimenti nel primo argomento separati dai due punti. In questo modo si possono anche defi nire intervalli di celle non adiacenti. Il codice seguente usa solo il primo argomento della proprietà Range per applicare un formato a un intervallo di celle non adiacenti.

ws.Range(“B2:B” & RowIndex & “;E2:E” & RowIndex).NumberFormat

= “€ #,##0.00”

La soluzione usa sistematicamente la proprietà Range per applicare formati a intervalli di celle e la proprietà Cells per scrivere dati nelle singole celle.

Grafi ciIl modello a oggetti di Excel rende semplice anche la

creazione di un grafi co. Per esempio il Listato 2 mostra il codice del metodo NuovoGrafi co che restituisce un oggetto Chart in cambio di tre argomenti. Il primo argomento r deve essere un oggetto Range, per indicare le celle contenenti i dati di origine del nuovo grafi co.

Il secondo argomento chType deve essere il valore di uno dei membri dell’enumeration XlChartType, per indicare il tipo del nuovo grafi co.

Il terzo argomento chTitle deve essere una stringa, per indicare il titolo del nuovo grafi co. La Figura 2 mostra il risultato del metodo NuovoGrafi co applicato alle celle A1:A26;E1:E26 del foglio di lavoro creato dal Listato 1, per ottenere un grafi co di tipo “istogramma non in pila” intitolato “Customer Orders Detail”. In pratica mostra il risultato della seguente istruzione.

Dim ch As Excel.Chart = NuovoGrafico(ws.Range(“A1:A26;E1:E26”),

Excel.XlChartType.xlColumnClustered, “Customer Orders Detail”)

Per creare un grafi co a partire dalle celle contenenti i dati di origine, il metodo NuovoGrafi co deve risalire al foglio di lavoro che contiene le celle e da qui alla cartella di lavoro che contiene il foglio.

Dim ws As Excel.Worksheet = r.Worksheet

Dim wb As Excel.Workbook = ws.Parent

Dim ch As Excel.Chart = wb.Charts.Add()

Prima di tutto legge il valore della proprietà Worksheet dell’oggetto Range per risalire all’oggetto Worksheet,

BEGINNER

Li sta to 2 Creare un grafico

Function NuovoGrafico(ByVal r As Excel.Range, ByVal chType As Excel.XlChartType, ByVal chTitle As String) As

Excel.Chart Dim ws As Excel.Worksheet = r.Worksheet Dim wb As Excel.Workbook = ws.Parent Dim ch As Excel.Chart = wb.Charts.Add() ch.SetSourceData(r) ch.ChartType = chType ch.HasLegend = False ch.HasTitle = True ch.ChartTitle.Text = chTitle ch.PlotArea.Border.LineStyle = Excel.XlLineStyle.

xlLineStyleNone ch.PlotArea.Interior.ColorIndex = Excel.XlColorIndex.

xlColorIndexNone ch.Location(Excel.XlChartLocation.xlLocationAsObject,

ws.Name) Return chEnd Function

Fi gu ra 3 Il risultato del Listato 4

Page 34: v2004 06 vbj60

34 VBJ N. 60 - Novembre/Dicembre 2004

poi legge il valore della proprietà Parent dell’oggetto Worksheet per risalire all’oggetto Workbook, infi ne invoca il metodo Add della collection Charts dell’oggetto Workbook per creare un nuovo oggetto Chart.

Creato il grafi co, il codice usa il metodo SetSourceData per indicare le celle contenenti i dati di origine assegnando al suo unico argomento il valore di r, ovvero un oggetto Range.

ch.SetSourceData(r)

Il codice assegna poi un valore ad alcune proprietà dell’oggetto Chart.

Anzitutto imposta il tipo di grafi co assegnando alla proprietà ChartType il valore di chType, ovvero uno dei membri dell’enumeration XlChartType.

ch.ChartType = chType

Nasconde la legenda e mostra il titolo usando rispettivamente le proprietà HasLegend e HasTitle e imposta il titolo assegnando la stringa chTitle alla proprietà Text dell’oggetto ChartTitle.

ch.HasLegend = False

ch.HasTitle = True

ch.ChartTitle.Text = chTitle

Infi ne, dopo aver reso trasparente bordo e sfondo dell’area del tracciato, usa il metodo Location dell’oggetto Chart al fi ne di rendere il grafi co un oggetto sul foglio di lavoro.

ch.Location(Excel.XlChartLocation.xlLocationAsObject, ws.Name)

Il metodo Location richiede due argomenti: il primo deve essere un membro dell’enumeration XlChartLocation (in questo caso xlLocationAsObject) e il secondo il nome del foglio di lavoro.

Creare un fi le di testoExcel permette di importare dati da un fi le di testo

separato da tabulazioni (e non solo) in un foglio di lavoro per mezzo del Text Import Wizard.

Se poi il nome del fi le termina con estensione xls, allora Excel lo apre come se fosse una cartella di lavoro senza nemmeno chiamare in causa il wizard. (Allo stesso modo si potrebbe anche creare un fi le di testo separato da virgole - CSV, un formato standard - ma il “trucco” dell’estensione xls non funzionerebbe.)

La seconda soluzione (Listato 3) parte da questa osservazione per creare un fi le di testo chiamato soluzione2.xls che contenga nella prima riga i nomi dei campi separati da una tabulazione e poi i dati strutturati,

BEGINNER

Li sta to 3 Creare un file di testo

Dim cn As New SqlConnection(“DATA SOURCE=(local)\INSTANCE1;INITIAL CATALOG=Northwind;USER ID=sa;PASSWORD

=password”)Dim cm As New SqlCommand(“CustOrdersDetail”, cn)cm.CommandType = CommandType.StoredProcedureDim OrderID As SqlParameter = cm.Parameters.Add(“@OrderID”, SqlDbType.Int)OrderID.Value = 11077Try cn.Open() Dim dr As SqlDataReader = cm.ExecuteReader() Dim sw As StreamWriter = New StreamWriter(“soluzione2

.xls”, False, System.Text.Encoding.Unicode) sw.WriteLine(“Product name” & vbTab & “UnitPrice” & vbTab & “Quantity” & vbTab & “Discount” & vbTab & “Total

price”) While dr.Read() sw.WriteLine(dr(“ProductName”) & vbTab &

dr(“UnitPrice”) & vbTab & dr(“Quantity”) & vbTab & dr(“Discount”) & vbTab & dr(“ExtendedPrice”))

End While sw.Close() dr.Close()Catch ex As Exception Console.WriteLine(ex)Finally cn.Close()End Try

Li sta to 4 Creare un XML Spreadsheet

Dim cn As New SqlConnection(“DATA SOURCE=(local)\INSTANCE1;INITIAL CATALOG=Northwind;USER ID=sa;PASSWORD

=password”)Dim cm As New SqlCommand(“CustOrdersDetail”, cn)cm.CommandType = CommandType.StoredProcedureDim OrderID As SqlParameter = cm.Parameters.Add(“@Order

ID”, SqlDbType.Int)OrderID.Value = 11077Try cn.Open() Dim da As SqlDataAdapter = New SqlDataAdapter(cm) Dim ds As DataSet = New DataSet da.Fill(ds) ds.WriteXml(“DataSet.xml”) Dim xslt As XslTransform = New XslTransform xslt.Load(“soluzione3.xslt”) xslt.Transform(“DataSet.xml”, “soluzione3.xml”,

Nothing)Catch ex As Exception Console.WriteLine(ex)Finally cn.Close()End Try

Page 35: v2004 06 vbj60

35N. 60 - Novembre/Dicembre 2004 VBJ

un record per ogni linea con i campi sempre separati da una tabulazione. Dopo aver ottenuto un oggetto SqlDataReader come nella prima soluzione, il codice seguente mostra come creare un oggetto StreamWriter per scrivere nel fi le di testo.

Dim sw As StreamWriter = New StreamWriter(“soluzione2.xls”,

False, System.Text.Encoding.Unicode)

sw.WriteLine(“Product name” & vbTab & “UnitPrice” & vbTab &

“Quantity” & vbTab & “Discount” & vbTab & “Total price”)

ll metodo WriteLine dell’oggetto StreamWriter scrive testo seguito dai caratteri carriage return e line feed (codici 13 e 10). La costante vbTab concatenata alla stringa rappresenta una tabulazione (codice 9). Così la seconda istruzione del codice precedente scrive i nomi dei campi nella prima riga del fi le; allo stesso modo l’istruzione contenuta nel ciclo While scrive una riga per ogni record letto.

Creare un XML SpreadsheetQuando Excel versione 2002 (quello di Offi ce XP) o

successiva salva una cartella di lavoro come documento XML, rispetta le regole di una grammatica chiamata XML Spreadsheet.

Queste regole, ben descritte nell’articolo “XML Spreadsheet Reference” [2], sono rispettate anche dalla terza soluzione (Listato 4) per creare un documento XML che Excel possa aprire come se fosse una cartella di lavoro. Diversamente dalle prime due soluzioni che usano un SqlDataReader, questa ultima usa un DataSet (e un DataAdapter per riempirlo) per sfruttare il suo metodo WriteXml e salvarlo come documento XML con una sola istruzione.

ds.WriteXml(“DataSet.xml”)

Dim xslt As XslTransform = New XslTransform

xslt.Load(“soluzione3.xslt”)

xslt.Transform(“DataSet.xml”, “soluzione3.xml”, Nothing)

Tra i molti overload del metodo WriteXml, il codice precedente usa quello che richiede solo il nome del fi le, percorso incluso.

Il resto del codice trasforma il documento ottenuto in un esempio (instance) di XML Spreadsheet con una XSLT (eXtensible Stylesheet Language Transformation). Per farlo, usa un oggetto XslTransform che carica un foglio di stile con il metodo Load ed esegue la trasformazione con il metodo Transform.

I tre argomenti richiesti da questo overload del metodo Transform sono due stringhe e un oggetto XmlResolver qui non usato. Le due stringhe contengono rispettivamente il nome del documento di origine e il nome del documento di destinazione, percorso incluso.

La trasformazione coinvolge il documento di origine (ottenuto salvando il DataSet come documento XML), il documento di destinazione (XML Spreadsheet) e il foglio di stile XSL usato per trasformare il primo nel secondo.

Il documento di origine (Listato 5) ha un elemento radice con lo stesso nome del DataSet (per assegnare un nome al DataSet sarebbe stato suffi ciente crearlo con un altro costruttore). La radice per ogni record contiene un elemento Table, che a sua volta per ogni campo contiene un elemento fi glio con lo stesso nome del campo. Per avere una conferma di queste pur semplici regole grammaticali si potrebbe usare un altro overload del metodo WriteXml che permetta di includere nel documento il suo schema.

ds.WriteXml(“DataSet.xml”, XmlWriteMode.WriteSchema)

Il foglio di stile soluzione3.xslt (Listato 6) permette di trasformare la grammatica di questo documento in quella di un XML Spreadsheet. Il codice seguente mostra un esempio di XML Spreadsheet che contiene una cartella di lavoro e un foglio di lavoro chiamato “Il mio primo XML Spreadsheet”. Nella cella A1 e nella cella B1 scrive due numeri e nella cella C1 una formula che somma il valore delle altre due.

BEGINNER

Li sta to 5 Parte del DataSet salvato come docu-

mento XML

<?xml version=”1.0” standalone=”yes”?><NewDataSet> <Table> <ProductName>Chang</ProductName> <UnitPrice>19.0000</UnitPrice> <Quantity>24</Quantity> <Discount>20</Discount> <ExtendedPrice>364.8000</ExtendedPrice> </Table> <Table> <ProductName>Aniseed Syrup</ProductName> <UnitPrice>10.0000</UnitPrice> <Quantity>4</Quantity> <Discount>0</Discount> <ExtendedPrice>40.0000</ExtendedPrice> </Table> <Table> <ProductName>Chef Anton’s Cajun Seasoning</ProductName> <UnitPrice>22.0000</UnitPrice> <Quantity>1</Quantity> <Discount>0</Discount> <ExtendedPrice>22.0000</ExtendedPrice> </Table>

<!-- ... -->

</NewDataSet>

Page 36: v2004 06 vbj60

36 VBJ N. 60 - Novembre/Dicembre 2004

<?xml version=”1.0” ?>

<ss:Workbook xmlns:ss=”urn:schemas-microsoft-com:office:spread

sheet”>

<ss:Styles>

<ss:Style ss:ID=”evidenziato”>

<ss:Interior ss:Color=”#ffff00” ss:Pattern=”Solid” />

</ss:Style>

</ss:Styles>

<ss:Worksheet ss:Name=”Il mio primo XML Spreadsheet”>

<ss:Table>

<ss:Row>

<ss:Cell>

<ss:Data ss:Type=”Number”>1</ss:Data>

</ss:Cell>

<ss:Cell>

<ss:Data ss:Type=”Number”>2</ss:Data>

</ss:Cell>

<ss:Cell ss:Formula=”=RC[-2]+RC[-1]” ss:

StyleID=”evidenziato” />

</ss:Row>

</ss:Table>

</ss:Worksheet>

</ss:Workbook>

Tutti gli elementi e gli attributi appartengono al namespace associato al prefi sso ss.

L’elemento radice Workbook, il cui attributo xmlns indica lo spazio dei nomi, rappresenta la cartella di lavoro e

Li sta to 6 Il foglio di stile soluzione3.xslt

<?xml version=”1.0” ?><xsl:stylesheet version=”1.0” xmlns:xsl=”http://www.w3.org/1999/XSL/Transform” xmlns:ss=”urn:schemas-microsoft-com:office:sprea

dsheet”>

<xsl:template match=”/”> <ss:Workbook xmlns:ss=”urn:schemas-microsoft-com:

office:spreadsheet”> <ss:Styles> <ss:Style ss:ID=”Bold”> <ss:Font ss:Bold=”1” /> </ss:Style> <ss:Style ss:ID=”Currency”> <ss:NumberFormat ss:Format=”Euro Currency” /> </ss:Style> </ss:Styles> <ss:Worksheet ss:Name=”CustOrdersDetail”> <ss:Table> <ss:Column ss:Width=”192” /> <ss:Column ss:Width=”72” /> <ss:Column ss:Width=”72” /> <ss:Column ss:Width=”72” /> <ss:Column ss:Width=”72” /> <ss:Row ss:StyleID=”Bold”> <ss:Cell> <ss:Data ss:Type=”String”>Product

name</ss:Data> </ss:Cell> <ss:Cell> <ss:Data ss:Type=”String”>Unit price</

ss:Data> </ss:Cell> <ss:Cell> <ss:Data ss:Type=”String”>Quantity</

ss:Data> </ss:Cell> <ss:Cell> <ss:Data ss:Type=”String”>Discount</

ss:Data> </ss:Cell> <ss:Cell>

<ss:Data ss:Type=”String”>Total price</ss:Data>

</ss:Cell> </ss:Row> <xsl:apply-templates /> </ss:Table> </ss:Worksheet> </ss:Workbook></xsl:template>

<xsl:template match=”Table”> <ss:Row> <ss:Cell> <ss:Data ss:Type=”String”> <xsl:value-of select=”ProductName” /> </ss:Data> </ss:Cell> <ss:Cell ss:StyleID=”Currency”> <ss:Data ss:Type=”Number”> <xsl:value-of select=”UnitPrice” /> </ss:Data> </ss:Cell> <ss:Cell> <ss:Data ss:Type=”Number”> <xsl:value-of select=”Quantity” /> </ss:Data> </ss:Cell> <ss:Cell> <ss:Data ss:Type=”Number”> <xsl:value-of select=”Discount” /> </ss:Data> </ss:Cell> <ss:Cell ss:StyleID=”Currency”> <ss:Data ss:Type=”Number”> <xsl:value-of select=”ExtendedPrice” /> </ss:Data> </ss:Cell> </ss:Row></xsl:template>

</xsl:stylesheet>

BEGINNER

Page 37: v2004 06 vbj60

37N. 60 - Novembre/Dicembre 2004 VBJ

BEGINNER

contiene un elemento Worksheet per ogni foglio di lavoro. Ogni elemento Worksheet contiene un elemento Table con un elemento Row per ogni riga e un elemento Cell per ogni cella.

Per ogni cella che contiene dati, il contenuto di un elemento fi glio Data indica il valore e il suo attributo Type indica il tipo. I tipi possibili sono Number, DateTime, Boolean, String ed Error.

Prima ancora degli elementi Worksheet la radice contiene un elemento Styles, al cui interno sono defi niti gli stili da applicare a singole celle o a intervalli di celle.

Ogni elemento Style ha un attributo ID che lo identifi ca univocamente. Gli elementi fi gli ne defi niscono il formato. Per applicare uno stile a un elemento, si assegna il valore dell’attributo ID dello stile al suo attributo StyleID.

Per trasformare in un XML Spreadsheet il documento ottenuto salvando il DataSet (Listato 5), il foglio di stile soluzione3.xslt (Listato 6) contiene due template.

Il primo trasforma la radice del documento di origine nella radice del documento di destinazione creando l’ele-mento Workbook, gli stili, gli elementi Worksheet, Table, e la prima riga con i nomi dei campi.

Il secondo trasforma ogni elemento Table nel docu-mento di origine in un elemento Row nel documento di destinazione.

Quale approccio scegliereSebbene la prima soluzione permetta un controllo assolu-

to sul risultato (il modello a oggetti di Excel permette di la-vorare con le celle del foglio di lavoro come si farebbe at-traverso la sua interfaccia grafi ca), richiede che Excel sia installato sul computer che esegue il programma. Se il pro-gramma fosse eseguito da un server, Excel potrebbe non essere disponibile. Al contrario la seconda soluzione da un lato non permette alcun controllo sul risultato (permet-te solo di scrivere dati “nudi e crudi” nelle celle di un foglio di lavoro), dall’altro non richiede che Excel sia installato sul computer che esegue il programma. La terza soluzione ha i pregi delle prime due senza difetti. Permette controllo as-soluto sul risultato (Figura 3) e non richiede che Excel sia installato sul computer che esegue il programma. Permet-te inoltre di modifi care il risultato senza dovere ricompilare, ma modifi cando solo il foglio di stile.

Bibliografi a[1] Pierpaolo Raguso, Corso ADO.NET, Visual Basic &

.NET Journal numero 56 e successivi

Riferimenti[2] http://msdn.microsoft.com/library/en-us/dnexcl2k2/

html/odc_xmlss.asp

Page 38: v2004 06 vbj60

38 VBJ N. 60 - Novembre/Dicembre 2004

Servizi NT conVB .NET

di Gianluca Cannalire

ENTERPRISE

Nella puntata precedente abbiamo esplorato la classe ServiceBase, ovvero la classe dalla quale ereditare per implementare un servizio NT. Per comprendere

al meglio l’utilizzo delle classi del .NET Framework dedica-te alla creazione di servizi, abbiamo iniziato la realizzazione di un progetto reale (Event Notifi cator) che prevede la no-tifi ca via email delle informazioni che vengono man mano inserite nei vari EventLog di Windows. Naturalmente, que-sto notifi catore verrà eseguito come servizio NT.

In questa seconda ed ultima parte dell’articolo comple-teremo la realizzazione del progetto analizzando le classi che ci serviranno per installare (o rimuovere) il servizio nel sistema e sarà illustrata una tecnica di debug per questo genere di applicazioni, che si differenzia, per alcuni parti-colari, dal debug di una classica applicazione desktop.

Infi ne, studieremo come interagire con il servizio nello stesso modo in cui lavora il Service Control Manager pre-sente nel Control Panel di Windows.

Installare un servizioL’utilizzo della classe ServiceBase non è di per sé suf-

fi ciente per poter realizzare un servizio completo. Infat-ti, per poter informare il sistema operativo della presenza di un eseguibile che dovrà essere eseguito come servizio, è necessario creare una serie di chiavi nel registry ed in particolare nel ramo HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services ([1]). Ovviamente, seppur ciò sia possibile, non è il caso di provvedere manualmente alla creazione delle particolari chiavi/valori all’interno del regi-

stry: affi deremo tale compito ad una specifi ca classe presente nella libre-ria del .NET Framework.

Per l’esattezza, le classi da utilizzare per implementare il codice necessa-rio all’installazione di un servizio NT sono due: la ServiceProcessInstaller e la ServiceInstaller.

La ServiceProcessInstaller, che a sua volta eredita da System.Confi -guration.Install.Installer, è la clas-se che si occupa dell’interazione con le utility di setup quali Win-dows Installer o installutil.exe (che vedremo successivamente) men-tre la ServiceInstaller si occupa di gestire le informazioni necessarie alla ServiceProcessInstaller, speci-fi che per un determinato servizio.È da notare che all’interno di un sin-golo progetto possiamo avere più di una classe che eredita da ServiceBa-se (ovvero, più di un servizio esegui-bile all’interno di un singolo assem-bly .NET) e che, per ognuna di que-ste classi, è necessaria la presenza di una corrispondente istanza di Ser-viceInstaller, mentre sarà suffi ciente una singola istanza di ServiceProces-sInstaller che provveda all’installazio-ne di tutti i servizi.

Cerchiamo di capire bene la funzio-ne di queste due classi partendo dal-la ServiceInstaller: è questa la classe che mantiene effettivamente le infor-mazioni relative al singolo servizio da installare all’interno del sistema ope-rativo. Nella fi nestra delle proprietà (Figura 1) si nota che ci sono poche

Servizi NT2

Caso reale di realizzazione e implementazione di servizi Windows con .NET Seconda parte

Gianluca Cannalire è diplomato in Elettronica e Teleco-municazioni, si occupa della realizzazione di gestionali in VB6 e .NET. È fondatore e coordinatore di “Visual Basic Tips & Tricks” (http://www.visual-basic.it), una community dedicata completamente a Visual Basic in tutte le sue versioni. Da aprile 2003 è stato nominato Microsoft MVP (Most Valuable Professional) per Visual Basic.

Page 39: v2004 06 vbj60

39N. 60 - Novembre/Dicembre 2004 VBJ

È interessante notare che la ServiceInstaller gestisce an-che una serie di eventi relativi alle varie fasi di installazio-ne (Before/AfterInstall, Before/AfterUninstall, ecc.) del sin-golo servizio, eventi che permettono allo sviluppatore di eseguire operazioni particolari (ad esempio, la creazione/rimozione di eventuali chiavi/valori nel registry a supporto del servizio stesso) fornendo pertanto un completo con-trollo sulla fase di installazione e rimozione del compo-nente software. La ServiceProcessInstaller si occupa in-vece di effettuare l’installazione vera e propria del servi-zio (creando e valorizzando le giuste chiavi nel registry) ottenenendo le informazioni dalle varie istanze di Servi-ceInstaller associate. Tramite la ServiceProcessInstaller provvederemo a fornire al sistema ulteriori informazioni relative al servizio ed in particolare le credenziali con le quali vogliamo che il servizio venga eseguito.

A questo scopo utilizzeremo le proprietà Account, User-name e Password. Nella Tabella 1 sono illustrati i possi-bili valori della proprietà Account (di tipo ServiceAccount) notando che solo nel caso il cui il valore venga imposta-to su ServiceProcess.ServiceAccount.User sarà neces-sario impostare uno username ed una password ricono-sciuti validi per il sistema sul quale stiamo installando il servizio. Nel caso in cui non si provvedesse a valorizza-re uno username e la relativa password, durante l’instal-lazione apparirà una dialog box di login che richiederà le informazioni mancanti (Figura 3).

Inizialmente, per non incorrere in eventuali problemi le-gati al contesto di sicurezza, lasciamo il valore di Account su ServiceProcess.ServiceAccount.LocalSystem avendo l’accortezza di utilizzare un account con privilegi più bas-si (se possibile) una volta terminata la prima fase di de-bug dell’applicazione. Un particolare, di non poco con-to, è che tutta la procedura di installazione del servizio viene effettuata in modo transazionale, ovvero, in caso di errore in uno degli step del processo di installazione (ad esempio per la mancanza di particolari diritti di ese-cuzione), tutte le modifi che effettuate dall’installer saran-

ENTERPRISE

proprietà pubbliche. Tra queste, spiccano DisplayName e ServiceName, le quali corrispondono al testo che ver-rà visualizzato all’interno del Service Control Manager di Windows e al nome della classe derivata da ServiceBase per la quale dovrà provvedere all’installazione.

Valorizzeremo DisplayName con “VB.NET Event Noti-fi cator Service” e ServiceName con “Service1” (che nel nostro progetto corrisponde al nome della classe che eredita da ServiceBase).

Ci sono altre due proprietà di cui bisogna tener conto: ServicesDependsOn e StartType, che indicano rispetti-vamente un elenco di servizi dai quali dipende il corret-to funzionamento del servizio (Figura 2) e la modalità di esecuzione dello stesso all’avvio del sistema (Automatic, Manual, Disabled). Imposteremo il valore di StartType su Automatic in modo tale che il nostro servizio venga auto-maticamente eseguito all’avvio del sistema operativo.

Fi gu ra 1 Proprietà del component ServiceInstaller

Tabella 1 I valori della proprietà Account

Valori proprietà ‘Account’ Descrizione

LocalServiceAccount con bassi privilegi sulla macchina locale. Utiliz-

za credenziali anonime per dialogare con server remoti.

LocalSystem Account con alti privilegi

NetworkServiceAccount con privilegi estesi sulla macchina locale. Presen-

za credenziali specifiche per dialogare con server remoti.

User

Account derivato da uno specifico utente definito nel

sistema. Indicando User, sarà necessario valorizzare

anche le proprietà UserName e Password, o fornirle

durante l’installazione del servizio

Page 40: v2004 06 vbj60

40 VBJ N. 60 - Novembre/Dicembre 2004

no sottomesse a rollback e nel sistema non resteranno tracce orfane (fi le, chiavi del registry) della nostra appli-cazione. Le classi ServiceProcessInstaller e ServiceIn-staller dispongono anche di una serie di metodi che nel-la maggior parte dei casi non dovranno essere invocati direttamente dallo sviluppatore. Il metodo Install, dispo-nibile in entrambe le classi, è quello che effettivamente scatena la scrittura nel registry delle varie chiavi e viene invocato direttamente dalle utility di installazione qua-li la installutil.exe.

Aggiungere l’Installer al progettoVediamo ora come aggiungere le classi di installazio-

ne al progetto. In Visual Studio.NET, apriamo il proget-to e selezioniamo dal Solution Explorer il componente/classe “Service1”. Se nell’IDE non compare il designer del componente, assicurarsi che non vi sia una “window code” aperta sul fi le Service1.vb. Eventualmente sarà ne-cessario chiudere questa fi nestra e, cliccando con il ta-sto destro su “Service1”, selezionare View Designer. Solo in questo modo, in fondo alla fi nestra “Properties” del componente, apparirà il link “Add Installer” (Figura 4).Cliccando su “Add Installer”, Visual Studio aggiungerà au-tomaticamente al progetto un nuovo fi le (ProjectInstaller.vb) contenente il codice necessario all’installazione del servi-zio ovvero le instanze di ServiceProcessInstaller e Servi-ceInstaller opportunamente confi gurate.

Tutto il codice (Listato 1) è implementato nella clas-se ProjectInstaller (che eredita a sua volta System.Confi -guration.Install.Installer) che verrà chiamata in causa da InstallUtil.exe grazie all’attributo RunInstaller impostato su True. Per non uscire troppo dall’argomento dell’articolo, non illustrerò il comportamento della classe Installer e dell’attri-

buto RunInstallAttribute; ci basterà sapere che, con que-sta particolare confi gurazione, sarà l’utility di installazione ad invocare i metodi Install esposti dalla ServiceProces-sInstaller e dalle dipendenti ServiceInstaller.

A questo punto, il servizio è pronto all’uso.Eseguiamo il build del progetto e, dopo aver lanciato il

prompt dei comandi di Visual Studio .NET (vsvars32.bat), per avere tutte le variabili d’ambiete correttamente im-postate, digitiamo:

installutil.exe <path assembly>\nomeassembly.exe

Il risultato dell’operazione, qui sotto riportato, confer-merà che tutta l’operazione è stata eseguita all’interno di un’unica transazione:

Running a transacted installation.Beginning the Install phase of the installation.See the contents of the log fi le for the <assembly path>The fi le is located at <assembly path>\<assembly name>.InstallLog.

The Install phase completed successfully, and the Commit phase is beginning.See the contents of the log fi le for the <assembly path>The fi le is located at <assembly path>\<assembly name>.InstallLog.

The Commit phase completed successfully.The transacted install has completed.

A questo punto, apriamo il Service Control Manager dal pannello di controllo di Windows e noteremo che il no-stro servizio compare nella lista (Figura 5), ad ulteriore conferma della corretta installazione del componente; ovviamente non risulta ancora avviato.

ENTERPRISE

Fi gu ra 2 Dipendenze di un servizio

Fi gu ra 3 Richiesta di Login in fase di installazione

Page 41: v2004 06 vbj60

41N. 60 - Novembre/Dicembre 2004 VBJ

ENTERPRISE

Manca un piccolo, ma non trascurabile, dettaglio. Nel-la fi nestra del SCM del vostro computer non apparirà al-cuna descrizione, a differenza di quanto avviene in Figu-ra 5. Per la sostanza del nostro applicativo non cambia molto, ma fornire una descrizione dello scopo del servi-zio utile all’utente che interagirà con il SCM rende il tutto un po’ più professionale.

Come fare per inserire tale descrizione? Considerato che il testo che appare nella colonna “Description” viene rile-vato dall’omonima chiave Description presente in HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\<nome servizio>, dobbiamo aggiungere tale chiave, con il relativo testo descrittivo, sfruttando gli eventi esposti dalla classe ServiceInstaller cui abbiamo precedentemente ac-cennato. Per la precisione, utilizzeremo l’evento AfterIn-stall che viene generato in seguito alla scrittura nel regi-stry delle coppie chiavi/valori fondamentali (Listato 2). È

bene ricordare che per testare ogni volta le nuove modifi -che apportate al codice bisognerà disinstallare e succes-sivamente reinstallare il servizio. Per rimuovere il servizio è suffi ciente utilizzare lo switch /u di InstallUtil.exe

Avviare il servizioNon ci resta che avviare il servizio e testarne l’effetti-

vo funzionamento. Pertanto, selezioniamolo dal Service Control Manager di Windows, tramite un click sul tasto destro, e scegliamo Start. Dopo pochi istanti, nella colon-na Status, il servizio risulterà Started. Per verifi care che tutto funzioni come da manuale, sarà suffi ciente forzare la scrittura di un entry qualsiasi all’interno dell’EventLog Application. Nei sorgenti allegati all’articolo, disponibili sul sito FTP di Infomedia, è compreso un piccolo proget-to di test che simula la scrittura di record nell’EventLog. Lo scopo del servizio era quello di notifi care all’ammini-stratore di un server, tramite email, le informazioni che i vari applicativi scrivono all’interno del registry. Per invia-re i vari messaggi di posta elettronica abbiamo fatto uso della classe MailMessage (namespace System.Web.Mail) che, se non diversamente specifi cato tramite la proprietà SmtpServer di SmtpMail,delega al servizio SMTP di IIS il delivery del messaggio. Questo non vuol dire che per effettuare il test dell’applicazione sia necessario avere il servizio in esecuzione: infatti, sarà suffi ciente controlla-re il contenuto della directory c:\InetPub\Mailroot\Queue dove saranno creati i fi le di testo relativi ai messaggi da spedire (in formato RFC822).

Debug di un servizioEffettuare il debug di un servizio è un’operazione in parte

differente da quella di una normale applicazione WinFor-ms. Se si tenta di avviare nell’IDE il nostro servizio, otter-remo infatti come risposta questo messaggio:

“Cannot start service from the command line or a debugger. A Windows Service must fi rst be

Fi gu ra 4 Aggiungere l'installer ad un servizio

Tabella2 I valori della proprietà Status

Valori proprietà ‘Status’ Descrizione

ContinuePending Il servizio si sta riavviando

Paused Il servizio è in pausa

PausePending Il servizio si sta ponendo in pausa

Running Il servizio è in esecuzione

StartPending Il servizio si sta avviando

Stopped Il servizio non è in esecuzione

StopPending Il servizio si sta fermando

Page 42: v2004 06 vbj60

42 VBJ N. 60 - Novembre/Dicembre 2004

ENTERPRISE

installed (using installutil.exe) and then started with the ServerExplorer, Windows Services Administrative tool or the NET START command.”

Come è possibile quindi procedere al debug step-by-step del codice? Utilizzando Visual Studio .NET possia-mo agganciare il debugger a qualsiasi processo in ese-cuzione sul sistema. Dal menù “Debug -> Process...” si accede alla maschera (Figura 6) dalla quale è possibile selezionare il processo da sottoporre a debug (il proces-so deve essere in esecuzione ovviamente).

Se il servizio è eseguito sotto LocalSystem bisogna se-lezionare la checkbox Show System Process per farlo comparire nell’elenco. Una volta selezionato il proces-so (EventNotifi cator.exe) clicchiamo su Attach e poi su Common Language Runtime. Da questo momento pos-siamo impostare gli opportuni breakpoint nel codice del

progetto e procedere al debug classico. Ci sono ancora un paio di particolari da tenere in considerazione. Il pri-mo è che, per effettuare il debug, il processo deve es-sere in esecuzione, ovvero che il servizio sia stato av-viato. Il secondo è derivante dal primo: come effettuare il debug del metodo OnStart se non possiamo aggan-ciare il debugger prima dell’avvio del servizio? Dobbia-mo trovare un sistema che possa caricare in memoria il nostro processo ancor prima che il metodo OnStart del servizio venga invocato dal Service Control Manager. E il sistema è tutto sommato banale: basta aggiungere al progetto un altra classe ereditata da ServiceBase (senza implementare alcun tipo di codice al suo interno). Come già anticipato all’inizio dell’articolo, all’interno di un sin-golo assembly possono coesistere più classi che ere-ditano da ServiceBase e quindi in un singolo processo possiamo avere più servizi in esecuzione (i quali non ne-

Li sta to 1 La classe ProjectInstaller

Imports System.ComponentModelImports System.Configuration.Install

<RunInstaller(True)> Public Class ProjectInstaller Inherits System.Configuration.Install.Installer

#Region “ Component Designer generated code “

Public Sub New() MyBase.New()

‘This call is required by the Component Designer. InitializeComponent()

‘Add any initialization after the InitializeCom-ponent() call

End Sub

‘Installer overrides dispose to clean up the compo-nent list.

Protected Overloads Overrides Sub Dispose(ByVal disposing As Boolean)

If disposing Then If Not (components Is Nothing) Then components.Dispose() End If End If MyBase.Dispose(disposing) End Sub

‘Required by the Component Designer Private components As System.ComponentModel.IContainer

‘NOTE: The following procedure is required by the Component Designer

‘It can be modified using the Component Designer.

‘Do not modify it using the code editor. Friend WithEvents ServiceProcessInstaller1 As

System.ServiceProcess.ServiceProcessInstaller Friend WithEvents ServiceInstaller1 As System.Service

Process.ServiceInstaller <System.Diagnostics.DebuggerStepThrough()> Private

Sub InitializeComponent() Me.ServiceProcessInstaller1 = New System.Service

Process.ServiceProcessInstaller Me.ServiceInstaller1 = New System.ServiceProcess

.ServiceInstaller ‘ ‘ServiceProcessInstaller1 ‘ Me.ServiceProcessInstaller1.Account = System.

ServiceProcess.ServiceAccount.LocalSystem Me.ServiceProcessInstaller1.Password = Nothing Me.ServiceProcessInstaller1.Username = Nothing ‘ ‘ServiceInstaller1 ‘ Me.ServiceInstaller1.DisplayName = “VB.NET Event

Notificator Service” Me.ServiceInstaller1.ServiceName = “Service1”

‘ ‘ProjectInstaller ‘ Me.Installers.AddRange(New System.Configuration.

Install.Installer() {Me.ServiceProcessInstaller1, Me.ServiceInstaller1})

End Sub

#End Region

End Class

Page 43: v2004 06 vbj60

43N. 60 - Novembre/Dicembre 2004 VBJ

lore che dovremo utilizzare nel costruttore della clas-se ServiceController. ServiceName rispecchia la chia-ve specifi ca del servizio creata all’interno di HKEY_LO-CAL_MACHINE\SYSTEM\CurrentControlSet\Services.DisplayName è invece solo un valore “user friendly” per identifi care facilmente un servizio all’interno del SCM.

Tra i membri della ServiceController ci sono un paio di metodi che vale la pena segnalare: GetServices (shared) e ExecuteCommand. Il primo restituisce una lista di og-getti ServiceController rappresentanti ogni singolo servizio installato sulla macchina locale; per ottenere l’elenco dei

ENTERPRISE

cessariamente devono interagire tra loro od essere tut-ti in esecuzione). Sfruttando il fatto che il processo vie-ne caricato in memoria all’avviamento del primo servizio invocato dal SCM e scaricato quando l’ultimo servizio viene fermato, ecco che l’uso di un servizio fi ttizio per-mette di caricare in memoria l’immagine del nostro pro-cesso ed agganciare il debugger prima di avviare il ser-vizio vero e proprio da debuggare. Nonostante tutto c’è ancora qualcosa di cui dobbiamo tener conto durante il debug: il SCM quando invoca lo startup di un servizio si aspetta che lo stesso risponda in un tempo massimo di 30 secondi, altrimenti genererà l’errore “Could not start the VB.NET Event Notifi cator service on Local Compu-ter. Error 1053: The service did not respond to the start or control request in a timely fashion.”

Controllare un servizioDopo aver visto come creare, installare ed eseguire il

debug di un servizio, è il momento di capire come possia-mo controllare il suo stato al di fuori del Service Control Manager standard di Windows. Come potete immagina-re ancora una volta l’immensa libreria di .NET ci viene in aiuto. All’interno del namespace System.ServiceProcess troviamo il tipo ServiceController, che in sostanza per-mette di connettersi tramite apposite API ad un servizio installato nel sistema, fornendo la capacità di controllare e/o modifi care il suo stato. La ServiceController è una classe abbastanza semplice ed intuitiva da utilizzare: una volta creata un’istanza, passando come parametro al co-struttore il nome del servizio per il quale vogliamo otte-nere il controllo, sarà suffi ciente far riferimento ai metodi Start, Stop, Pause e Continue per modifi care lo stato di attività del servizio e alle proprietà DisplayName, Servi-ceName, MachineName, ServiceType e Status per otte-nere informazioni sullo stesso. È bene ribadire che Ser-viceName e DiplayName sono due cose ben differenti: ServiceName, che corrisponde al nome della classe che eredita da System.ServiceProcess.ServiceBase, è il va-

Fi gu ra 5 Il Service Control Manager di Windows ed il nostro servizio

Li sta to 2 Modificare il registry durante l'installa- zione del servizio

Private Sub ServiceInstaller1_AfterInstall(ByVal sender As System.Object, ByVal e As System.Configuration.Install.InstallEventArgs) Handles ServiceInstaller1.

AfterInstall

Dim regDescription As Microsoft.Win32.RegistryKey

Dim strDescription As String = “”

regDescription = _ Microsoft.Win32.Registry.LocalMachine.

OpenSubKey( _ “System\CurrentControlSet\Services\” & _ Me.ServiceInstaller1.ServiceName & “\”,

True)

If (Not regDescription Is Nothing) Then regDescription.SetValue(“Description”, _ “Questa è la descrizione del servi-

zio”) regDescription.Close() End If

End Sub

Fi gu ra 6 Aggancio del debugger al processo

Page 44: v2004 06 vbj60

44 VBJ N. 60 - Novembre/Dicembre 2004

zio) tramite l’invio di un valore numerico (i valori ammessi vanno da 128 a 255). Pertanto, se vogliamo sfruttare que-sto metodo per modifi care il comportamento del servizio, all’interno dello stesso sarà necessario prevedere il codi-ce che reagisca alla ricezione di questi valori (intercettati dalla ServiceBase tramite l’evento OnCustomCommand. Nei sorgenti che accompagnano l’articolo è presente un esempio di Service Controller personalizzato che mostra anche l’uso del metodo ExceuteCommand.

ConclusioniCome abbiamo potuto vedere, la realizzazione di un servi-

zio, grazie all’infrastruttura offerta dal .NET Framwork, è di-ventata un’operazione banale anche con Visual Basic(.NET). Basterà aggiungere al nostro servizio un sistema di moni-toraggio del fi le system (vedi System.IO.FileSystemWatcher) e magari implementare anche la notifi ca via SMS ([2],[3]) per ottenere un tool completo per la notifi ca eventi.

Riferimenti[1] “CurrentControlSet\Services Subkeys Entries”, http:/

/support.microsoft.com/?id=103000[2] “Il protocollo AT+ dei telefoni cellulari”, DEV 115, 116,

117, 118, 119, 120[3] Vito Vessia , “Programmare il cellulare”, Hoepli,

2002

servizi di una macchina remota è suffi ciente fornire, come argomento del metodo GetServices, il nome di un com-puter remoto. Nel Listato 3 viene illustrato un possibile uso del metodo GetServices. Tramite ExecuteCommand possiamo invece inviare dei “comandi personalizzati” al nostro servizio. Effettivamente non si tratta di comandi veri e propri: la ExecuteCommand ci permette di comu-nicare in modalità ‘one-way’ (ovvero solo verso il servi-

ENTERPRISE

Li sta to 3 Enumerazione dei servizi

Private Sub ListAllServices() Dim mySCMs() As ServiceController mySCMs = ServiceController.GetServices()

Dim mySCMTemp As ServiceController

ListView1.BeginUpdate() ListView1.Items.Clear()

For Each mySCMTemp In mySCMs

Dim myItem As System.Windows.Forms.ListViewItem = ListView1.Items.Add(mySCMTemp.ServiceName)

myItem.SubItems.Add(mySCMTemp.DisplayName)

w w w . i n f o m e d i a . i t / r i v i s t e / l o g i n

Scrivi anche tu un articolo!

Come fare? Semplice!Basta inviare la descrizione del proprio articolo a

[email protected]

a chi si rivolge, che cosa spiega,

i titoli di paragrafo e i relativi abstract

Se ti piace scrivere e l’informatica è la tua passione,non perdere questa occasione! Login, la prima rivista italiana dedi-cata ai professionisti di Internet, sta cercando nuovi collaboratori

e...uno di questi potresti proprio essere tu!

WEB PUBLISHING Per i creativi della rete. HTML, XML, VRML, ASP, scripting, multimediaE-COMMERCE Soluzioni per il commercio elettronico Stato dell’arte, pagamenti sicuri, negozi virtuali, crittografia, tool di sviluppo, ecc.NETWORKING Installazione e configurazione di computer in rete. Protocolli, routing, hub, bridge, router, switch, network management, ecc.

Page 45: v2004 06 vbj60

45N. 60 - Novembre/Dicembre 2004 VBJ

Grafici dinamiciin ASP .NET

di Dino Esposito

Se avete mai avuto a che fare con la programmazio-ne grafi ca, in particolare quella dei grafi ci, nell’era pre-.NET – vista oggi sembra una sorta di Jurassic

Windows! – potrete solo ringraziare il destino per avervi fatto incontrare questo ramo della programmazione solo nell’era di .NET.

Il .NET Framework contiene una libreria avanzata di fun-zioni conosciuta come GDI+, in onore del mitico Windows Graphics Device Interface (GDI), croce e delizia (bé, più cro-ce) per generazioni di programmatori Windows.

I servizi GDI+ rientrano nelle seguenti categorie: grafi ca vettoriale 2D ed elaborazione delle immagini. La grafi ca vettoriale 2D permette di disegnare fi gure semplici come linee, curve e poligoni. Sotto la dicitura “elaborazione im-magini” invece ricadono tutte quelle funzioni per visualiz-zare, manipolare, salvare e convertire immagini bitmap e vettoriali. Infi ne, può essere defi nita anche una terza ca-tegoria di funzioni, quelle che riguardano il testo, ovvero la sua visualizzazione in tutte le salse, giocando con font, dimensioni e stili.

Come potete vedere, GDI+ fornisce solo le basi per la programmazione grafi ca; utilizzando i suoi tool a basso li-vello si possono confezionare forme più avanzate, come appunto dei grafi ci. Fintanto che avete bisogno di visualiz-

zare una serie di barre verticali, ci si può anche arrangiare da soli. Ma come fare quando il cliente chiede di più?

Usare le classi GDI+ per produrre un grafi co come quello in Figura 1 è sicuramente possibile, ma certamen-te non banale. Il codice per piazzare i valori al centro delle sezioni richie-de un po’ di tempo. Lo stile 3D con effetti luce pure. E i piccoli partico-lari come rendere le etichette visibi-li sia sui colori chiari che scuri non è da meno.

Insomma, sono tutte cose che si possono ottenere da soli, ma ne vale la pena in termini di tempo/costo? E siamo sicuri di perdere del tempo e contemporaneamente soddisfare le aspettative del cliente? Vi dico che, anche se riuscissimo a soddisfare le sue aspettative, avremmo perso l’op-portunità di superarle, allo stesso co-sto o anche a meno!

Un grafi co a torta è ottimo per rap-presentare i dati. Ma non tutti i tipi di dati. Il bello è quando abbiamo uno strumento che permette di cambiare tipo di grafi co con piccole modifi che. È diffi cile implementare a mano il co-dice per il grafi co di Figura 2.

Effetti come la trasparenza, lo stile 3D, lo sfondo richiedono codice; e il codice richiede tempo e denaro. E, molto importante, quello non è il tipo di codice per il quale siete pagati.

Riassumendo, l’aiuto professionale che vi può dare una libreria grafi ca ben fatta può portare ad un servizio di prima classe per il cliente. Dovre-ste sempre considerarlo.

Vale la pena di sprecare tempo per creare grafici da zero? Molto meglio utilizzare una libreria altamente personalizzata. Vediamo tutti i pregi di .netCHARTING

Dino Esposito è consulente e formatore per Wintellect, per cui tiene il corso ADO.NET. Dino collabora con MSDN Magazine e MSDN Voices. È autore di Building Web Solu-tions with ASP.NET e ADO.NET (Microsoft Press), è anche co-fondatore di www.vb2themax.com. È raggiungibile via e-mail all’indirizzo [email protected]

WEB

Page 46: v2004 06 vbj60

46 VBJ N. 60 - Novembre/Dicembre 2004

bly del controllo nella pagina e se ne piazza un’istanza nel sorgente ASPX:

<%@ Register TagPrefix=”dotnet” Namespace=”dotnetCHARTING”

Assembly=”dotnetCHARTING”%>

...

<dotnet:Chart runat=server id=chart />

Poi bisogna collegare il controllo con qualche dato per generare il grafi co. Il modello di .netCHARTING consiste in “elementi” che insieme formano una “serie”. .netCHAR-TING legge i dati da una serie. Una o più serie possono essere raggruppate in una collection e passate al con-trollo. La classe Element rappresenta un elemento del grafi co ed espone proprietà per contenere differenti tipi di dati come i valori X e Y, percentuale di completamen-to per grafi ci Gantt, e così via. Inoltre fornisce il control-lo su altri attributi come il colore.

La classe Series rappresenta un gruppo di elementi ed espone le proprietà di default per tutti i suoi elementi in-terni, come pure le proprietà specifi che per la serie, come le voci della legenda. Espone dei metodi per manipolare i dati, ad esempio per ordinare gli elementi ed effettuare alcuni calcoli per derivare nuovi elementi della serie.

Per associare una serie al controllo si usa la proprietà SeriesCollection:

Element e = new Element();

e.Name = “My Element”;

e.YValue = 10;

Series s = new Series();

s.Name = “My Series”;

s.Elements.Add(e);

SeriesCollection sc = new SeriesCollection();

sc.Add(s);

chart.SeriesCollection.Add(sc);

Scegliere la giusta libreriaScegliere la giusta libreria può non essere semplice, e in

qualche modo è anche una questione di preferenze per-sonali. Comunque, qualsiasi libreria scegliate deve almeno soddisfare i requisiti basilari, in modo da essere d’aiuto allo sviluppatore per offrire un buon servizio al cliente.

La mia libreria ideale dovrebbe:

• Essere facile da usare• Avere un’interfaccia di programmazione .NET “stan-

dard”, tipo quella della FCL• Fornire una vasta gamma di grafi ci • Essere effi cace in termini di risultati grafi ci, sforzi di

programmazione richiesti e costo• Fornire una ricca interfaccia utente che va oltre la

solita lunga lista di grafi ci da scegliere e aggiunge-re supporto per feature come trasparenza, gradienti, sfondi, legende, ecc.

• Essere internamente ottimizzata in velocità e offrire controllo su certe caratteristiche come il caching

Mi sono dimenticato niente? Ma certo, ovviamente non deve essere complessa e poco maneggevole. Adoro il software potente che permette comunque di iniziare con un semplice ma signifi cativo esempio.

Credo che la libreria .netCHARTING soddisfi tutti que-sti requisiti ([1]).

È eccezionale, per come la vedo io. Tra l’altro è dispo-nibile in versione completa e gratuita per scopi di svilup-pi e testing, quindi potrete tranquillamente sperimentare le tecniche illustrate nel seguito dell’articolo.

Lavorare con .netCHARTINGUna semplice applicazione ASP.NET con .netCHARTING

è, per l’appunto, semplice. Si inizia registrando l’assem-

WEB

Fi gu ra 2 Effetti 3D, background e trasparenza

Fi gu ra 1 Grafico 3D a torta

Page 47: v2004 06 vbj60

47N. 60 - Novembre/Dicembre 2004 VBJ

Il codice prima istanzia un nuovo elemento e poi gli as-segna il valore per l’asse Y. Quindi crea una nuova serie e vi inserisce l’elemento appena creato. Elements elen-ca gli elementi presenti nella collection. Infi ne, la serie è aggiunta in un oggetto SeriesCollection che a sua volta è aggiunto all’omonima proprietà del controllo.

Questo pezzo di codice si trova di solito nell’evento Page_Load.

Anche se è estremamente fl essibile, non è proprio rea-listico creare gli elementi individualmente.

Nella maggior parte dei casi infatti i dati arriveranno dai business object che a loro volta li avranno ricava-ti dal database, documenti Excel, stream XML. I busi-ness object di solito espongono i dati usando contenitori ADO.NET come i DataSet o interfacce come il DataRea-der. .netCHARTING accetta dati provenienti dal DataSet, ma per consistenza il DataSet deve essere impacchetta-to in un oggetto Series.

Ecco un esempio:

void Page_Load(object sender, EventArgs e)

{

// Set global properties

chart.Title = “Item sales report”;

chart.TempDirectory = “temp”;

chart.Type = ChartType.Pie;

// Adding series programatically

chart.Series.Name = “Item sales”;

chart.Series.Data = GetDataSet();

chart.SeriesCollection.Add();

}

La proprietà Data del-la classe Series può es-sere collegata a qualsia-si oggetto enumerabile ADO.NET, inclusi i Da-taReader.

La proprietà TempDi-rectory controlla la di-rectory dove le immagi-ni generate dal controllo sono temporaneamente salvate. Dovrete avere i permessi di scrittura su tale directory nella mac-china server. Ogni imma-gine è salvata in un fi le del server per evitare di penalizzare la memoria.

Per default l’immagine è rigenerata ad ogni hit, a meno che non impostia-

te la proprietà CacheDuration. Quando impostata, l’im-magine è generata solo al primo hit e conservata per un determinato numero di secondi. Tutte le richieste che ar-rivano in questo intervallo di tempo accederanno all’im-magine in cache senza ulteriore elaborazione da parte del controllo. La proprietà booleana UseFile permette di indicare se l’immagine generata debba essere salvata su fi le oppure spedita direttamente sotto forma di stream al browser. Inoltre i fi le obsoleti vengono automaticamente eliminati, senza accumularsi nel tempo.

Un’altra classe che vale la pena di menzionare è la Da-taEngine. Prima di tutto l’oggetto DataEngine può esse-re confi gurato per raccogliere i dati da interrogazioni su database oppure da DataSet disconessi.

I dati sono convertiti in una SeriesCollection che è il solo tipo che il controllo riconosce. In aggiunta, DataEn-gine offre molte comode opzioni di manipolazione, spe-cifi catamente progettate per le esigenze comuni come l’aggregazione dei dati.

DataEngine de = new DataEngine();

de.StartDate = “1/1/02 8:00:00 AM”;

de.EndDate = “1/1/02 8:00:00 AM”;

de.DateGrouping = TimeInterval.Days;

de.SqlStatement = “SELECT names, values FROM myTable “ + “WHERE

start > #StartDate# AND e” + “nd < #EndDate#”;

SeriesCollection sc = de.GetSeries();

chart.SeriesCollection.Add(sc);

Il DataEngine supporta anche il raggruppamento del-le date ad un desiderato livello di granularità – da anni a minuti.

WEB

Fi gu ra 3 Le barre del grafico permettono di fare il “drill-down” sui dati

Page 48: v2004 06 vbj60

48 VBJ N. 60 - Novembre/Dicembre 2004

Dalla versione 2.5 il controllo supporta varie operazio-ni matematiche come la media, il minimo e il massimo. Precedentemente era supportata solo la somma.

Caratteristiche avanzate.netCHARTING possiede varie caratteristiche avanza-

te, In questa sede vorrei brevemente discuterne tre: drill-down, fi le manager e le infi nite opzioni per allineare il te-sto e inserire delle annotazioni (Figura 3).

Il drill-down permette di supportare una catena di gra-fi ci logicamente correlati.

L’idea è quella di visualizzare il grafi co iniziale e poi clic-care su un elemento per ottenere una vista dettagliata dei dati. Il livello di annidamento deve essere impostato dal programmatore e sfrutta le capacità di raggruppamento delle date sopra menzionate.

Ecco un esempio:

Chart.DateGrouping = TimeInterval.Years;

Chart.DrillDownChain=”Years,Quarters,Months,Days,Hours,Minutes”;

Chart.DefaultSeries.DefaultElement.ToolTip=”%yvalue”;

La proprietà DrillDownChain defi nisce i vari livelli di dettaglio che si intendono vedere.

Non è necessario altro codice. Il tooltip visto in Figu-ra 3 visualizza la vista attuale per ogni elemento ogni volta che ci si sposta col mouse sopra di esso.

Tutti i grafi ci generati dal controllo possono essere salvati in un fi le nel server.

Si può fare usando la classe FileManager. Il suo me-todo SaveImage e l’enum ImageFormat permettono di scegliere il nome e il formato dell’immagine. Il co-dice seguente salva il grafico e assegna il suo per-corso alla proprietà ImageUrl di un controllo Image di ASP.NET.

img.ImageUrl = Chart.FileManager.SaveImage(Chart.GetChart

Bitmap());

Nella versione 2.5 la legenda può essere salvata in un fi le separato, per avere un layout fl essibile all’interno del-la pagina web.

leg.ImageUrl = Chart.FileManager.SaveImage(Chart.GetLegend

Bitmap());

Una annotazione è un riquadro con un testo, una posi-zione, una sorgente dati e con funzionalità hotspot. Po-tete associare un’annotazione ad un elemento del gra-fi co e ottenere un effetto come quello in Figura 4. Ave-te il completo controllo sull’orientamento, stile, colore e layout del testo.

I riquadri dei tooltip e delle legende possono essere defi -niti basandosi su un template, come quello in esempio:

chart.LegendBox.Template =”%icon %name”;

Si possono defi nire template utilizzando dei token spe-ciali predefi niti. Ogni token è preceduto dal simbolo %. Nell’esempio viene visualizzata un’icona e il nome del-la voce. L’icona rappresenta un piccolo rettangolo con il colore di riempimento del corrispondente elemento del grafi co. Il nome invece è il nome dell’elemento.

ConclusioniIn .netCHARTING, la lista dei tipi di grafi co supportati

è incredibilmente ricca, ma non infi nita: questo signifi ca che il controllo è ben bilanciato. Non è troppo grande in memoria e non è troppo piccolo per le caratteristiche che ha. Ogni elemento di una serie può essere personalizzato in base a condizioni note a runtime, ad esempio si può cambiare il colore delle fette della torta in base alla per-centuale. Un altro aspetto da enfatizzare è che per usa-re effi cacemente il controllo non bisogna essere esperti di .NET. Alcune caratteristiche come l’aggregazione sul-le date è resa semplice anche per i non esperti. Tenete presente che con quattro linee di codice si può costrui-re un bel grafi co associato ai dati:

chart.TempDirectory=”temp”;

chart.DefaultSeries.ConnectionString = “data.mdb”;

chart.Series.SqlStatement= “SELECT Customer, Sales FROM Table”;

chart.SeriesCollection.Add();

Devo dire che questo articolo ha solo mostrato la pun-ta dell’iceberg .netCHARTING ma credo sia abbastanza per farne capire le potenzialità. A voi la prossima mos-sa. Enjoy!

Riferimenti[1] http://www.dotnetcharting.com/

WEB

Fi gu ra 4 Annotazioni nel grafico

Page 49: v2004 06 vbj60
Page 50: v2004 06 vbj60

50 VBJ N. 60 - Novembre/Dicembre 2004

Applicazioni scalabili in pratica

di Ingo Rammer

ARCHITECT'S CORNER

Nella puntata precedente abbiamo parlato della differen-za tra scalabilità e performance.

Quando parliamo di performance ci riferiamo solitamen-te alla velocità con cui una singola operazione può essere svolta. Invece la scalabilità si riferisce al grado di paralleliz-zazione delle operazioni o di alcune parti dell’applicazione. Quando si ottimizza un sistema si punta ad entrambe, per migliorare il throughput (capacità di elaborazione).

Immaginiamo che un vostro collega abbia ottimizzato un’applicazione talmente bene da gestire 1000 richieste concorrenti su un singolo server.

Dall’altra parte voi avete una soluzione alternativa, che in realtà è un po’ più lenta e può gestire solo 500 richieste concorrenti. Però la vostra soluzione può essere esegui-ta su 10 server in parallelo. Grazie alla possibilità di sca-lare la vostra applicazione può raggiungere un throughput di circa 5000 richieste concorrenti mentre la prima, che a prima vista era più veloce, solo 1000.

Il problema delle architetture di questo tipo è che di soli-to gli sviluppatori sanno benissimo come ottimizzare la ve-locità delle applicazioni, ma raramente sanno concentrar-si altrettanto bene sulla realizzazione di una applicazione veramente scalabile.

Ma cosa si può fare per creare un’applicazione scalabile, che può essere eseguita su più server in parallelo per di-stribuire il carico di lavoro? Nel seguito vedremo tre passi che ci aiuteranno a rispondere alla fatidica domanda.

Ingo Rammer è il fondatore di thinktecture, una com-pagnia che aiuta gli sviluppatori e gli architetti software a progettare e implementare applicazioni e Web service .NET. Partecipa come speaker alle conferenze internazio-nali dedicate a tali argomenti ed è autore del best-seller Advanced .NET Remoting (APress). È Microsoft Regional Director per l’Austria e può essere contattato tramite il sito http://www.thinktecture.com/staff/ingo.

1. Analizzare i datiUno dei modi più semplici per au-

mentare le performance di un’appli-cazione è utilizzare una cache.

Purtroppo le cache sono però il ne-mico della scalabilità.

È semplice mettere in cache più dati possibile fi ntanto che l’applica-zione rimane sullo stesso server, e a volte la velocità migliora anche di un ordine di grandezza.

Però quando entra in scena il se-condo server la situazione cambia radicalmente.

Facciamo un esempio. Supponia-mo che degli ipotetici dati dei clienti siano memorizzati nella cache ser-ver dell’applicazione per 30 minuti dopo ogni accesso.

Ogni volta che un utente modifi ca i dati, prima viene eseguita la tran-sazione sul database e poi viene aggiornata la cache, in modo che le letture successive restituiscano i dati corretti.

Le strategie di caching come que-ste possono in alcuni casi ridurre drasticamente i tempi di risposta e quindi incrementare il throughput.

Ma se ci fosse bisogno di eseguire l’applicazione su una seconda mac-china in un cluster, la cache di una macchina dopo ogni aggiornamen-to non sarebbe corretta (perché solo una cache di una macchina è stata aggiornata).

Senza un’adeguata strategia di ca-che invalidation, applicazioni come

“Two is meglio che one” diceva una nota pubblicità. In questa puntata daremo dei consigli pratici per progettare e implementare un’applicazione scalabile.

Page 51: v2004 06 vbj60

51N. 60 - Novembre/Dicembre 2004 VBJ

La categoria più complessa in termini di stragia di ca-ching è la seconda: infatti ci sono due tipi di dati che cambiano raramente:

• Le informazioni che sono di solito stabili per un lungo periodo di tempo e che sono modifi cate ad una data conosciuta a priori (ad esempio “listino prezzi valido dalla mezzanotte del 1 ottobre 2004“).

Se si conosce la data, queste informazioni possono essere messe in cache facilmente. Bisogna solo assi-curarsi di rimuovere la versione obsoleta al momento giusto.

• Il secondo tipo di dati è quello più diffi cile da tratta-re. Sono i dati che di solito non cambiano, ma quan-do cambiano, le modifi che devono diventare effetti-ve immediatamente.

Per esempio i permessi per gli utenti. Se questi dati sono in cache, bisogna assicurarsi di avere una effi -cace strategia di cache invalidation.

Si può ad esempio pensare di esporre un web service su ogni nodo del cluster per comunicare alla macchi-na esplicitamente di rimuovere alcuni dati dalla me-moria.

Le prossime versioni di .NET e SQL Server avranno delle nuove caratteristiche che permetteranno di notifi -care alle applicazioni server le modifi che critiche ai dati. Ad esempio potrete fare il subscribe a query tipo “select userid, locked_out from users” per fare in modo che SQL Server 2005 automaticamente vi informi quando un re-cord è cambiato.

2. Porsi le giuste domandeL’analisi delle strategie di caching che ho appena illu-

strato è in realtà solo il primo passo di un approccio ge-nerico per progettare un sistema scalabile.

Per assicurare la scalabilità statica e la parallelizzazio-ne dell’applicazione si può essenzialmente lavorare con un semplice modello mentale. Ogni volta che si sta per chiamare un metodo su un oggetto server-side, biso-gnerebbe chiedersi “Cosa potrebbe succedere se que-sto metodo fosse eseguito su una macchina diversa da quella su cui è stato eseguito prima?”.

E una seconda domanda dovrebbe essere “Cosa suc-cederebbe se il server fosse stato riavviato proprio pri-ma di questa chiamata?”.

Per illustrare il problema in dettaglio, diamo un’occhia-ta al codice seguente:

void UpdateCustomer(Customer cust)

{

ISomeRemoteObject obj = (ISomeRemoteObject) Activator.

GetObject(...);

obj.Initialize(_username, _password);

ARCHITECT'S CORNER

queste semplicemente non scalano su più server. Uno dei primi passi per creare un’applicazione scalabile quin-di è quello di analizzare tutti i tipi di dati che sono usa-ti dall’applicazione.

Può essere utile suddividere le tabelle del database in tre gruppi:

• Gruppo 1: dati statici• Gruppo 2: dati semi-statici, che cambiano raramen-

te o che sono aggiornati solo in determinati momen-ti (ad esempio ogni giorno a mezzanotte)

• Gruppo 3: Dati operativi, dinamici

Nel caso di un negozio online, i dati statici sono di so-lito i CAP, le città, la lista dei metodi di pagamenti accet-tati (carta di credito, assegno, bonifi co…). Molto spes-so rientrano in questa categoria i dati che non possono essere cambiati dall’applicazione stessa, ma solo attra-verso strumenti di amministrazione.

Nel secondo gruppo si trovano i dati che di solito non cambiano più di una volta al giorno.

Ad esempio la lista dei prodotti, i prezzi, ecc. Questo gruppo può contenere anche i dati che sono modifi ca-ti in precisi intervalli di tempo, come una volta al gior-no oppure ogni ora.

L’ultimo gruppo contiene i dati su tutte le vere transa-zioni, ovvero il cuore del business.

Questi sono i dati che cambiano più frequentemen-te, come le informazioni sui clienti, i livelli di magazzi-no, gli ordini.

Vedrete che in pratica molte tabelle apparterranno ai primi due gruppi, e solo poche conterranno i dati mo-difi cati dalle transazioni.

Se poi una stessa tabella contiene dati di due grup-pi differenti potete modifi care lo schema del database oppure semplicemente trattare i due tipi di dati diffe-rentemente.

Questo ad esempio succede quando in origine si sono memorizzati nella stessa tabella i livelli di magazzino (che cambiano sempre) insieme alle informazioni sui prodot-ti. Dovrebbero essere separati, almeno nel modello ad oggetti se non nel database, in modo da avere i dati transazionali (livelli di magazzino) separati dalle infor-mazioni sui prodotti.

Seguendo questa classifi cazione, potrete facilmente adottare una adeguata strategia di caching.

La decisione se mettere in cache o no i dati statici è abbastanza semplice: di solito si memorizzano fi n tan-to che c’è RAM disponibile.

Sempre semplice il discorso per i dati operativi del ter-zo gruppo: non bisognerebbe mai metterli in memoria, ma leggerli e scriverli direttamente dallo storage condi-viso, ad esempio il database.

Page 52: v2004 06 vbj60

52 VBJ N. 60 - Novembre/Dicembre 2004

obj.StoreCustomer(cust);

}

e confrontiamolo con questo:

void UpdateCustomer(Customer cust)

{

ISomeRemoteObject obj = (ISomeRemoteObject) Activator.

GetObject(...);

obj.StoreCustomer(_username, _password, cust);

}

Nel primo caso, dovrebbe suonare una sirena d’al-larme. Per assicurare un comportamento corretto in-fatti è essenziale che entrambe le chiamate a Initiali-ze e StoreCustomer siano invocare sulla stesso ogget-to server-side.

Altrimenti il load balancing automatico sessionless sa-rebbe impossibile. E se il server fosse riavviato tra le due chiamate, la seconda fallirebbe.

Avrete notato che in generale le applicazioni che sono state sviluppate con un approccio stateless (senza sta-to), come nel secondo frammento di codice, permetto-no un alto grado di parallellizzazione.

In quel caso infatti ogni richiesta può potenzialmente essere gestita da un server differente in un cluster NLB (network load balanced).

Potreste chiedervi se il secondo frammento è davvero conforme alle pratiche di programmazione orientate agli oggetti: tutto è confi nato ad una chiamata a metodo e l’identità dell’oggetto non conta niente. Avete ragione!

Quando si lavora con oggetti remoti non è una buo-na abitudine continuare ad usare il paradigma orienta-to agli oggetti, meglio invece concentrarsi su ogni chia-mata a sé stante.

I sistemi ad oggetti distribuiti sui quali potreste fare affi -damento per avere l’identità degli oggetti server-side vi le-gherebbero indissolubilmente ad una macchina, limitando la capacità di load balancing e failover automatico.

3. Scovare i colli di bottigliaNelle applicazioni distribuite di solito si trovano due dif-

ferenti colli di bottiglia: quelli locali e quelli globali.Un collo di bottiglia locale interessa solo un server e

può essere di solito eliminato con miglioramenti sulla performance o aggiungendo una macchina al cluster.

Questi tipi di blocchi hanno solitamente una criticità minore in quanto possono essere risolti successivamen-te (cioè quando si riveleranno).

L’identifi cazione dei colli di bottiglia globali invece è molto più importante.

Si verifi cano ogni volta che si accedono risorse condi-vise, come tabelle di database. Rientrano nella categoria il deadlock (il classico killer della scalabilità., vedi [2]), i

normali lock su database e resource contention. Posso-no piantare un sistema in men che non si dica.

Capita spesso che questi colli di bottiglia saltino fuori quando più di un’operazione accede alla stessa risorse contemporaneamente.

Sebbene questi problemi siano molto comuni, ottimiz-zare le strategie di accesso è un compito diffi cile per-ché non ci sono ricette generiche se non quella di man-tenere le transazioni e i lock su database per tempi più brevi possibile.

A questo proposito raccomando di leggere un arti-colo di MSDN [3] che fornisce un’introduzione alle dif-ferenti modalità di locking e transazioni disponibili in SQL Server.

Ricordate che non bisognerebbe basarsi sul taskma-nager o comunque sul livello di carico della CPU per identifi care i colli di bottiglia.

Infatti il workload della CPU può anche restare al mi-nimo, ad esempio nel caso di lock a livello di tabella in-vece che di riga.

La CPU rimane in idle, attendendo il rilascio di un lock e senza la possibilità di effettuare altre operazioni. E ag-giungere CPU al server o macchine al cluster non risol-verebbe la situazione. Uno dei tool più utili per tenere traccia dei lock è SQL Profi ler, che è installato insieme a SQL Server. Permette di vedere la lista tutti i lock per tutti gli utenti attivi in un certo istante.

ConclusioniÈ ovviamente impossibile coprire in un singolo artico-

lo tutti gli aspetti della progettazione e dell’implemen-tazione di applicazioni scalabili.

Ma la mia esperienza insegna che i tre passi che ho spiegato nell’articolo aiutano a migliorare la scalabilità per molte applicazioni.

Per quelli che vogliono approfondire gli argomenti rac-comando il libro [4] e i primi capitoli di [5], che con-tengono informazioni sui fondamenti della elaborazio-ne delle transazioni, condivisione delle risorse e siste-mi scalabili.

Bibliografi a

[1] Ingo Rammer, “La velocità non è tutto“, VB & .NET Journal n° 59

[2] Ingo Rammer, “Accesso ai dati più veloce e scalabi-le“, VB & .NET Journal n° 56

[3] “Locking Hints”, http://msdn.microsoft.com/library/en-us/acdata/ac_8_con_7a_1hf7.asp

[4] Jim Gray and Andreas Reuter, “Transaction Proces-sing: Concepts and Techniques”, Morgan Kaufmann. ISBN 1558601902.

[5] Tim Ewald, “Transactional COM+”, Addison-Wesley, ISBN 0201615940.

ARCHITECT'S CORNER

Page 53: v2004 06 vbj60
Page 54: v2004 06 vbj60

P O S T Aa cura di Alberto [email protected]

54 VBJ N. 60 - Novembre/Dicembre 2004

Gestore globale di eccezioni

Sto lavorando ad un applicativo WindowsForms, scritto parte in VB, parte in C#.

L’applicativo consta di un Main iniziale che lancia una MDI form all’interno della quale si lanciano tutte le varie funzionalità.Ho implementato un sistema di gestione delle eccezio-ni che, in ogni metodo, intercetta l’eccezione, crea un oggetto di eccezione (una nostra classe che eredita da Exception), aggiunge le informazioni necessarie, tra cui la sequenza dei metodi chiamati, e poi rilancia l’eccezione al metodo chiamante (se esiste), oppure scrive un log dell’ec-cezione su file, dando un opportuno messaggio all’utente.Sto osservando un comportamento differente nella gestione delle eccezioni, a seconda che l’eccezione si verifichi eseguendo il codice all’interno dell’ambiente di sviluppo oppure lanciando semplicemente l’eseguibile.Nel primo caso qualunque eccezione riesce a “risalire” fino alla gestione eccezioni presente nel Main. Invece, se si è lanciato semplicemente l’eseguibile, l’eccezio-ne viene intercettata dal gestore di eccezioni di .NET.Credo (ma non ne ho la certezza) che questa dif-ferenza si verifichi quando si ha un’eccezione in un metodo che gestisce direttamente o indirettamen-te un evento (ad esempio il click su un pulsante).A questo punto vorrei capire se le mie supposizioni sono corrette e se è possibile decidere il comportamento del ge-store delle eccezioni intervenendo con qualche impostazione.Sarebbe, infatti, molto comodo poter far risalire tutte le eccezioni al Main.

Giorgio

Come intercetti le eccezioni? In genere nelle applicazioni Win-Forms puoi usare un comodo evento per le eccezioni non gestite, e quindi centralizzare in un unico punto la gestione delle stesse.

Alberto Falossi è formatore e consulente per Code Architects (www.codearchitects.com) su .NET e tecnologie correlate. Si occupa del coordinamento editoriale di VBJ e collabora con numerose riviste di programmazione italiane e straniere. È mem-bro del team di .NET-2-The-Max (www.dotnet2themax.com). Può essere contattato tramite il sito www.albertofalossi.it, dove mantiene anche il blog personale.

Questo metodo è sfruttato anche dall’IDE e comporta le diffe-renze da te notate.

‘ di solito in Form_Load

AddHandler Application.ThreadException, AddressOf UnhandledExcep-

tionProc

Private Sub UnhandledExceptionProc(ByVal sender As Object, ByVal e

As System.Threading.ThreadExceptionEventArgs)

Dim msg As String = StringFormat(“{0}\n\nClick Yes to save current

data\nClick No to exit without saving\nClick Cancel to ignore

error”, e.Exception.Message)

Select Case MessageBox.Show(msg, “Unhandled error”, MessageBox

Buttons.YesNoCancel, MessageBoxIcon.Error)

Case DialogResult.Cancel

Exit Sub

Case DialogResult.No

Application.Exit()

Case DialogResult.Yes

‘ salva i dati dell’applicazione

‘ scrive nel log

Application.Exit()

End Select

End Sub

A.F.

Richiamare un DLL C++ da VB6

Ho scritto una DLL con Visual C++, e sto cercando di uti-lizzarla dall’interno di Visual Basic, ma non viene riconosciuta. Come posso fare?

I compilatori C++ applicano una tecnica nota come name mangling per assegnare nomi differenti a funzioni omonime definite all’interno di classi diverse. Anche se la funzione viene definita globalmente, e quindi esternamente alle classi, questa tecnica viene applicata egualmente, col risultato di ottenere una funzione con un nome diverso da quello effettivamente impostato. Per evitare questo pro-blema occorre utilizzare la direttiva extern “C” davanti al nome della

Page 55: v2004 06 vbj60

55N. 60 - Novembre/Dicembre 2004 VBJ

POSTA

funzione, in modo da indicare al compilatore di considerarla come una funzione C e non C++. Per esempio:

extern “C” __declspec(dllexport) int MyFunc( char *str ) {

}Lorenzo Vandoni

Calcolo del codice fiscale:errata corrige

Volevo segnalarvi un errore nell’algoritmo per la generazione del codice fiscale apparso nel numero 56.

Ho scaricato il sorgente ed ho eseguito il progetto relativo inserendo i miei dati anagrafici: il codice fiscale generato era però più corto di un carattere.

Ho notato che i giorni da 01 a 09 vengono formattati senza lo zero, cosi nella riga 122 della “Function Compute” ho inse-rito un Len(giorno) < 2 per il controllo del giorno di nascita aggiungendo lo zero nella stringa.

Saluti,Hansel

È vero. Effettivamente, nel progetto VB allegato all’articolo di cui parli è presente un piccolo errore che influisce sul calcolo del codice restituito.

L’articolo in questione illustra in dettaglio quale sia la struttura di un codice fiscale ed afferma che i caratteri che vanno dal settimo all’undicesimo riportano informazioni relative alla data di nascita della persona cui si riferisce. Se andiamo a vedere nel codice, troviamo

CF = CF & Right(Anno, 2)

CF = CF & Mesi(CInt(Mese))

If Sesso = “F” Then

CF = CF & CStr(CInt(Giorno) + 40)

Else

CF = CF & Mid(Giorno, 1, 2)

End If

Nel caso in cui il soggetto sia di sesso femminile, si somma 40 al giorno di nascita. In questo modo si può essere assolutamente certi del fatto che verranno concatenati alla variabile CF 2 caratteri. Se, invece, il soggetto è di sesso maschile, non si effettua alcuna operazione sul giorno di nascita, ma lo si accoda al codice fin qui ottenuto.

L’errore è determinato dal fatto che il giorno di nascita può essere composto anche da una sola cifra. Si verifica un’anomalia, quindi, nel caso in cui il soggetto sia di sesso maschile e sia nato in uno dei primi nove giorni di un mese qualsiasi. Esistono molteplici possibilità per risolvere il problema.

La più semplice è manipolare opportunamente la variabile che contiene il giorno di nascita:

Scrivere una pagina ASP .NETcon diversi linguaggi

Nella mia piccola compagnia abbiamo iniziato a sviluppare in ASP.NET. Abbiamo discusso su quale linguaggio usare: alla fine abbiamo preferito VB.NET.

Tra le questioni che sono sorte ve n’è una a cui non siamo stati capaci di rispondere: è possibile realizzare una pagina Web con linguaggi differenti?

Nel senso di mixare linguaggi di scripting? Probabilmente è più una curiosità che una necessità, ma confidiamo in una sua risposta.

Saluti,Federico

Una pagina ASP.NET deve essere scritta in un unico linguaggio di scripting, quello specificato nella direttiva language della pagina. Ad esempio

<%@ Page language=”C#“ %>

Tuttavia non tutti sanno che è possibile realizzare una pagina con linguaggi diversi avvalendosi degli user control. Infatti uno user control può essere scritto in qualsiasi linguaggio ASP.NET, anche differente da quello della pagina che lo ospita.

Quindi basta isolare le diverse parti di codice in user control separati e usare il linguaggio preferito.

Il problema è che questa modalità è supportata dal framework ed è utilizzabile ad esempio quando si scrivono le pagine con notepad, ma non è supportata da Visual Studio, che obbliga a sviluppare uno user control nello stesso linguaggio della pagina e del progetto.

Il discorso vale anche a livello di applicazione: se si usa Visual Studio e quindi il code-behind, tutte le pagine di una Web application devono essere scritte nello stesso linguaggio. Se invece si usa la modalità inline, ogni pagina può essere in un linguaggio differente.

A.F.

If Len(Giorno) = 1 Then

Giorno = “0” & Giorno

End If

Essendo la variabile in questione definita come String, questa correzione garantisce che il codice accodato sarà sempre di 2 ca-ratteri. Si possono, comunque, percorrere anche strade alternative. Per esempio si può operare sull’interfaccia utilizzata dall’utente per inserire i dati e fare sì che l’informazione che arriva alla routine sia sempre di due cifre. Una terza possibilità è rappresentata dall’utiliz-zo della funzione Format nell’istruzione che concatena la variabile Giorno al resto del codice fiscale.

Grazie della segnalazione, il sorgente sul server Infomedia è stato aggiornato.

Lorenzo Braidi

Page 56: v2004 06 vbj60

56 VBJ N. 60 - Novembre/Dicembre 2004

L'accesso ai datinei linguaggiobject-oriented

di Lorenzo Vandoni

SOFTWARE ENGINEERING

Il paradigma object-oriented si è ormai decisamente im-posto come standard de-facto nell’ambito dei linguag-gi di programmazione. L’ultimo caposaldo di resisten-

za relativamente allo sviluppo su PC, Visual Basic 6, ha dovuto cedere il passo alla sua versione object-oriented Visual Basic .NET, ed anche i linguaggi tipicamente utiliz-zati in ambienti mini e mainframe, come Cobol, sono stati modifi cati per permettere la gestione di classi ed oggetti. A difendere gli estimatori della programmazione struttura-ta rimane ora solo il C.

Il paradigma object-oriented, però, non è riuscito a impor-si ovunque. I database object-oriented (ODBMS), in parti-colare, sono rimasti relegati a campi di applicazione mol-to particolari, e non di rado si è sentito utilizzare il termi-ne “fallimento” nei loro riguardi. Il modello dei dati tuttora più diffuso rimane quello relazionale, introdotto ormai più di 30 anni fa.

Il problema dell’accesso ai datiTutte le applicazioni sviluppate con linguaggi object-orien-

ted, nel caso in cui abbiano la necessità di accedere a dati permanenti gestiti da un DBMS, dovranno quindi affron-tare un problema concettuale, noto come impedance mi-smatch, dato dal fatto che gli stessi oggetti dovranno es-sere rappresentati in modi diversi in memoria, dove si potrà sfruttare la ricchezza dei costrutti offerti dal linguaggio di programmazione, e su disco, dove si dovranno fare i conti

Lorenzo Vandoni è laureato in Informatica, ed è uno specialista di progettazione e sviluppo con tecniche e linguaggi object-oriented. Ha collaborato alla realizzazione di framework, strumenti di sviluppo e software commerciali in C++, Java e Visual Basic. Può essere contattato tramite e-mail all’indirizzo [email protected].

con la relativa povertà semantica del modello relazionale.

Tra gli esempi che vengono spes-so citati per spiegare tale problema vi è quello relativo ad oggetti com-plessi, come il disegno tridimensio-nale di un’automobile all’interno di un programma CAD. Utilizzando un lin-guaggio di programmazione object-oriented potremo facilmente defi nire una classe Automobile, dotata di me-todi complessi per la sua gestione, per permettere ad esempio di ruota-re l’immagine, capovolgerla o stam-parla, e potremo sfruttare l’eredita-rietà per incapsulare in una classe base Oggetto3D tutte quelle opera-zioni che possono essere facilmen-te estese ad altri tipi di grafi ci tridi-mensionali.

Nel caso in cui molti oggetti di que-sto tipo dovessero essere memoriz-zati su un DBMS, e se si volessero permettere interrogazioni del tipo “ri-cerca tutte le auto con cambio auto-matico”, ci troveremmo nella neces-sità di scrivere un’algoritmo per tra-durre la rappresentazione in memo-ria dell’oggetto in un insieme di righe memorizzate in molte tabelle diverse. In più, sarà necessario scrivere l’al-goritmo inverso, e tradurre in sintas-si SQL un’interrogazione come quel-la precedentemente citata.

Tutti i linguaggi object-oriented devono fare i conti con la necessità di gestire dati in formato relazionale

Page 57: v2004 06 vbj60

57N. 60 - Novembre/Dicembre 2004 VBJ

bile caricandone i dati dal DBMS (una soluzione alterna-

tiva, pure corretta, potrebbe essere quella di trasformar-

li in costruttori).

Possiamo tralasciare in questa sede i problemi tutt’altro

che banali legati alla necessità di disporre di un object

identifi er e di un formato opportuno per la rappresenta-

zione delle interrogazioni, semplicisticamente costituite,

nell’esempio, da un parametro di tipo stringa. Tralascia-

mo anche una possibile discussione sulla defi nizione di

interfacce o classi base che permettano di meglio forma-

lizzare questo tipo di soluzione, e concentriamoci invece

sui possibili vantaggi e svantaggi che questa può offrire,

in relazione al problema precedentemente esposto.

Il principale vantaggio è dato da un corretto incapsula-

mento del problema. I dettagli relativi alla traduzione dei

dati dell’oggetto tra le due rappresentazioni vengono co-

difi cati all’interno dei metodi identifi cati, lasciando il resto

dell’applicazione all’oscuro del problema. Questo signifi -

ca, tra l’altro, che il tipo di rappresentazione dei dati all’in-

terno del DBMS potrà essere modifi cato senza che l’ap-

plicazione che utilizza la classe ne risenta.

Questa soluzione viene citata all’interno di molti testi, e

in alcuni “vecchi” libri scritti con l’obiettivo di spiegare ai

programmatori Visual Basic 6 perché mai potesse essere

necessario creare delle classi, invece di limitarsi come in

passato ad usare form e funzioni, questo veniva addirit-

tura indicato come il motivo principale. Si tratta senz’al-

tro di una soluzione corretta, ma presenta un paio di pro-

blemi non banali.

Il primo è dato dalla proliferazione di classi del tutto si-

mili tra loro. Il caso della classe Automobile, ovvero di

una classe di oggetti complessi con problemi di Impe-

dance Mismatch non banali, è di fatto piuttosto raro. Mol-

to più spesso, nelle applicazioni gestionali, si utilizzano

oggetti come Cliente, Fornitore o Fattura, che anche al-

l’interno del programma possono essere visti come sem-

plici aggregati di campi, rappresentabili, a seconda del

linguaggio utilizzato, con un type o una struttura. Lavo-

rando con oggetti di questo tipo, l’implementazione delle

classi di livello intermedio diventerebbe banale e ripetiti-

va, nonché soggetta ad errori e causa di inutili rallenta-

menti nel codice.

Il secondo problema è dato dalla necessità di accedere

dall’interno del codice ai dati corrispondenti ai campi della

tabella. Questa necessità porta alla creazione di un insie-

me di metodi Get e Set per l’accesso ai dati, in corrispon-

denza ad ognuno dei campi. Oltre ad enfatizzare i rischi

di ripetitività e rallentamenti precedentemente riscontrati,

questi metodi introducono anche problemi di manutenzio-

ne, in quanto ad ogni modifi ca della struttura del database

dovrà corrispondere una modifi ca del codice della classe.

Questo tipo di soluzione, quindi, dimostra di essere adat-

ta per la gestione di oggetti complicati, ma eccessiva e

ridondante per il caso di oggetti più semplici.

SOFTWARE ENGINEERING

Il problema dell’impedance mismatch è quindi costituito dalla necessità di fornire algoritmi di traduzione per con-vertire la rappresentazione di uno stesso oggetto tra due formati differenti. La soluzione più naturale per questo problema sarebbe quella di utilizzare un unico formato di rappresentazione, come proposto dai sostenitori de-gli ODBMS, ma, come già visto, questi sistemi non han-no avuto una suffi ciente diffusione.

Uno strato di software per l’accesso ai datiPreso atto della necessità di scrivere uno strato di sof-

tware intermedio (o middleware) che si preoccupi di con-

vertire i dati da una rappresentazione object-oriented al

modello relazionale, e viceversa, occorre trovare il modo

migliore per farlo.

Le soluzioni possibili sono sostanzialmente due: la prima

prende atto della differenza esistente tra i due modelli, e

prevede l’introduzione all’interno delle classi dei metodi

necessari per espletare tutte le operazioni necessarie per

il reperimento, il salvataggio e la ricerca dei dati, mentre

la seconda tende a considerare il tipo tabella come un

particolare tipo del linguaggio, dotato di caratteristiche e

limitazioni particolari, e permette di creare uno strato di

software più generico e facilmente riusabile.

Accesso object-oriented ai datiIl primo tipo di soluzione prevede l’aggiunta all’interno

delle classi che costituiscono la rappresentazione object-oriented del sistema, dei metodi necessari per gestire gli oggetti di quella classe all’interno del DBMS.

Tornando all’esempio della classe Automobile, signifi -ca che dovrà essere creata una classe del tipo (in sin-tassi VB.NET):

Class Automobile

‘metodi specifici dell’oggetto

Public Sub Ruota(nGradi As Integer)

...

Public Sub Capovolgi()

...

Public Sub Stampa()

...

‘metodi per la gestione del DBMS

Public Sub Salva()

...

Public Shared Sub Leggi(nObjId As Long) As Automobile

...

Public Shared Sub Trova(sCondition As String) As Automobile

...

End Class

I due metodi Leggi e Trova devono essere statici (o

Shared, secondo la sintassi utilizzata in VB.NET), per-

ché si preoccupano di creare un nuovo oggetto Automo-

Page 58: v2004 06 vbj60

58 VBJ N. 60 - Novembre/Dicembre 2004

Classi generiche di accesso ai datiIl secondo approccio prevede la creazione di un’unica

classe Tabella, che possa essere genericamente utilizza-

ta per accedere al contenuto di qualsiasi tabella sul data-

base. Questo approccio è quello adottato dalla maggior

parte delle librerie più diffuse. Un recordset ADO o un da-

taset ADO.NET, nonostante siano notevolmente differenti

tra loro, possono entrambi essere considerati in questa

categoria. In questo tipo di soluzione, la classe Tabella (o

Recordset, o Dataset) è concettualmente assimilabile alla

rappresentazione in memoria di una tabella relazionale. I

dati mantenuti al suo interno sono raggruppati in colonne,

il cui tipo può essere scelto solo all’interno di un insieme

di tipi predefi niti, e le operazioni consentite sono quelle di

inserimento, modifi ca e cancellazione di righe.

I vantaggi e gli svantaggi di questo approccio sono spe-

culari rispetto alla soluzione prospettata nel paragrafo pre-

cedente. Non è necessario implementare alcuna classe

aggiuntiva, in quanto per ogni tabella di database verrà

semplicemente creata una nuova istanza della classe Ta-

bella, e non si potranno riscontrare quindi problemi legati

a possibili errori di codifi ca, rallentamenti e necessità di

manutenzione. D’altra parte, questo tipo di approccio non

risolve il problema dell’impedance mismatch, perché non

permette di creare una classe Automobile che mascheri

il modo in cui i vari componenti, come volante, motore e

ruote, sono rappresentati all’interno del database. La lo-

gica applicativa dovrà essere implementata in una clas-

se a parte, che utilizzerà una o più istanze della classe

Tabella per reperire i dati utili a costruire una rappresen-

tazione in memoria dell’oggetto.

In medio stat virtusL’approccio più conveniente da adottare consiste nel-

l’applicare contemporaneamente le due tecniche prece-denti, comportandosi in modo diverso a seconda del livel-lo di complessità dell’oggetto rappresentato. Per oggetti strutturalmente semplici, come Cliente, Fornitore e Fattu-ra, potrà essere suffi ciente istanziare una classe Tabella di tipo generico, mentre per oggetti più complessi, come Automobile, potrà essere conveniente creare una classe specifi ca che mascheri la struttura del database.

Con l’avvento di .NET, questo approccio diventa parti-colarmente conveniente, grazie all’introduzione di mec-canismi come ereditarietà e polimorfi smo. In pratica, di-venta possibile fare in modo che la propria classe Auto-mobile venga creata come sottoclasse della classe più generica Tabella, ereditandone in questo modo tutte le funzionalità di base, come l’accesso ai valori dei cam-pi elementari.

In questo modo, la classe potrà essere facilmente do-tata di funzionalità supplementari, e si potrà anche fare in modo di sovrascrivere il comportamento di alcuni me-todi. Per esempio, il metodo Salva, o Update, invece di

limitarsi a salvare i dati delle varie colonne, registrerà in modo opportuno anche i dati delle tabelle collegate, pre-levandoli da strutture dati mantenute in memoria. Analo-gamente, il metodo Leggi non si limiterà ad eseguire una sola interrogazione, ma cercherà di reperire, utilizzando diverse query, tutte le informazioni necessarie per crea-re l’oggetto Automobile in memoria.

Estendere ADO.NETLa struttura gerarchica delle classi di ADO.NET, con la

separazione di responsabilità fra managed provider (Da-taReader, DataAdapter) e classi dedicate al mantenimen-to dei dati in memoria (DataTable, DataSet) rende l’im-plementazione delle idee precedentemente esposte un po’ più complicata. L’idea di base, infatti, era quella di creare una classe Automobile come estensione di una generica classe Tabella, e di inserire all’interno di que-sta classe tutta la logica relativa sia alla manipolazione dei dati (Ruota, Capovolgi, Stampa), sia quella relativa al salvataggio e al reperimento dei dati dal database (Sal-va, Leggi, Trova). La separazione di responsabilità tra le classi base ci costringe ad adottare una soluzione alter-nativa. Le possibilità sono diverse:

• defi nire la classe Automobile come estensione di Da-

taset, aggiungendo al suo interno anche un riferimen-

to ad un DataAdapter, in modo da poter implementa-

re la logica relativa ai metodi Salva, Leggi e Trova;

• creare due classi separate, AutomobileDataSet e Au-

tomobileDataAdapter, mantenendo anche nelle clas-

si derivate la stessa distinzione esistente nelle classi

base;

• creare una nostra classe base Tabella, che incapsu-

li al suo interno un DataSet e un DataAdapter, e de-

fi nire la classe Automobile come estensione di que-

st’ultima.

Per ora, limitiamoci a queste considerazioni. Torneremo su queste possibilità in un prossimo articolo, in cui pre-senterò più in dettaglio una possibile soluzione.

ConclusioniIl framework che è stato tratteggiato nei paragrafi prece-

denti non tiene conto della possibilità di sfruttare l’eredi-tarietà multipla. L’unico linguaggio che permette l’utilizzo dell’ereditarietà multipla, tra quelli più diffusi, è C++, per cui ho preferito non considerare questa ipotesi.

La creazione di un insieme di classi che forniscano una gestione completa dei “business object” utilizzati dall’ap-plicazione può essere molto utile per la suddivisione del programma in più strati, o tier, e può semplifi care il riuso della logica applicativa in applicazioni diverse.

SOFTWARE ENGINEERING

Page 59: v2004 06 vbj60
Page 60: v2004 06 vbj60

VBJ 60

IN VETRINA VBJ 60

Managing Securitywith Snort & IDS Tools

Kerry J. Cox,Christopher Gerg

288 pp. euro 39,95ISBN 0596006616

CSS Cookbook

Christopher Schmitt

270 pp. euro 34,95ISBN 0596005768

Head First Servletsand JSP

Bryan Basham,Kathy Sierra, Bert Bates

886 pp. euro 44,95ISBN 0596005407

Linux Shell Scripting with Bash

Ken O Burtch

432 pp. euro 36,95ISBN 0672326426

Mobile Applications.Architecture, Design, and Development

Valentino Lee, Heather Schneider, Robbie Schell

368 pp, euro 46.95ISBN 0131172638

Microsoft Office 2003 for Healthcare

Ahmad Hashem

608 pp. euro 31,95ISBN 0789732114

Scrivi a

[email protected]

specificando

nell’oggetto della

e-mail:

IN VETRINAVBJ n. 60

OPPURE

inviaci il coupon

sottostante

al numero di fax

0587/732232

Potrai acquistare

i libri qui riportati con uno

SCONTOECCEZIONALE

del 10% anche se

acquisti solo un libroOPPURE

del 20% se acquisti

3 libri

Page 61: v2004 06 vbj60

L I B R I

61N. 60 - Novembre/Dicembre 2004 VBJ

Questo libro non è stato scritto per i novizi, ma è rivolto ad una utenza professionale, così come testimoniato dal titolo stesso. La promessa è quella di trarre utilità dall’apprendimento dei

segreti di questa ultima versione di Acrobat e la ragione per cui gli autori dicono di aver scritto il testo (e per cui bisognerebbe leggerlo!) è sottolineata da loro stessi: raggiungere l’obiettivo di lavorare di meno facendo lavorare di più le macchine. Un intento del genere, che ha un che di filosofico, viene perseguito illustrando i molti vantaggi di Acrobat 6: già da un veloce sguardo al primo capitolo si intuisce come le differenze della celeberrima suite rispetto alla versione precedente siano sostanziali. Tra le molte funzionalità illustrate impossibile non ricordare il Binder, un potentissimo tool che consente di combinare in un unico file PDF differenti formati di file. Nei primi capitoli si familiarizza con l’interfaccia (sotanzialmente nuova) e si impara a creare e manipolare file PDF. Una volta capito come ottenere il proprio file PDF si entra nello specifico: editing avanzato e strumenti per la “collaboration” vengono descritti nei dettagli. Ormai non si può prescindere dalla sicurezza ed il libro non fa eccezione, illustrando come proteggere da alterazioni i propri documenti e come assicurarne e verificarne l’integrità e la genuinità. Leggendo il libro si ha l’impressione che nulla venga tralasciato: un intero capitolo dedicato alla creazione di forms, ad esempio, non lascia al lettore alcun dubbio riguardo al loro utilizzo. Anche le funzionalità classiche, quali l’indicizzazione e la ricerca, la preparazione dell’output e la stampa vengono opportunamente descritte con grossa attenzione alla accessibilità dei documenti creati. Le sorprese più positive arrivano dalla lettura degli ultimi capitoli: un “All about ebooks” che non tradisce le aspettative del titolo e un capitolo che descrive funzionalità avanzate. Scorgendo l’indice potrebbe soprendere un capitolo su JavaScript, ma leg-gendolo si apprende come questo linguaggio estenda in modo sostanziale le potenzialità della Suite che pure non è storicamente indirizzata a chi scrive codice. Utilissimo, infine, l’ultimo capitolo che permette di mettere insieme in maniera pragmatica molti dei concetti appresi durante la lettura mediante la realizzazione di un progetto di creazione di una knowledgebase.

ProUn testo utilissimo per diverse categorie di professionisti: graphic/web

designer, architetti, ingegneri ed in generale utenza di tipo business.

Contro Il testo si rivolge ad un utenza di tipo professionale risultando di

conseguenza non molto indicato per un pubblico che si avvicina alla materia per la prima volta.

Massimiliano Mariconda

Adobe Acrobat6 The Professional User’s Guide Autore D. L. Baker, T. Carson Editore Apress ISBN 1-59059-232-8 Lingua Inglese Anno 2004 Pagine 460 Prezzo € 42,75

Visual Studio .NET è un ottimo ambiente di lavoro integrato per programmatori .NET, sia che usino VB.NET o C#. Anche nello strumento migliore però qualcuno troverà immancabilmente qual-

che mancanza, e VS.NET non si sottrae certo a questa regola. Tramite il modello ad oggetti esposto un programmatore può però estendere l’IDE con nuovi comandi e finestre. Questo libro si propone appunto come guida allo sviluppo di add-in e macro per VS.NET. Si comincia da un’introduzione alle varie parti dell’IDE, per passare velocemente all’uso dell’Add-in wizard che genera automaticamente uno scheletro per un nuovo add-in, a tecniche (semplici a dire il vero) di debug-ging, manipolazione di codice, controlli dell’interfaccia utente (solo di Windows Forms però, non si nomina neanche ASP.NET), progetti e soluzioni. Un capitolo è dedicato anche alle macro. Gli argomenti trattati sono parecchi, ma nessuno veramente in dettaglio purtroppo. Moltissime domande che ci si ritrova ad affrontare non appena si comincia un progetto reale restano senza risposta, dal momento che il codice di esempio presentato non è certo molto completo o professionale. La presenza di alcuni errori nel codice poi non aiuta di certo. Sempre riguardo il codice, sono stampati listati anche di quin-dici pagine dove ne sarebbe bastata una per il codice interessante e relativo all’argomento specifico del capitolo in questione. Inoltre, nonostante in copertina si citi sia VB.NET che C#, a C# sono dedicate solo un paio di pagine, e il codice VB.NET sembra in realtà VB6, dato l’uso estensivo di funzioni come MsgBox, Len, Mid, Left, Right ecc. Addirittura l’addin presentato come progetto finale genera codice in stile VB6! Un capitolo che invece ho apprezzato più degli altri è “How do I…”, che presenta una lista di Q&A con frammenti di codici pronti da usare (sebbene qualche ritocco sia di solito necessario…), perché va direttamente al punto e riesce a dare effettivamente un aiuto al programmatore in cerca di risposte a problemi tipici. In definitiva, il libro può essere un’utile introduzione per farsi un’idea di quelle che sono le possibilità di estensione di VS.NET, per chi ha già sviluppato addin in VB6 e a maggior ragione per chi comincia da zero, ma non può essere considerato come la guida definitiva sull’argomento.

ProLa sezione “How do I…?” presenta Q&A e pezzi di codice

che possono tornare utili, con le opportune modifiche, in molte situazioni.

ControListati troppo lunghi e dispersivi, a volte copiati più volte solo per cambiare una singola riga. Gli argomenti sono trattati troppo superficialmente, e gli esempi sono troppo semplici per essere usati come base per progetti di qualità commerciale..

Marco Bellinaso

Writing Add-ins for Visual Studio .NET Autore Les Smith Editore Apress ISBN 1-59059-026-0 Lingua Inglese Anno 2002 Pagine 520 Prezzo € 53,45

Page 62: v2004 06 vbj60

a cura di Davide Mauri [email protected]

.NET TO OLS

62 VBJ N. 60 - Novembre/Dicembre 2004

Reflector

Credo che sicuramente abbiate usato tutti, chi per semplice curiosità chi per necessità professionali, il tool ILDASM.exe, ossia il disassembler fornito da Microsoft nel .NET Framework SDK, che permette di visualizzare il codice MSIL generato dal compilatore dal nostro codice sorgente e vedere ciò che viene davvero dato in pasto al CLR. Lutz Roeder deve aver pensato che, se un com-pilatore può generare MSIL deve essere vero anche il contrario, e quindi passare da questo a C#, VB.NET, e così via. Detto, fatto! Nell’ottobre del 2000 nasce la prima versione di questo ormai popolarissimo tool, che permette a chiunque di sbirciare all’interno di un assembly .NET (quindi in qualsiasi programma basato sul framework), di disassemblarlo e di ricostruirne il codice sorgente (Figura 1). Il codice sorgente così ottenuto ovviamente non è identico all’originale (tranne per i casi più semplici), ma è funzionalmente equivalente, ossia produce lo stesso risultato. La cosa interessante è che Refl ector permette di decompilare in quattro diversi linguaggi: MSIL, C#, VB.NET e Delphi.NET. Un esempio pratico è visibile tramite il Listato 1 ed il Listato 2; il primo è il codice originale scritto in C#, il secondo è il risultato della decompilazione utilizzando come destinazione VB.NET. Proprio tramite i due listati credo si possa capire l’enorme utilità di Re-fl ector: grazie a questo è possibile scoprire quello che accade davvero durante l’esecuzione di un’applicazione… che, purtroppo, non sempre è quello che ci aspettiamo o quello che è documentato.

Nel caso preso in esame è possibile vedere come dichiarazione, creazione ed inizializzazione del campo statico someStrings siano convertite in realtà (come giustamente ci si aspettava) in una dichiarazione pura separata dalla creazione e dall’inizializzazione che invece vengono eseguite nel costruttore statico, creato in modo automatico dal compilatore. Interessante, vero? Beh, direi che curiosi ed assetati di conoscenza sono serviti! Se le funzionalità fi n qui descritte ancora non vi bastano, niente paura! Refl ector supporta l’aggiunta di funzionalità tramite plugin. Già diversi se ne trovano in giro, e tra i più interessanti segnalo questi:

� Refl ector.VisualStudio: come lascia intendere il nome per-mette di integrare Refl ector all’interno di Visual Studio, e rimpiazzare defi nitivamente il class browser nativo;

� Refl ector.McppLanguage: per ottenere un decompilato scritto in C++;

Fi gu ra 1 Reflector in azione su un’applicazione di test

Davide Mauri è un consulente freelance che si occupa di SQL Server 2000 e di sviluppo di soluzioni Web basate sul .NET Framework. All’attività di consulenza affianca una co-spicua attività di docenza e di formazione presso Mondadori Informatica. Il suo sito personale è www.davidemauri.it.

Li sta to 1 Il codice originale C# di una semplice

applicazione di esempio

using System;

namespace SampleApp{ class SampleClass { static string[] someStrings = new string[10] { “a”, “b”, “c”, “d”, “e”, “f”, “g”, “h”, “i”, “l”}; [STAThread] static void Main(string[] args) { foreach(string s in someStrings) { Console.WriteLine(s); } } }}

Page 63: v2004 06 vbj60

63

.NET TOOLS

N. 60 - Novembre/Dicembre 2004 VBJ

� Refl ector.FileDisassembler: per salvare il codice pro-dotto da Refl ector su fi le in modo automatico;

� Refl ector.OpenRunningAssembly: per caricare in Re-fl ector un assembly in esecuzione;

� Refl ector.Graph: per visualizzare grafi camente collega-menti e dipendenze del nostro codice. Spettacolare!

Potete scaricarli al terzo indirizzo nella tabella sottostante. Creare un proprio plugin è relativamente semplice, e su GotDotNet si trova tutto il necessario: nel caso vi servisse qualcosa di particolare potete svilupparne uno in auto-nomia, e ovviamente non mancate di segnalarmelo! Per concludere il tool è veramente molto semplice da usare, e, oltre all’utilità intrinseca di un’applicazione di questo tipo, tutta una serie di tocchi di classe, come la ricerca integrata su Google o su MSDN dei metodi e delle pro-prietà dell’assembly in analisi, oppure l’integrazione con la documentazione prodotta in formato XML, ne hanno fatto un tool indispensabile per molti sviluppatori.

The Regulator

Se dovessi scrivere in una sola riga che cos’è The Regu-lator, mi basterebbe dire “È il Visual Studio delle Regular Expression”. Per fortuna lo spazio non manca, ma penso che quanto appena detto dia immediatamente un’idea della potenza e della qualità del tool di cui stiamo parlando. Come avrete già potuto intuire The Regulator è un editor di Regu-

lar Expression, oggettini simpatici, estremamente potenti (in alcuni casi indispensabili) ma parecchio complessi da scrivere. Vi ricordate? Ne avevamo già parlato in un numero precedente di .NET Tools, [1]. Perché questo tool è così bello? Beh, i motivi sono tanti ma probabilmente il principale è l’integrazione con il sito RegExLib.com, tramite il quale è possibile cercare, direttamente dall’interno dell’editor, le regular expression che ci servono. Dovete validare un indirizzo e-mail? Cercate “email”. Vi serve una RegEx per validare un numero di telefono nel formato italiano? Cer-cate “italian”. Nel caso in cui invece la regular expression debba essere scritta ex-novo, avete a disposizione syntax highlighting ed intellisense, più la possibilità di comporre l’espressione costruendola tramite un menu contestuale. Una volta in possesso della regular expression desiderata, questa può essere provata su un testo di esempio, in modo da verifi carne la correttezza. A questo punto è poi possibile generare il codice C# o VB.NET da inserire nella nostra soluzione per applicare l’espressione, oppure un’assem-bly da referenziare nel progetto, già compilato. Un’ultima feature non meno interessante è il “RegEx Analyzer”, che, leggendo la regular expression digitata, descrive, in inglese, il comportamento della stessa. Ad esempio, questa porzione di espressione:

(?<Hour>\d{2})

viene descritta come:

Capture to <Hour> Any Digit Exactly 2 times

utile, soprattutto se siete agli inizi, per decifrare le criptiche espressioni. Come ultima nota, e come ciliegina sulla torta, viene anche fornito un help che spiega tutte le funzionalità del programma e che contiene anche diversi articoli (alcuni

ProdottoRefl ector

Url di riferimento http://www.aisto.com/roeder/dotnet/http://www.gotdotnet.com/workspaces/workspace.aspx

?id=0f5746c3-c7aa-4879-8043-e0f4fc233adehttp://www.freewebs.com/csharp/Refl ector/Addins/

Sta to Release4.1.3.0

Semplicità d’uso �����Semplicissimo! Basta caricare l’assembly desiderato ed è fatta.

Utilità �����Non è un tool che utilizzerete tutti i giorni...ma se non ci fosse bisognerebbe inventarlo!

Qualità prodotto ����� Nulla da dire. Ottima, soprattutto per il supporto di plug-in esterni.

Qualità documentazione �����Di documentazione praticamente non ce n’é, ma non se ne sente troppo il bisogno, visto che è tutto molto sem-plice. Molto buona la documentazione e gli esempi per la scrittura di plugin.

Fi gu ra 2 Il completo IDE di The Regulator

Page 64: v2004 06 vbj60

64

.NET TOOLS

VBJ N. 60 - Novembre/Dicembre 2004

provenienti direttamente dal sito MSDN) che mostrano dei possibili utilizzi (pratici) delle regular expression. Un tool che non può mancare alla vostra collezione!

UnleashItUnleashIt è un piccolo tool, dalla quale gli sviluppatori di

applicazioni web diffi cilmente si separeranno dopo averlo usato. Il suo scopo è quello di semplifi care ed automatizzare al massimo la fase di deploy della soluzione. Se infatti non si utilizzano le FrontPage extensions, in pratica il deploy di un’applicazione web deve essere fatto a mano, a meno di non voler creare un’applicazione di setup; cosa non sempre possibile, soprattutto se volete solamente trasferire i fi le sul server della società che vi fornisce l’hosting. In questo caso di solito si utilizzano i soliti metodi di copia via FTP o, se pos-sibile, via VPN su una cartella condivisa. L’inconveniente di tale soluzione è generalmente dovuto alle dimensioni dell’ap-plicazione stessa. Se è abbastanza grossa può essere facile dimenticarsi qualche fi le durante l’upload. Con UnleashIt (che prima di giungere alla versione 2.0 si chiamava WebDeploy) tutto questo non sarà più un problema. Una volta caricato, l’interfaccia grafi ca, visibile in Figura 3, permette di scegliere la directory nella quale sono presenti i fi le sorgenti oppure, se preferiamo, il fi le del progetto generato da Visual Studio. Tramite quest’ultima opzione UnleashIt selezionerà in auto-matico tutti i fi le presenti nel progetto. Nel caso si vogliano personalizzare i fi le da trasferire, è possibile utilizzare le voci visibili sulla destra, in modo da includere od escludere fi le e/o directory a seconda delle proprie necessità. Fatto ciò è poi necessario specifi care il percorso di destinazione, che può essere un percorso di rete, un indirizzo ftp oppure un fi le .zip, cliccare sul pulsante “Deploy” ed il gioco è fatto. Tra le feature particolarmente interessanti da segnalare troviamo:

� Gestione dei profi li È possibile memorizzare la confi gurazione di deploy-

ment per un progetto in modo da poter effettuare il rilascio dello stesso con un solo click.

� Multi Profi le Deployment È possibile distribuire i fi le su più profi li contemporanea-

mente. Utile quando si rilasciano applicazioni ospitate su più di un web server.

� Integrazione con Visual Studio Cliccando con il tasto destro sulla cartella del progetto,

è disponibile una nuova icona che attiva il deployment di UnleashIt.

Oltre a tutte queste peculiarità è possibile anche ef-fettuare il backup dei fi le di cui si sta facendo il deploy-ment, attivare il logging di tutte le operazioni eseguite, e, per una massima personalizzazione, anche defi nire

ProdottoThe Regulator

Url di riferimento http://royo.is-a-geek.com/iserializable/regulator/

Sta to Release2.0.3.0

Semplicità d’uso �����Semplifi ca enormemente sviluppo e debug di regular ex-pression. Fantastica la possibilità di effettuare ricerche direttamente sul RegexLib.com

Utilità �����Dato che le regular expression sono utili dappertutto, questo lo è di conseguenza.

Qualità prodotto ����� Ottima. Non a caso è stato messo tra i top-ten tools da avere assolutamente.

Qualità documentazione �����Ottima, con tanto di help in linea.

Li sta to 2 Il codice VB.NET generato da Reflector

tramite decompilazione

Imports System

Namespace SampleApp Friend Class SampleClass ‘ Methods Private Shared Sub New() Dim textArray1 As String() = New String(10) {} textArray1(0) = “a” textArray1(1) = “b” textArray1(2) = “c” textArray1(3) = “d” textArray1(4) = “e” textArray1(5) = “f” textArray1(6) = “g” textArray1(7) = “h” textArray1(8) = “i” textArray1(9) = “l” SampleClass.someStrings = textArray1 End Sub

Public Sub New() End Sub

<STAThread> _ Private Shared Sub Main(ByVal args As String()) Dim textArray1 As String() =

SampleClass.someStrings Dim num1 As Integer = 0 Do While (num1 < textArray1.Length) Dim text1 As String = textArray1(num1) Console.WriteLine(text1) ++num1 Loop End Sub

‘ Fields Private Shared someStrings As String() End ClassEnd Namespace

Page 65: v2004 06 vbj60

.NET TOOLS

dei comandi che devono essere eseguiti prima e dopo l’operazione di rilascio.

Riferimenti

[1] Rubrica .NET Tools, Visual Basic & .NET Journal n° 55

Fi gu ra 3 La semplice – ma completa – interfaccia

utente di UnleashIt

ProdottoUnleashIt

Url di riferimento http://www.eworldui.net/UnleashIt/

Sta to Release2.0

Semplicità d’uso �����Più semplice di così è impossibile.

Utilità �����Ormai non posso più farne a meno. Fantastica l’integra-zione con Visual Studio!

Qualità prodotto ����� Ottima.

Qualità documentazione �����

Tutta contestuale, molto semplice e molto pratica.

Page 66: v2004 06 vbj60

&

V. Valdera P., 116 - 56038 Ponsacco (Pi) - Tel. 0587/736460 - Fax 0587/732232P.Iva 01474440508 - [email protected] - www.infomedia.it

Arretrati Numero rivista Prezzo cadauno Totale

� CP ____________________ € 10,00 € _________

� DEV ___________________ € 10,00 € _________

� Login _________________ € 14,00 € _________

� VBJ ____________________ € 14,00 € _________

TOTALE € _________

+ Spese spedizione

(€ 2,50 per 1 rivista,€ 4.00 per 2 o più riviste) € _________

TOTALE € _________

INDIRIZZO DI SPEDIZIONE

Nome ___________________________ Cognome ___________________________ Ditta _____________________________

Via ________________________________ C.A.P. _______________ Città _____________________________ Prov. ________

Tel. ____________________________ Fax ____________________________ E-mail _________________________________

� I prezzi degli abbonamenti per l’estero sono maggiorati di spese di spedizione.

Garanzia di riservatezza - Il Gruppo Editoriale Infomedia garantisce la massima riservatezza dei dati da lei forniti e la possibilità di richiederne gratuitamente la rettifica o la cancellazione scrivendo a: Responsabile Dati - Gruppo Editoriale Infomedia Srl - Via Valdera P. 116 - 56038 Ponsacco (PI). Le informazioni custodite nel nostro archivio elettronico verranno trattate in conformità alla legge 675/96 sulla tutela dati personali.

TIPO DI PAGAMENTO

� Contrassegno (+ € 7,00 per spese postali)� Allego fotocopia della ricevuta del versamento su C/C postale n. 14291561 intestato a “Gruppo Editoriale Infomedia S.r.l. Ponsacco”� Allego assegno bancario intestato a “Gruppo Editoriale Infomedia S.r.l.” (NON TRASFERIBILE)� Allego fotocopia del Bonifico Bancario effettuato sul C/C 000010005424 CAB 71120 ABI 6370 CIN “M” - Cassa di Risparmio di Volterra Agenzia di Ponsacco� Autorizzo l’addebito dell’importo di €_______________ sulla mia CARTA VISA/CARTASì Numero

Scadenza Titolare ____________________________________________ nato il ___/___/_______ Firma ___________________________________

� Mi collego al sito www.infomedia.it dove potrò effettuare il pagamento con carta di credito in modalità sicura (Banca SELLA)

ABBONAMENTICOD. CLIENTE ____________________

VBJ 60

� Gli abbonamenti decorrono dal primo numero raggiungibile. Per attivazioni retroattive contattaci al numero 0587/736460 o invia una e-mail all’indirizzo [email protected]

L’IVA sul prezzo dell’abbonamento è assolta dall’Editore e non sussiste l’obbligo di emissione della fattura, ai sensi del D.M. 9 aprile 1993, art.1, comma 5; pertanto, a fini contabili, farà fede la sola ricevuta di pagamento; perciò la fattura verrà emessa solo se esplicitamente richiesta al momento dell’ordine.

Prezzo PrezzoRiviste Abbonamento Rinnovo

CP € 52,00 � € 48,00 �

DEV € 52,00 � € 48,00 �

VBJ € 46,00 � € 42,00 �

Login € 46,00 � € 42,00 �

4 riviste € 196,00 � € 180,00 �

CP Web € 42,00 � € 38,00 �

DEV Web € 42,00 � € 38,00 �

VBJ Web € 44,00 � € 40,00 �

Login Web € 44,00 � € 40,00 �

4 riviste Web € 172,00 � € 156,00 �

4 riv. + 4 riv. Web € 368,00 � € 300,00 �

TOTALE € ________________

Prezzo PrezzoRiviste - Spedizione Abbonamento Rinnovo

POSTA PRIORITARIA

CP P.P. € 70,00 � € 66,00 �

DEV P.P. € 70,00 � € 66,00 �

VBJ P.P. € 56,00 � € 52,00 �

Login P.P. € 56,00 � € 52,00 �

4 riviste P.P. € 252,00 � € 236,00 �

4 riv. P.P. + 4 riv. Web € 424,00 � € 356,00 �

TOTALE € ________________

ARRETRATI

Page 67: v2004 06 vbj60
Page 68: v2004 06 vbj60