Per port scanning o "scansione delle porte" si intende una procedura attraverso la quale è possibile raccogliere informazioni sullo stato di un sistema e dei servizi in esecuzione all'interno di esso; il riferimento alle porte contenuto nel nome è dovuto al fatto che la scansione viene effettuata definendo quali porte siano in ascolto su una determinata postazione, una porta è infatti "in ascolto"(e quindi aperta) nel momento in cui vi è un processo che la utilizza.
Contrariamente a quanto si potrebbe pensare, il port scanning non è di per sé una tecnica di hacking ma una procedura abbastanza comune presso gli amministratori di rete e di sistema, serve infatti a verificare il funzionamento di determinate applicazione o per stabilire se una macchina presenta un ambiente adeguato per il funzionamento di un certo programma.
Anche uno script PHP è un'applicazione, in sede di sviluppo può quindi essere utile raccogliere informazioni relative ai servizi che girano all'interno del server e pensare di conseguenza la propria applicazione in modo che sia in grado di generare dinamicamente comportamenti in risposta alle condizioni dell'ambiente di riferimento. Un semplice esempio potrebbe essere quello relativo alle notifiche: un'applicazione potrebbe cioè essere concepita per segnalare all'utilizzatore, magari tramite posta elettronica, che un servizio è offline nel momento in cui si cerca di accedere ad esso.
In questa breve trattazione, vedremo come sia possibile effettuare basilari operazioni di scansione delle porte utilizzando dei semplici script realizzati in PHP.
Port Scanning e interazione con i servizi lato server
Uno script PHP viene eseguito all'interno di un Web server (per esempio Apache) e con esso interagisce, ma all'interno della macchina ospitante l'applicazione ha spesso la possibilità di funzionare in associazione ad altri servizi, come per esempio quelli relativi ad un Database Manager o ad un server di posta elettronica.
I servizi generalmente in funzione all'interno di un server destinato ad ospitare pagine Web, come per esempio HTTP, FTP o SMTP, sono in ascolto attraverso una determinata porta e la loro esecuzione potrà essere facilmente verificata attraverso di essa, se questa non restituisce alcuna risposta il servizio è infatti off line.
Una semplice verifica dello stato di una porta potrà essere effettuata tramite il test del servizio HTTP messo a disposizione da un Web server come Apache; a meno di personalizzazioni, di default questo servizio rimane in ascolto tramite la porta 80, quindi per verificarne il funzionamento sarà sufficiente richiamarlo attraverso il numero identificativo di quest'ultima: http://localhost:80
Questa elementare operazione di port scanning viene eseguita abbastanza frequentemente, quando per esempio si digita l'URL del proprio sito Web per sapere se è on line si esegue in pratica la stessa procedura.
PHP e funzioni per il port scanning
fsockopen()
è una funzione fornita nativamente da PHP per le operazioni di scansione delle porte, in particolare essa ha il compito di aprire una connessione ad un socket (connettore) relativo un dominio; nel caso in cui la chiamata non dovesse avere successo, la funzione restituirà FALSE
, inoltre è possibile introdurre degli argomenti opzionali, Errno
e Errstr
, che permettono di verificare gli errori prodotti a livello di sistema.
Gli argomenti Errno
e Errstr
indicano rispettivamente il numero dell'errore prodotto (il suo identificativo) e la stringa descrittiva dell'errore stesso; non sempre però capita che un tentativo di interrogazione di una porta produca un errore a causa di un malfunzionamento, se per esempio una pagina Web è offline più semplicemente l'interrogazione non riceverà una risposta e l'effetto conseguente sarà un timeout dell'applicazione che l'ha lanciata.
Per questo motivo è possibile passare un ulteriore argomento alla funzione fsockopen() che definisce la durata in secondi della chiamata alla porta, si tratta di un valore numerico che dovrà essere passato come ultimo parametro alla funzione.
Un esempio abbastanza semplice dell'utilizzo di fsockopen() potrebbe essere il seguente:
<?php $chiamata = @fsockopen("www.html.it",80, $errno, $errstr, 10); if ($chiamata) { echo "La porta 80 è aperta per www.html.it."; fclose($chiamata); } else { echo "$errstr ($errno)<br>n"; } ?>
Dato che per il dominio di HTML.it la porta 80 è aperta, la risposta alla chiamata effettuata dallo script sarà positiva, ma se si sostituisse al valore 80 il numero di un'altra porta (ad esempio: 81), si riceverebbe una risposta negativa dopo 10 secondi (valore di timeout passato alla funzione).
Nel caso in cui sia possibile stabilire la connessione al socket, per liberare risorse è bene che questa venga chiusa immediatamente dopo aver ricevuto l'esito della chiamata alla porta, per far questo deve essere introdotta la funzione fclose()
a cui è necessario passare come argomento la chiamata stessa; fclose()
non va utilizzata nel caso di risposta negativa da parte della porta perché questa funzione per non produrre errori deve agire su una connessione effettivamente aperta.
Non sempre si ha la possibilità di conoscere a priori quale sia la porta di ascolto per un determinato servizio; in questo caso è possibile sfruttare le potenzialità dei cicli utilizzando come condizioni di inizializzazione e di terminazione gli estremi di un determinato range di porte, come nell'esempio seguente:
<?php for($i=75;$i<=80;$i++) { $chiamata = @fsockopen("localhost", $i, $errno, $errstr, 3); if ($chiamata) { echo "La porta $i è aperta.n"; fclose($chiamata); }else{ echo "$errstr ($errno) per la porta $i.<br>n"; } } ?>
Per tutte le porte non aperte relativamente al dominio richiesto, lo script proposto produrrà una notifica (Errstr
) di errore come la seguente: "Impossibile stabilire la connessione. Risposta non corretta della parte connessa dopo l'intervallo di tempo oppure mancata risposta dall'host collegato.", ad ognuna di queste notifiche sarà associato uno stesso valore (10060) che è l'identificativo numerico dell'errore prodotto (Errno
).
Semplice esempio pratico di port scanning in PHP
I servizi attivi all'interno di un server Web utilizzano generalmente sempre le stesse porte per l'ascolto, di default infatti solitamente:
- 80 è la porta di ascolto per HTTP;
- 3306 è la porta per MySQL;
- 25 per SMTP;
- 110 per POP3;
- 143 per IMAP;
- 21 per FTP;
- 22 è la porta per il servizio SSH.
Basandosi su questi standard e sulla funzione fsockopen()
è possibile creare una semplice applicazione in grado di effettuare il port scanning per ricavare informazioni sullo status dei servizi appena elencati:
<?php # definizione della funzione per il port scanning function port_scanning($porta) { # introduzione della funzione per la chiamata alle porte $chiamata = @fsockopen("localhost", $porta, $errno, $errstr, 0.5); # valore di ritorno della funzione if ($chiamata) { # chiusura della connessione fclose($chiamata); return true; } } # definizione dell'array contenente i valori delle porte function server_info() { $info = array(); $servizi = array('21'=>'FTP', '22'=>'SSH', '25'=>'SMTP', '80'=>'HTTP', '110'=>'POP3', '143'=>'IMAP', '3306'=>'MySQL'); # esecuzione della funzione di scansione per ogni valore dell'array foreach ($servizi as $porta=>$servizi) { $info[$servizi] = port_scanning($porta); } # valore di ritorno return $info; } $info = server_info(); # visualizzazione dei valori di ritorno ?> <strong>FTP</strong>: <?php echo $info['FTP'] ? "On line" : "Off line"; ?> <br><strong>SSH</strong>: <?php echo $info['SSH'] ? "On line" : "Off line"; ?> <br><strong>SMTP</strong>: <?php echo $info['SMTP'] ? "On line" : "Off line"; ?> <br><strong>HTTP</strong>: <?php echo $info['HTTP'] ? "On line" : "Off line"; ?> <br><strong>POP3</strong>: <?php echo $info['POP3'] ? "On line" : "Off line"; ?> <br><strong>IMAP</strong>: <?php echo $info['IMAP'] ? "On line" : "Off line"; ?> <br><strong>MySQL</strong>: <?php echo $info['MySQL'] ? "On line" : "Off line"; ?>
L'applicazione creata funziona in modo molto semplice:
- viene creata una funzioncina basata sul costrutto di
fsockopen()
per effettuare la scansione delle porte relative ai servizi; - viene definito un array che ha per chiavi i numeri identificativi delle porte in ordine crescente e come valori i nomi dei servizi stessi;
- la funzione per il port scanning viene eseguita per ogni singola porta;
- viene definita una variabile array che ha come valori il risultato dell'applicazione della funzione per la scansione delle porte;
- vengono visualizzati i risultati della scansione mostrando per ognuno di essi lo status corrente;
Nella postazione in cui è stato testato lo script appena proposto erano attivi soltanto i servizi relativi al Web server e al DBMS, per cui l'output prodotto dal test delle porte è stato:
FTP: Off line
SSH: Off line
SMTP: Off line
HTTP: On line
POP3: Off line
IMAP: Off line
MySQL: On line
Conclusioni
Il port scanning è una procedura che non serve per valutare la funzionalità di uno script ma le caratteristiche dell'ambiente in cui un'applicazione dovrà funzionare; si tratta di una tecnica che potrà rivelarsi molto utile per adattare i propri codici sulla base delle caratteristiche del server ospitante e dei servizi che girano all'interno di esso.
In questa breve trattazione, sono state analizzate le funzioni messe a disposizione da PHP per le operazioni di scansione delle porte e sono stati proposti alcuni esempi pratici di script che potranno essere utilizzati per effettuare semplici operazioni di port scanning.