Lo strato applicativo di Internet

Server Apache

Utilizzeremo il server HTTP Apache, il più diffuso in assoluto.

Apache, questo sconosciuto

Si narra che il nome Apache derivi da una contrazione di a patchy server, dato che inizialmente, non era costituito da nient'altro che una serie di patch (pezze) al preesistente server di NCSA.

Il suo funzionamento prevede il pre-fork di alcuni processi figli, in modo che le nuove richieste HTTP non vengano rallentate nell'attesa della creazione di un nuovo processo figlio. Oppure, si può ricorrere ad un modulo orientato al multi-threading come worker.

In ogni modo, l'architettura di Apache prevede di delegare molti tipi di elaborazioni possibili ad un insieme di diversi moduli che implementano ognuno una diversa funzionalità. In questo modo, si rende indipendente lo sviluppo dei moduli, e delle funzionalità associate, da quello del nucleo del server web.

Installazione

Con Synaptic, scarichiamo ed installiamo apache2apache2-doc e apache2-utils. Impartendo il comando ps axf | grep apache, verifichiamo se è già in esecuzione, ed in caso contrario, lanciamo il server web con il comando sudo service apache2 start. Apriamo il Browser web, e visitiamo la URI http://127.0.0.1. Apparirà una pagina, con una sola frase: It works! a conferma del successo dell'operazione.

Abilitazione dei componenti

Apache basa la sua configurazione su di un nutrito insieme di files, che troviamo a partire dalla directory /etc/apache2, e di cui il principale è (/etc/apache2/apache2.conf) che, oltre ad impostare alcuni parametri di base, provvede a referenziare gli altri files di configurazione, ognuno dedicato ad un aspetto particolare.

Moduli

Per quanto riguarda l'abilitazione dei moduli, ognuno di essi può richiedere la definizione di particolari variabili e direttive, che sono dichiarate in files separati, e tenuti assieme mediante direttive di Include, e links simbolici presenti nelle directory. Infatti, in /etc/apache2/apache2.conf, sono presenti le direttive

Include /etc/apache2/mods-enabled/ *.load
Include /etc/apache2/mods-enabled/ *.conf

che attivano il caricamento (.load) e la configurazione (.conf) dei moduli associati a tutti i files presenti nella directory mods-enabled. Se investighiamo sul contenuto della directory /etc/apache2/mods-enabled però, vi troviamo una serie di collegamenti simbolici a files dallo stesso nome, ma che si trovano in /etc/apache2/mods-available. Per abilitare un modulo quindi, occorre creare il link simbolico per i suoi due files di configurazione, dalla seconda directory alla prima. Per semplificare questa operazione, sono disponibili i comandi a2enmod e a2dismod, rispettivamente per abilitare e disabilitare un modulo, come ad esempio

sudo a2dismod info        # disabilita il modulo info
sudo a2enmod info         # ri-abilita il modulo info

Siti

Lo stesso server Apache può ospitare diversi siti virtuali, per ognuno dei quali è possibile avere un diverso file di configurazione, presente presso la directory /etc/apache2/sites-available. D'altra parte, in fondo al file di configurazione principale /etc/apache2/apache2.conf troviamo la direttiva Include /etc/apache2/sites-enabled/ che provvede ad attivare solo i siti definiti dai files presenti in questa seconda directory. In modo del tutto analogo ai moduli, i diversi siti vengono vitalizzati creando un link simbolico all'interno della directory /etc/apache2/sites-enabled verso i loro file di configurazione, mediante il comando sudo a2ensite info. A seguito della installazione, risulta già pre-impostato un unico sito principale.

La mia prima pagina

Come già fatto in una precedente esercitazione, pubblichiamo le nostre pagine sotto la propria directory di utente. Prima di tutto, dobbiamo abilitare il modulo userdir, che consente appunto l'uso delle directory di utente. Per questo, eseguiamo i comandi

sudo a2enmod userdir
sudo service apache2 force-reload

Creiamo quindi la directory public_html in /home/labint, scriviamo al suo interno un file di test (es prova.txt) contenente una frase simpatica, e proviamo a visualizzare il file, referenziando la URI http://127.0.0.1/~labint/prova.txt. Come dite? Compare un messaggio Forbidden ??

Studiamo il log

Per verificare l'esito delle operazioni in corso, possiamo andare a leggere le informazioni salvate nei file di log, ossia /var/log/apache2/access.log che documenta gli accessi, e /var/log/apache2/error.log, che documenta le circostanze di errore. Quest'ultimo contiene l'informazione che cercavamo, ossia il messaggio

[Wed May 30 08:32:02 2007] [error] [client 127.0.0.1] (13): file permissions deny server access: /home/labint/public_html/prova.txt, referer: http://127.0.0.1/~labint/

I permessi dei files da mostrare

Infatti, il file che abbiamo creato va reso leggibile dall'utente con i cui privilegi è in esecuzione il server web. Per modificare i permessi del file, si può agire mediante Nautilus, oppure impartire il comando chmod 644 /home/labint/public_html/prova.txt. Inoltre, anche la directory public_html deve poter essere accessibile, e questo (per le directory) equivale ad aver settato il bit di esecuzione, che può essere modificato con chmod 755 /home/labint/public_html. Fatto ciò, possiamo riprovare a visualizzare il nostro file !

Tenere d'occhio il log può essere un buon modo per accellerare lo sviluppo di un sito: a tal fine, è utile impartire il comando tail -f /var/log/apache2/error.log, che ha l'effetto di mantenere la schermata agganciata allo stato corrente del file, scrollando quando vi vengono aggiunte nuove linee.

Personalizzazione del Log

Le informazioni che compaiono nei file di log, come access.log, possono essere personalizzate mediante le direttive LogFormat and CustomLog anche allo scopo di particolarizzare il tipo di report generato dagli appositi programmi di analisi giornaliera.

Il sito principale

Ma, oltre alle directory degli utenti, come si fa ad usare direttamente il FQDN del sito, con una URL del tipo http://localhost/pagina.html ? Le definizioni legate a questa possibilità, si trovano nel file /etc/apache2/sites-enabled/000-default, in cui si dichiara, tra le altre cose, che la DocumentRoot del web server corrisponde alla directory /var/www/, e quindi è lì che dobbiamo salvare i files corrispondenti a path della URI immediatamente successivi al nome a dominio del server.

Virtual Host

Abbiamo parlato di sito principale, perché presso lo stesso computer possono essere ospitati siti diversi, corrispondenti a nomi a dominio diversi, ma relativi (mediante dei RR del DNS di tipo CNAME) allo stesso indirizzo IP. Questa tecnica prende il nome di Virtual Host, e si basa sul fatto che il nome a dominio che compare nella URL di richiesta, è comunque indicato nell'header Host della stessa. In virtù di questo, Apache si basa sulle impostazioni contenute nei files di configurazione presenti in /etc/apache2/sites-enabled/, per individuare la configurazione del Virtual Host corrispondente, e mappare la richiesta su diverse parti del filesytem.

La documentazione

Il package di documentazione che abbiamo installato copia nelle opportune directory dello stesso server web le pagine accessibii a partire da http://127.0.0.1/manual/, che sono copie conformi di quanto compare presso il sito della fondazione Apache. Per apprezzare  le direttive di configurazione che consentono la comparsa della nostra pagina on-line, possiamo leggere l'How-to corrispondente a Per-user Web Directories (public_html), che ci guida alla configurazione di Apache indicando una procedura operativa, ed al contempo fornendo i links alla documentazione delle direttive usate.

I file di configurazione

Il primo impatto con le direttive, i moduli, ed contesti, che definiscono il comportamento di apache, può scoraggiare chi è abituato alle interfacce di configurazione grafica: d'altra parte, affrontando un problema alla volta, dopo qualche tempo si apprezza l'elevato grado di configurabilità ottenibile. Dei moduli abbiamo già parlato; per quanto riguarda le direttive (ad es userdir), la documentazione relativa utilizza una terminologia il cui significato è documentato a parte. In particolare, le direttive sono attivabili all'interno di specifici contesti, come ad esempio <Directory> o <Location>, che individuano il loro campo di applicabilità. Per esempio, in userdir.conf troviamo

UserDir public_html        # la dir di utente da rendere accessibile
UserDir disabled root      # per l'utente root non è accessibile

<Directory /home/*/public_html>
    AllowOverride FileInfo AuthConfig Limit
    Options MultiViews Indexes SymLinksIfOwnerMatch IncludesNoExec
</Directory>

che indica come, a seguito della attivazione del modulo userdir, le direttive AllowOverride etc etc, si applichino a tutte le directory di utente /home/*/public_html. Il meccanismo dei contesti permette di limitare l'effetto delle direttive di configurazione, solo a particolari sezioni del sito.

Ma oltre ai files presenti in /etc/apache2/mods-enabled, la configurazione di apache può essere ulteriormente modificata anche mediante dei file .htaccess, dislocati nelle stesse directory in cui si trovano i documenti da servire, in modo da permettere agli utenti stessi di impostare le loro preferenze, per i contesti in cui si trovano i loro contenuti.

Informazioni sul server

Apache offre due URI mediante le quali ci mostra i dettagli sullo stato delle connessioni che sta servendo, e l'effetto delle diverse direttive di configurazione:

Mappare le URI sui files

Sicuramente un indirizzo in cui compare una tilde (~) non è dei più belli da dare in giro, e per costruire un indirizzo più leggibile possiamo ricorrere ad una tra due direttive che ci vengono in soccorso: Alias e Redirect, in cui la prima serve per mettere in corrispondenza la parte path della URI, con un percorso nel filesystem locale, mentre la seconda serve per inviare risposte di tipo 30x Redirect, anche verso siti esterni, in corrispondenza di URI particolari. Mentre l'argomento nel suo complesso è ben trattato nella documentazione, indichiamo qui la direttiva da inserire nel file nostro alias.conf (sudo gedit /etc/apache2/mods-enabled/alias.conf), per far corrispondere all'indirizzo http://127.0.0.1/~labint, un più leggibile http://127.0.0.1/labint:

Alias /labint "/home/labint/public_html"

e quindi, far rileggere la configurazione ad apache con il comando sudo service apache2 reload.

Autenticazione

Anche se la nostra prima pagina non è un granché, possiamo comunque proteggerla da sguardi indiscreti. Nella documentazione di Apache troviamo le indicazioni su come procedere, e seguendo quei consigli, possiamo fare così:

htpasswd -c /home/labint/passwords labint
e quando ci viene chiesta, inseriamo la password che vogliamo ci venga richiesta. Proviamo a guardare ora, cosa contiene quel file ?
AuthType Basic
AuthName "Laboratorio Internet"
AuthUserFile /home/labint/passwords
Require user labint
in cui AuthName definisce il realm che verrà indicato nella finestra di richiesta del browser, la direttiva AuthUserFile definisce il file di password che consente l'autenticazione dell'utente, e Require definisce le restrizioni di accesso (autorizzazione) successive alla fase di autenticazione.

Notiamo che non è stato necessario ri-lanciare il server apache perché le modifiche avessero effetto: questo vuol dire che i file .htaccess vengono ri-letti ad ogni nuova richiesta, e questo può essere un elemento di inefficienza all'aumentare del carico, per cui se si ha la possibilità di intervenire sui files di configurazione di sistema, è preferible modificare quelli.

Digest Authentication

Nel caso si scelga questo tipo più sicuro di autenticazione, la versione delle password residente presso il server dovà essere generata mediante l'utility htdigest anziché htpasswd, dato il diverso meccanismo utilizzato, ed usata anche la direttiva AuthDigestDomain.

Fornitori delle credenziali

Nell'esempio svolto, le credenziali degli utenti presso il server sono memorizzate su di un semplice file piatto. All'aumentare delle dimensioni di questo, e della sua frequenza di utilizzo, questo metodo può divenire inefficiente. Esiste allora la possibilità di accedere alle credenziali da verificare mediante un file indicizzato (modulo authn_dbm), un DBMS (modulo authn_dbd), od un server LDAP (modulo authnz_ldap).

Oscuramento

Può succedere che si voglia interdire l'accesso ad una pagina, od una directory, nei riguardi delle richieste che giungono da un certo computer, o da un gruppo di computer. Le regole generali prevedono l'uso delle direttive Allow, Deny e Order, ma per una sperimentazione semplice semplice, ci si può limitare ad inserire nel file /home/labint/public_html/.htaccess la direttiva

Deny from All

e quindi, provando ad accedere di nuovo alla nostra pagina, possiamo verificare come ora l'accesso sia divenuto proibito. Una volta fatta la prova, commentiamo la direttiva, anteponendo un #.

Gestione dei Mime Type

Nel file /etc/apache2/mods-enabled/mime.conf, compare la direttiva

TypesConfig /etc/mime.types

che individua in che file sono dichiarate le corrispondenze tra le estensioni dei nomi di file ed i Mime-Type. Questa corrispondenza sarà quindi usata per determinare il Mime-Type da usare come argomento dell'Header Content-Type nella risposta inviata ad un browser che avesse richiesto un file con una delle estensioni presenti. Nel caso in cui si desideri erogare un contenuto con una estensione che non rientra tra quelle elencate in /etc/mime.types, si possono definire delle ulteriori associazioni, inserendo in mime.conf una o più direttive del tipo

AddType MIME-type extension [extension]

Il mio primo CGI

Seguendo (più o meno) i suggerimenti riportati nell'How-To fornito con Apache, per sperimentare l'esecuzione di codice da parte di un server web possiamo modificare il file di configurazione relativo alle directory degli utenti (sudo gedit /etc/apache2/mods-enabled/userdir.conf) in modo che appaia come segue:

<IfModule mod_userdir.c>
    UserDir public_html
    UserDir disabled root
    <Directory /home/*/public_html>
        AllowOverride FileInfo AuthConfig Limit Indexes
        Options ExecCGI MultiViews Indexes SymLinksIfOwnerMatch IncludesNoExec
        AddHandler cgi-script .cgi
    </Directory>
</IfModule>

In particolare, la direttiva Options ExecCGI è quella che abilita l'esecuzione di codice CGI, mentre AddHandler istruisce Apache a trattare i files con estensione .cgi come programmi eseguibili, eseguirli, e tentare di inviare lo standard output risultante al browser che aveva referenziato tale programma. Quindi, non tutti i programmi presenti in /home/labint/public_html saranno eseguiti, ma solo quelli con estensione .cgi. Se nella propria versione del file ci sono cose in più, non importa; verifichiamo invece che non ce ne siano in meno e, se abbiamo apportato modifiche, chiediamo ad Apache di rileggere la configurazione con il comando sudo service apache2 reload

Creiamo ora il nostro CGI (gedit primocgi.cgi) utilizzando il linguaggio Perl, in modo che appaia come segue:

#!/usr/bin/perl
#
# - primocgi - visualizzazione delle variabili di ambiente ricevute
#
print "Content-type: text/plain\n\n";
print "Salute a tutti.\n\n";
print "State osservando lo standard output prodotto da $0.\n";
print "I files presenti in questa directory sono:\n\n";
system "ls -l";
print "\n o o o o o o o o o o o o o o o o o o o o \n\n";
print "Qui sotto, mostriamo i valori associati alle variabili di ambiente che apache rende accessibili a questo script:\n\n";
foreach $key (keys %ENV) {       # scandisce la lista delle chiavi di %ENV
  print "$key --> $ENV{$key}\n"; # mostra le coppie chiave-valore
}

e quindi, ricordiamoci di rendere eseguibile il programma, anche da parte di Apache: chmod 755 primocgi.cgi. In alternativa, possiamo direttamente installare presso /home/labint/public_html questo e gli altri CGI discussi appresso, scomprimendo l'archivio fornito più avanti. Nel caso in cui i CGI sembrano non funzionare, è probabilmente possibile investigare sulla natura del problema, analizzando il contenuto dell'error.log, verso cui viene re-diretto lo standard error del CGI.

Ora verifichiamone il corretto funzionamento, referenziando la URI http://127.0.0.1/labint/primocgi.cgi, oppure invocandolo mediante la form di esempio mostrata a lezione.  Eventualmente, qui troviamo un esempio del risultato che si dovrebbe ottenere. Notiamo che:

Come risultato della esecuzione del CGI possiamo trovare tutti i nomi ed i valori delle variabili di ambiente, come ad esempio alcuni degli header contenuti nella richiesta HTTP. In particolare, se abbiamo invocato il CGI a partire da una form usando il metodo GET, nella variabile QUERY_STRING possiamo osservare la componente della URI che contiene i nomi ed i valori dei parametri di chiamata, corrispondenti a quelli impostati mediante i controlli della form stessa. Pertanto, primocgi.cgi può essere utilizzato al posto del reale CGI che stiamo progettando, per verificare che almeno il passaggio dei parametri produca l'effetto desiderato.

Metodo POST

Come illustrato nella parte di teoria, utilizzando il metodo POST, i valori impostati mediante i controlli della form sono passati nel body HTTP, e non nella URI: pertanto la modalità di accesso agli stessi usata da primocgi.cgi non è più idonea. Questo secondo esempio, che chiameremo secondocgi.cgi (ispirato da questo corso), ha appunto lo scopo di mostrare l'effetto di una tale chiamata.

#!/usr/bin/perl
#
# - secondocgi - visualizza il contenuto del body HTTP
#
use strict;

print "Content-type: text/plain\n\n";
print "Salute a tutti.\n\n";
print "State osservando lo standard output prodotto da $0.\n";
print "\n o o o o o o o o o o o o o o o o o o o o \n\n";

print "Qui sotto, lo standard input ricevuto dal server web\n\n";
my $buffer;
read (STDIN, $buffer, $ENV{'CONTENT_LENGTH'});
print $buffer;

print "\n\ned ora, alcune delle variabili di ambiente ricevute:\n\n";
my $key;
foreach $key ('REQUEST_METHOD', 'QUERY_STRING', 'CONTENT_LENGTH', 'CONTENT_TYPE') {
  print "$key --> $ENV{$key}\n";
}

L'istruzione read ha lo scopo di copiare nella variabile-stringa $buffer il contenuto del body della richiesta HTTP ricevuto via standard input, che ha la dimensione specificata dall'header HTTP Content-lenght, il cui valore è copiato da Apache nella omonima variabile di ambiente, che viene letta dal CGI accedendo all'hash %ENV.

Una form che invoca questo script è visibile nella parte di teoria, mentre qui possiamo osservare il risultato della invocazione. Come osserviamo, ora i parametri impostati mediante i controlli della form sono presentati nel body HTTP.

Interpretazione della richiesta

A prima vista, quella stringa del tipo nome1=valore1&nome2=valore2& etc etc ... non sembra molto masticabile da un programma, ma non è poi così difficile cavarne qualcosa di buono, come viene fatto con questo che chiameremo terzocgi.cgi, che funziona sia con il metodo GET che con il POST, e... si limita a farci vedere ciò che sa fare.

#!/usr/bin/perl
#
# - terzocgi - effettua la decodifica dei parametri ricevuti, sia con GET che con POST
#
use strict;

print "Content-type: text/plain\n\n";
print "Salute a tutti.\n\n";
print "State osservando lo standard output prodotto da $0.\n";
print "\n o o o o o o o o o o o o o o o o o o o o \n\n";

print "Qui sotto, lo standard input ricevuto dal server web\n\n";
my $buffer;
read (STDIN, $buffer, $ENV{'CONTENT_LENGTH'});
print $buffer;

print "\n\nPoi, alcune delle variabili di ambiente ricevute:\n\n";
my $key;
foreach $key ('REQUEST_METHOD', 'QUERY_STRING', 'CONTENT_LENGTH', 'CONTENT_TYPE') {
  print "$key --> $ENV{$key}\n";
}

print "\n\nInfine, la decodifica dei campi ricevuti\n\n";
my @data;
# suddivide le coppie name/value tra loro
if ( $ENV{ 'REQUEST_METHOD' } eq "GET" ) {
     @data = split /&/, $ENV{ 'QUERY_STRING' };
} elsif ( $ENV{ 'REQUEST_METHOD' } eq "POST" ) {
     @data = split /&/, $buffer;
} else {
   print "Sono permessi solo i metodi GET e POST!";
   exit;
}

my (%data, $value, $item);
foreach $item ( @data ) {

   # Suddivide le coppie e traduce i + in spazi
   ($key, $value) = split /=/, $item;
   $key   =~ tr/\+/ /;
   $value =~ tr/\+/ /;

   # Converte %XX da numeri hex ad alfanumerico
   $key   =~ s/%([\da-f][\da-f])/pack("c",hex($1))/gei;
   $value =~ s/%([\da-f][\da-f])/pack("c",hex($1))/gei;

   # Assegna il valore ottenuto in corrispondenza della chiave relativa
   $data{$key} = $value;
}

foreach $key (keys %data) {
  print "$key ==>> $data{$key}\n";
}

Notiamo che le conversioni effettuate sono presenti per de-codificare i parametri che sono stati inviati nel body HTTP con un MediaType MIME di tipo application/x-www-form-urlencoded, come possiamo verificare sniffando il traffico relativo, od anche aggiungendo al CGI la stampa della variabile di ambiente HTTP_CONTENT_TYPE. La form che invoca terzocgi.cgi è mostrata nella parte di teoria, e qui è mostrato il risultato della invocazione.

Un ristorante virtuale

A partire dagli esempi forniti fin qui, ed usando il modulo Perl Email::Send, scriviamo un CGI in grado di recapitare per email l'ordine ricevuto. Il sapore non sarà un gran che, però farà un gran bell'effetto :-)

Per prima cosa, scarichiamo ed installiamo il modulo con le sue dipendenze mediante il comando sudo cpan Email::Send, che vi chiederà un pò di volte la vostra opinione, ma dovrebbe essere più che sufficiente che voi rispondiate sempre di si, premendo enter. Quindi, inseriamo in una pagina HTML il codice della form (sempre quello proposto a lezione), facendogli però ora invocare (mediante l'attributo action) il nuovo cgi, che chiamiamo vristo.cgi

Il mio primo ristorante virtuale
email:
ordine:
pizza
lasagna
tiramisu
pinta 

Infine, editiamo il nostro nuovo cgi, vristo.cgi, derivato da quanto già realizzato con terzocgi, il cui codice (che fa uso di espressioni regolari) è riportato sotto. Dopo aver salvato vristo.cgi nella directory public_html ed aver correttamente configurato i permessi, possiamo provare ad immettere un ordine nella form soprastante, e verificare se l'email ci arriva! Ad ogni modo, anche stavolta riportiamo l'esito della esecuzione.

#!/usr/bin/perl
use strict;
use Email::Send;

# impostare qui, quello che sarà il mittente dell'email
my $from = 'alef@infocom.uniroma1.it';

print "Content-type: text/plain\n\n";
print "Salute a tutti.\n\n";
print "State osservando lo standard output prodotto da $0.\n";
print "\n o o o o o o o o o o o o o o o o o o o o \n\n";

# Leggiamo lo standard input ricevuto dal server web\n\n";
my $buffer;
read (STDIN, $buffer, $ENV{'CONTENT_LENGTH'});

my @data;
if ( $ENV{ 'REQUEST_METHOD' } eq "GET" ) {     # suddivide le coppie name/value tra loro
     @data = split /&/, $ENV{ 'QUERY_STRING' };
} elsif ( $ENV{ 'REQUEST_METHOD' } eq "POST" ) {
     @data = split /&/, $buffer;
} else {
   print "Sono permessi solo i metodi GET e POST!\n";
   exit;
}

my (%data, $value, $item, $key, $message, $sender, $result);
foreach $item ( @data ) {
   # suddivide le coppie e traduce i + in spazi
   ($key, $value) = split /=/, $item;
   $key   =~ tr/\+/ /;
   $value =~ tr/\+/ /;

   # Converte %XX da numeri hex ad alfanumerico
   $key   =~ s/%([\da-f][\da-f])/pack("c",hex($1))/gei;
   $value =~ s/%([\da-f][\da-f])/pack("c",hex($1))/gei;

   $data{$key} = $value;
}

$message = "To: $data{'email'}\nFrom: $from\nSubject: Buon Appetito!\n\nIl CGI $0 ha ricevuto un ordine per te, eccoti servito: $data{'ordine'}!\n";

$sender = Email::Send->new({ mailer => 'SMTP',
                             mailer_args => [ Host => '127.0.0.1']});

print "Ecco il messaggio che intendiamo spedire:\n\n$message\n";

$result = $sender->send($message) ;
print ("L'invio ha avuto esito: $result\n");

Dato che la spedizione della email avverrà a carico del server SMTP presente sul nostro stesso computer, accertiamoci che questo sia attivo, e ricordiamoci di usare un indirizzo email di destinazione. Qualora si desideri recapitare il pasto ad una email corrispondente ad un ISP esterno, ricordarsi di modificare il server SMTP in quello di Outbound.

Dietro le quinte

Tenendo aperto wireshark con filtro di cattura port 80 or port 25 possiamo osservare come, dopo aver ricevuto la richiesta HTTP ma prima di inviare la risposta relativa, vristo.cgi apra una connessione TCP verso il server SMTP specificato, e svolga con esso il colloquio necessario all'invio della email: è il modulo Perl Email::Send, che implementa le funzioni di un client SMTP, a permettere al CGI di interfacciarsi a questo diverso servizio di rete.

Tutto il codice prodotto

Nell'archivio cgi.tar si può trovare ilcodice discusso in questa pagina. Per far funzionare gli esempi che invocano i cgi, scomprimere l'archivio nella directory public_html del proprio computer, a meno che non ne sia stata configurata una diversa, ed assicurarsi di aver impostato correttamente i permessi di accesso al file.

HTTPS e VirtualHost

Non resta ora che da configurare il nostro server web in modo da offrire la possibilità di collegarsi in modo crittograficamente sicuro, come previsto da HTTP su TLS (HTTPS), ovvero

Lo scenario generale della infrastruttura di sicurezza web è particolarmente ben riassunto nella documentazione di Apache, che offre questi servizi mediante il modulo ssl. Inoltre, usare lo stesso server Apache per offrire contemporaneamente sia l'HTTP su porta 80, che l'HTTPS su porta 443, richiede la configurazione di due diversi Virtual Host, di cui il primo è quello (default) utilizzato finora, ed il secondo è quello che ci accingiamo a definire nel file /etc/apache2/sites-available/secure. Pertanto, impartiamo ora i comandi

sudo a2enmod ssl
sudo cp /etc/apache2/sites-available/default /etc/apache2/sites-available/secure
sudo a2ensite secure
sudo gedit /etc/apache2/sites-enabled/secure

che abilitano sia il modulo ssl che il sito secure, il cui file di configurazione è ottenuto a partire da una copia di quello di default. L'ultimo comando della serie, ci consente di editare la configurazione del file che definisce il funzionamento del server HTTPS. Innanzitutto, modifichiamo le prime due linee, in modo da dichiarare esplicitamente l'uso della porta 443, e quindi abilitiamo l'uso di SSL su questa porta, mediante la direttiva SSLEngine, in modo che ora l'inizio del file secure appaia come segue:

NameVirtualHost *:443
<VirtualHost *:443>
SSLEngine on

Se ora proviamo a rilanciare Apache (sudo /etc/init.d/apache2 restart), questo si rifiuta di andare in esecuzione, notificando nel file di log il messaggio:  Server should be SSL-aware but has no certificate configured [Hint: SSLCertificateFile]. Infatti, occorre specificare il certificato e la chiave da usare, inserendo di seguito alla direttiva SSLEngine, le direttive

SSLCertificateFile /etc/ssl/certs/ssl-cert-snakeoil.pem
SSLCertificateKeyFile /etc/ssl/private/ssl-cert-snakeoil.key

Stavolta Apache accetta di partire, ed invocando una delle URI servite (es https://localhost), la pagina arriva, e il campo di inserimento indirizzo si colora di giallo, e si arricchisce del disegno di un lucchetto, a segnalare la sicurezza della navigazione. Ma... prima di arrivare a questo bel risultato, nel file di log abbiamo osservato il messaggio

RSA server certificate CommonName (CN) `terranova' does NOT match server name!?

ed infatti, il certificato che abbiamo utilizzato non è rilasciato al nome a dominio che viene servito da Apache. Questo fatto comporta che anche il browser, prima di visualizzare la pagina richiesta, notifica alcune finestre di avvertimento, mediante le quali dichiara

Mentre il primo fatto è evidentemente legato alla assenza (presso il browser) del certificato della CA che ha firmato il certificato del server, il secondo è risolvibile avendo l'accortezza di richiedere un certificato per il quale il Common Name corrisponda esattamente al nome a domino per cui si intenderà usarlo. Per questo, possiamo generare una richiesta di certificato e firmarla, usando ad es. lo strumento gnoMint, ed utilizzando per la firma la chiave privata di labsoftelCA, che importiamo in gnoMint. Una volta esportati certificato e chiave privata intestati al CN uguale al nostro dominio, sostituiamo (nel path dichiarato mediante la direttiva SSLCertificateFile presente nel file secure) questi nuovi valori, a quelli relativi a ssl-cert-snakeoil, e ri-lanciamo Apache. Sullo schermo, si sviluppa il dialogo

alef@alef:~$ sudo /etc/init.d/apache2 restart
 * Starting web server apache2
Apache/2.2.4 mod_ssl/2.2.4 (Pass Phrase Dialog)
Some of your private key files are encrypted for security reasons.
In order to read them you have to provide the pass phrases.

Server alef-laptop.softel:443 (RSA)
Enter pass phrase:

OK: Pass Phrase Dialog successful.                               [ OK ]
alef@alef:~$

che rappresenta la richiesta, da parte di Apache, di conoscere la passphrase con cui è crittografata la chiave privata associata al nuovo cerificato usato, che nell'esempio precedente era invece lasciata in chiaro.

Distribuzione del certificato della CA

Finalmente immettendo la URI https://localhost, non si ottiene più l'avvertimento relativo al dominio sbagliato! Non resta quindi che importare nel browser il certificato della CA che ha firmato il certificato con cui abbiamo configurato Apache. Cliccando sul link del certificato appena fornito, questo viene visualizzato dal Browser, ma non importato dallo stesso. Pertanto, occorre prima salvarlo in un file, e quindi importarlo in modo esplicito, ossia (in firefox)

Alernativamente, si può configurare Apache affinchè quando uno UserAgent invoca la URI del certificato, questo venga automaticamente importato dal browser, senza la necessità dei passi ora illustrati. A questo scopo, occorre modificare il file /etc/apache2/apache2.conf, inserendo la direttiva

AddType application/x-x509-ca-cert pem

in modo che il file con estensione .pem sia inviato con il MIME-Type corretto.


Riferimenti

x
Logo

Lo Strato Applicativo
di Internet

Dal TCP al VoIP, dal DNS all'Email alla crittografia, tutto ciò che accade dietro le quinte di Internet, completo di cattura del traffico.
Scopri come effettuare il download, ricevere gli aggiornamenti, e contribuire!


Realizzato con Document made with Kompozer da Alessandro Falaschi -
Capitolo: OpenSER e Twinkle