<- SL: Intro - Archivio Generale - Copertina - SL: GNU/Linux e autoloader ->

Sistemi Liberi


Teoria del firewall

di Tommaso Di Donato


L'articolo...

Che il nostro sistema sia un server internet o una workstation per uso privato, è estremamente importante fare tutto quanto in nostro potere per garantirne la sicurezza. I motivi sono i più svariati: da semplici considerazioni riguardo l'importanza dei nostri dati, a vere e proprie questioni legali! La network security è un argomento affascinante, ma estremamente vasto e complesso; questo articolo vuole essere una introduzione teorica a ciò che, mooolto erroneamente, è creduto la panacea di tutti i mali: il firewall.



Indice


Qualche considerazione...

    Vorrei partire da una considerazione ovvia (tanto ovvia, che spesso viene assolutamente dimenticata): nessun firewall o IDS potrà mai proteggere un sistema mal configurato (o in generale una applicazione "bacata"). Da questo, emerge l'importanza di concepire il termine "security" come un'attitudine programmatica e progettuale, e non un mero insieme di accorgimenti tecnici. A questo riguardo, non si può fare a meno di sottolineare che ogni intervento mirato ad aumentare il livello di sicurezza implica inevitabilmente un aumento di costi, siano essi diretti (come l'acquisto di nuovo hardware) o indiretti (ad esempio, aumento di complessità di alcune operazioni).
    La prima cosa che dovremmo puntualizzare è: che cosa si intende precisamente con il termine "firewall"? Se la domanda in sè è molto chiara, la risposta non può esserlo altrettanto. In genere, la difesa perimetrale si compone di un packet filter e di un application proxy; alcuni indicano con firewall l'insieme dei due, altri solamente il packet filter in senso stretto. Personalmente, propendo per la seconda definizione (vedremo in seguito come un packet filter e un application proxy lavorino a livelli ISO/OSI assolutamente differenti).

Un minimo di basi: il modello ISO/OSI

    Per quanto possa sembrare il solito argomento trito e ritrito, il mio consiglio è quello di non saltare "a piè pari" questo paragrafo: la maggior parte delle richieste di aiuto che troviamo sulle mailing list derivano fondamentalmente proprio da una mancanza di conoscenze di base. Di seguito vorrei ricapitolare solamente ciò che ritengo fondamentale per capire bene cosa significhi pacchetto, firewall, circuito, ecc. Ed è per lo stesso motivo che la successiva analisi sarà fondamentalmente orientata al protocollo TCP/IP (anche se, come vedremo in seguito, sarebbe più opportuno definirlo una "suite" di protocolli).
    Il modello ISO/OSI (vedi fig.1) descrive le modalità di interazione tra le componenti di rete hardware e software. I livelli più bassi definiscono il supporto fisico della rete (cavo, scheda di rete o NIC, ecc); man mano che si sale, ciascun livello prepara i dati per il livello immediatamente superiore.

Fig.1: La pila ISO/OSI

    Il livello fisico (layer 1) è il più basso del modello OSI. Esso trasmette lo stream di bit su un supporto fisico (ad esempio il cavo): rappresenta proprio il collegamento tra le interfacce (elettriche ottiche, meccaniche...) e il cavo. Il livello data link (layer 2) si occupa di "impacchettare" i bit ricevuti dal livello fisico per trasformarli in un frame di dati, aggiungendo tra l'altro l'ID del mittente, del destinatario e il campo CRC. Il livello network (layer 3 ) è responsabile dell'indirizzamento dei pacchetti, e della traduzione degli indirizzi da nomi logici a indirizzi fisici. Determina inoltre il percorso del pacchetto (routing) in base alle condizioni della rete, alla priorità di servizio, ecc. E' il layer che si occupa della frammentazione e successivo riassemblaggio di pacchetti troppo grandi. Il livello di trasporto (layer 4) fornisce il controllo di flusso, la gestione degli errori, le conferme di avvenuta ricezione, ecc. Il livello di sessione (layer 5) consente a due applicazioni su differenti computer di comunicare utilizzando una connessione (detta appunto sessione). Il livello di presentazione determina il formato per lo scambio dei dati tra i vari computer: esso riceve le informazioni dal livello superiore, trasformandole in un formato intermedio facilmente riconoscibile. Esso è il responsabile della conversione del protocollo, della codifica dei dati, di una eventuale conversione del set di caratteri, ecc. Si occupa inoltre della compressione dati, per ridurre il numero di bit che verranno trasmessi. Il livello applicazione gestisce l'accesso ai servizi di rete da parte delle varie applicazioni, controllano l'accesso generale alla rete, il controllo del flusso e un eventuale recupero in caso di errori.
    Alla luce di quanto detto finora, vediamo di capire un po' meglio la struttura del pacchetto: a livello applicazione, esso è formato semplicemente dai dati da trasferire. Ma appena il pacchetto scende al di sotto del layer 7, la sua struttura viene modificata, e ad esso viene aggiunto un header (contenente informazioni relative al layer stesso). Il body invece contiene i dati, più l'header del livello precedente; questo processo prende il nome di encapsulation (vedi fig.2).

Fig.2: L'incapsulazione del pacchetto, attraverso i vari layer
Fig.3: Header IP

Le sessioni TCP

    Il protocollo TCP si basa su un preciso schema di comunicazione, che deve garantire, oltre al normale trasferimento di dati, affidabilità (i pacchetti potrebbero venire danneggiati o persi, oppure duplicati), controllo del flusso e molteplicità di connessioni simultanee. Per quanto riguarda l'affidabilità e l'ordinamento dei byte, il protocollo TCP prevede l'utilizzo dei numeri di sequenza (sequence number) e dei flag (vedi fig. 4)

Fig.4: Header TCP

    Ogni volta che una applicazione deve trasportare dati, trasmette una richiesta di connessione TCP al livello di trasporto del proprio computer il quale, a sua volta, invia a una porta del computer remoto un messaggio TCP con il flag di sincronizzazione settato. In gergo, questo si chiama un SYN packet (pacchetto con il flag di SYN a 1). Insieme al flag di SYN, il pacchetto contiene un numero casuale, detto numero di sequenza iniziale; ogni pacchetto successivamente inviato dal client verrà numerato partendo da questo numero. Il server, quando riceve il pacchetto SYN, risponde con un altro pacchetto SYN, ed un proprio numero di sequenza iniziale. Il pacchetto di risposta inviato dal server avrà un altro flag a 1, quello di ACK ( acknowledgement), contenente il numero inviato dal client incrementato di uno. In soldoni, il server sta dicendo al client che accetta ulteriori pacchetti, e che il prossimo pacchetto deve contenere il numero di sequenza indicato dall'ACK. A questo punto sarà il client a dare l'ACK al server, e fatto questo può iniziare la vera e propria comunicazione. Per quanto detto finora, si dice che l'hand shaking del protocollo TCP è costituito da tre passi. Per chiarimenti, vedere figura 5.

Fig.5: Una sessione TCP. Il pacchetto iniziale, con il flag SYN attivo, contiene il numero di sequenza iniziale

Il classico packet filter

    I packet filter in senso stretto sono tipicamente implementati nei router (anche se al giorno d'oggi esistono apparati anche economici che implementano già la stateful inspection); l'analisi del pacchetto avviene a livello IP (corrispondente al Layer 3, Network, del modello ISO/OSI): sono essenzialmente 4 i campi che vengono presi in considerazione: indirizzo di origine del pacchetto, indirizzo di destinazione, protocollo (tcp, upd, icmp, isakmp, esp, ecc), ed eventuali opzioni. Quest'ultimo campo, solitamente, è vuoto, ma può contenere un flag di source routing, che viene utilizzato dal mittente per "scavalcare" le routing tables del destinatario; in passato, questo flag è stato spesso utilizzato in alcuni tipi di attacco, per cui molti firewall droppano di default un pacchetto con questa opzione attiva (in caso di linux, il kernel 2.4 ha una apposita opzione modificabile run-time mediante il file /proc/sys/net/ipv4/conf/all/accept_source_route, parametro che andrebbe posto uguale a zero).
    Come è facile capire, le cosiddette routing decisions vengono prese valutando se gli indirizzi di provenienza sono considerati attendibili per quella particolare destinazione, con quel particolare protocollo, ecc. Nel caso di packet filter più evoluti (praticamente tutti, al giorno d'oggi), il controllo del pacchetto avviene al Transport Layer (livello 4) del modello ISO/OSI: questo implica che, oltre alle informazioni contenute nell'header IP (indirizzo origine, destinazione e opzioni), vengono considerate anche le porte (sia di origine, che di destinazione). In questo modo, è possibile filtrare il traffico in base al protocollo utilizzato (http, https, smpt, ecc), in quanto molto spesso le applicazioni server aprono porte ben precise.

Nota: va comunque sempre ricordato che il protocollo TCP/IP non è in grado di garantire l'autenticità dell'indirizzo o della porta di origine contenuti nel pacchetto! Infatti, è possibile costruire dei pacchetti appositamente modificati , in modo tale che l'ip di provenienza risulti falso. Questo tipo di tecnica è alla base del cosiddetto ip spoofing.

    Il vantaggio di questo tipo di firewall è essenzialmente l'economicità: infatti, come abbiamo visto, l'analisi dei pacchetti avviene tutta a livello 3 o 4 del modello OSI, e questo processo richiede un uso di risorse (cicli di CPU) molto basso; in più, abbiamo già accennato al fatto che molti sistemi operativi dei router (anche i più economici) implementano già nativamente questo tipo di soluzioni. Un altro importante vantaggio dei packet filter è la loro trasparenza: le applicazioni non devono essere scritte in maniera particolare per beneficiare di questo tipo di protezione.
    Per contro, utilizzando i packet filter un client che dall'esterno fa una richiesta ad una applicazione server è a tutti gli effetti connesso direttamente al server stesso. In più, a volte la corretta configurazione di tali firewall può richiedere una discreta conoscenza del funzionamento dei protocolli di rete, al fine di sapere esattamente quali porte aprire per consentire le connessioni, senza aprire più porte del necessario (vedremo oltre un esempio classico, l'ftp). Per finire, tutto il sistema decisionale si basa sui dati presenti nell'header del pacchetto: questo ha due importantissime conseguenze:
- i dati nell'header possono essere falsificati, creando pacchetti appositamente modificati (il cosiddetto spoofing)
- non ho nessun controllo su ciò che è contenuto nel pacchetto (i dati veri e propri). Ad esempio, una richiesta ad un web server mirata a sfruttare un buffer overflow del server stesso verrà comunque lasciata passare

L'application Gateway

    All'esatto opposto troviamo l'application gateway: in questo caso, tutte le connessioni verso una macchina posta dietro di esso non si stabiliscono direttamente con la macchina stessa, bensì con l'application gateway. Questo implica che tutte le applicazioni server devono essere progettate per supportare questo tipo di connessione; inoltre, ogni specifico servizio (ftp, telnet, web, ecc) necessita di un proprio proxy (il programma che effettivamente si occupa della comunicazione con il servizio "protetto"). Un esempio pratico aiuta a chiarire le cose: prendiamo un server web e un client che intende visualizzare una pagina del sito; in caso di assenza di protezione perimetrale (o in caso questa sia deputata ad un packet filter), il browser del client si collega al server web per richiedere una pagina; a questo punto, il server risponde al client inviando quanto richiesto. In presenza di un application gateway, invece, il client si collega a quest'ultimo (a dire il vero, con l'application proxy che gestisce il protocollo http) ed effettua a lui la sua richiesta. A questo punto, è il proxy a collegarsi al web server vero e proprio, richiedendo la pagina in questione, per poi inviarla al client come se fosse stato lui stesso a generarla.
    Come risulta chiaro dall'esempio precedente, il principale vantaggio degli application gateway è che non sono possibili connessioni dirette tra la rete interna e quella esterna: questo implica che un baco in una applicazione server non consentirà ad un eventuale attacker di prendere diretto possesso della macchina. Inoltre, lavorando a livello Applicazione, il proxy ha accesso diretto al contenuto del pacchetto, e quindi il controllo di accesso può avvenire anche in base al contenuto stesso (vedremo un importante esempio nel caso di un web server). Infine, tramite un application gateway è possibile gestire gli accessi anche tramite autenticazione (username e password), e non solo in base agli IP di provenienza e destinazione.
    I principali difetti di questo tipo di protezione perimetrale sono facilmente intuibili: il carico di lavoro in termini di cicli CPU è estremamente maggiore rispetto a quello dovuto ad un semplice packet filter. Inoltre, come precedentemente detto, tutte le applicazioni server devono essere progettate per l'utilizzo del proxy (anche se, a dire il vero, è già sul mercato una seconda generazione, "trasparente", di application pxoxy).

La stateful inspection

    Alcuni autori sostengono che i cosiddetti "stateful firewall" si posizionino a metà strada tra il classico packet filter e l'application gateway. Personalmente non sono d'accordo con questa visione: anche se è certamente vero che i proxy beneficiano dello stato di una connessione nel processo decisionale, a mio avviso gli stateful firewall (a volte deviniti anche circuit-level firewall) sono piuttosto una evoluzione del classico packet filter.
    La stateful inspection è quel processo per cui ogni singola connessione autorizzata viene registrata dal firewall in una apposita tabella (la cosiddetta connection o state table): oltre all'ip sorgente e di destinazione, solitamente vengono registrati tanti altri dati, quali il protocollo, le porte, i flag, i sequence number, ecc. In questo modo, è difficile per un hacker potersi inserire in una connessione stabilita (il cosiddetto session hijacking). In pratica, ogni volta che un pacchetto arriva al firewall, viene verificato se esso fa parte (o è correlato, come vedremo nel caso dell'ftp) di una connessione precedentemente stabilita: in caso affermativo, esso viene lasciato passare senza ulteriori controlli sulle catene del firewall stesso, altrimenti subisce la sorte di un normale pacchetto in ingresso. Il vantaggio sta nel fatto che la verifica sullo stato è molto meno impegnativa del controllo "normale", per cui il firewall risulterà più performante.     Oltre ad una innegabile questione di prestazioni, è importante sottolineare che alcuni servizi di rete (e il file transfer protocol ne è l'esempio per eccellenza) necessitano di aprire un secondo canale di comunicazione: se non potessi sfruttare lo stato di una connessione, mi troverei a dover permettere le connessioni a tutte le porte non privilegiate (>1024), quando invece esse devono rimanere aperte solo per quella determinata connessione. Questo discorso sarà più chiaro fra poco.

Un caso pratico: il protocollo FTP

    Vediamo ora di chiarirci le idee analizando il più ricorrente degli esempi: il protocollo FTP (File Transfer Protocol). Soprannominato in passato "l'incubo dei sistemisti", questo protocollo nacque appunto per permettere lo scambio di file; non entreremo ora in merito a considerazioni legate alla trasmissione dati in chiaro, all'utilizzo o meno di accessi anonimi, ecc. La nostra attenzione si rivolgerà esclusivamente alle tecniche che si utilizzano per filtrare, tramite firewall, l'accesso a questo servizio.
    ll protocollo FTP può funzionare in due modalità, normale e passivo. Nel primo caso, un client si collega all'ftp server, in ascolto sulla porta 21, stabilendo un control channel; nel momento in cui viene trasferito un file, si crea un secondo canale, detto data channel, utilizzato appunto per il trasferimento dati (invio e ricezione file, visualizzazione del contenuto di una directory, ecc). Questo secondo canale va da una porta qualsiasi del client alla porta 20 del server (vedi fig· 5). Mettiamoci ora nei panni di chi deve scrivere le regole di un packet filter, che deve permettere le connessioni ad un ftp server posto "dietro" di esso; in questo caso, la stateful inspection non risulta di particolare utilità (so che i sistemisti più smaliziati e i security specialist staranno storcendo il naso, a queste parole... perdonate la semplificazione a scopo didattico): basterà semplicemente permettere tutte le connessioni in ingresso, verso le porte 21 e 20 del server ftp, e tutte le connessioni in uscita dalle porte 20 e 21 dello stesso server. Facendo riferimento ai dati di fig. 6, vediamo come scrivere le regole per un firewall Netfilter che protegge un server ftp:

   iptables -A FORWARD -p tcp -d 5.6.7.8 --dport 20:21 -j ACCEPT
   iptables -A FORWARD -p tcp -s 5.6.7.8 --sport 20:21 -j ACCEPT

Fig.6: Schema di una connessione ftp "standard"

    Ben differente è il caso dell'ftp in passive mode: in questa modalità il data channel viene creato tra una porta casuale del client ed una altrettanto casuale del server. Non sarà quindi possibile aprire sul firewall la sola porta 20! Affinchè il servizio sia garantito, è necessario consentire la connessione a tutte le porte del server superiori alla 1024esima. In questo modo, però, se sul server è in esecuzione un servizio in ascolto su di una porta non privilegiata (un esempio a caso, Microsoft SQL Server....), non avrò più nessun filtro a proteggere da connessioni non autorizzate. Ed ecco che la stateful inspection dimostra tutta la sua utilità e potenza: potrò esplicitare che le connessioni verso le porte >1024 sono consentite solo se correlate ad una già stabilita (e quindi autorizzata). Tornando all'esempio di prima, utilizzando Netfilter scriverò:

   iptables -A FORWARD -p tcp -d 5.6.7.8 --dport 21 -j ACCEPT
   iptables -A FORWARD -p tcp -d 5.6.7.8 --dport 1024:65535 -m state \
     --state ESTABLISHED,RELATED -j ACCEPT

    Vale la pena di sottolineare la differenza sostanziale di queste due differenti situazioni: senza utilizzare la stateful inspection, sarei costretto a non filtrare in alcun modo le connessioni dirette a porte >1024! Tramite le connection table, invece, il mio packet filter consentirà solamente le connessioni dipendenti da quelle già instaurate.

Fig.7: Schema di una connessione ftp in passive mode


L'autore

Tommaso Di Donato è sistemista Linux (Red Hat Certified Engineer) e Microsoft dal 1998; è stato dba Oracle presso la PA, nell'ambito del progetto di informatizzazione dei Centri Protesi INAIL in Italia.
Attualmente lavora presso un portale Internet, in qualità di sistemista e dba; si occupa inoltre di sicurezza informatica e TLC.



<- SL: Intro - Archivio Generale - Copertina - SL: GNU/Linux e autoloader ->