Per hardening di un server si intende il processo di miglioramento della sicurezza di un sistema per ridurre le sue vulnerabilità e quindi i rischi di attacchi. Questo processo prevede diverse pratiche di configurazione, soprattutto per i servizi essenziali, per minimizzare le vulnerabilità e aumentare la resistenza del server agli attacchi.
La guida che segue si concentra sulle principali attività di hardening per un server Linux (ad esempio Ubuntu o CentOS), ma molte delle pratiche possono essere applicate anche su altri sistemi operativi.
Inoltre, ogni passo di questa guida aiuta a ridurre i rischi di vulnerabilità, ma è essenziale rimanere aggiornati sulle ultime minacce e migliorare costantemente la sicurezza del sistema anche in base agli applicativi in uso.
È altresì importante assicurarsi di eseguire regolarmente audit di sicurezza, aggiornamenti e backup per mantenere il server protetto.
Di seguito le principali azioni consigliate:
Aggiornamenti e patch di sicurezza
Tra i fondamenti della sicurezza troviamo il mantenere un ambiente sempre aggiornato così da installare eventuali fix di sicurezza per bug noti.
È possibile impostare gli aggiornamenti automatici per l’intero sistema così da non dover procedere manualmente. Tuttavia, in ambienti di produzione può essere consigliabile verificare di volta in volta gli aggiornamenti disponibili prima di installarli, così da sapere cosa si va ad installare, facilitando anche eventuali azioni di troubleshooting post aggiornamento.
In linea generale, la regola fondamentale da tenere a mente è quella di installare solamente pacchetti di cui si ha realmente bisogno in quanto ogni applicativo presente sul server potrebbe essere una potenziale vulnerabilità.
Di seguito i comandi principali per verificare e installare gli aggiornamenti:
Ubuntu/Debian:
sudo apt update && sudo apt upgrade
sudo apt dist-upgrade
sudo apt autoremove
CentOS/RHEL:
sudo yum update
sudo yum autoremove
Gestione degli utenti e controllo accessi
Entrare in un server con utente root o con gli stessi privilegi non è assolutamente consigliato in quanto in caso di possibile compromissione anche i malintenzionati potrebbero avere pieno accesso alla macchina.
Per questo, si tende a disabilitare la possibilità di accedere come root anche se spesso si ha bisogno di avere gli stessi privilegi per effettuare determinate attività.
In questo caso possiamo creare una nuova utenza:
adduser [nome user] (Ubuntu/Debian)
useradd [nome user] (Red Hat/Centos)
per la quale andare a definire una password:
passwd [nome user]
In seguito è possibile aggiungere l’utenza appena creata nel gruppo sudo in modo da permettergli l’utilizzo dei privilegi di root quando necessario:
usermod -aG sudo [nome user]
Così facendo, si può accedere e autenticarsi con le credenziali di questa utenza nell’ambiente, per richiamare poi i comandi con i privilegi di root aggiungendo il sudo davanti ad ogni comando che lo richiede, esempio:
sudo adduser
Altro aspetto da controllare sono gli utenti che hanno accesso al sistema, verificabili con:
cat /etc/passwd
quindi è possibile rimuovere gli utenti che non sono più necessari:
sudo deluser nome_utente
Infine, controllare i permessi dei file e delle cartelle sensibili tramite il comando:
ls -l
e modificare i permessi se necessario.
Configurazione del firewall
Il firewall è una delle prime linee di difesa contro gli attacchi esterni. Per configurare un firewall su Linux è consigliabile utilizzare iptables o UFW configurando regole per consentire solamente il traffico necessario ai servizi in uso e bloccare quello indesiderato.
La maggior parte dei servizi principali, infatti, utilizzano porte di default che vanno aperte per un corretto funzionamento come ad esempio le porte 80 e 443 utilizzate dai protocolli http/https, quindi per rendere raggiungibili dall’esterno i siti web ospitati sulla macchina.
In termini di sicurezza solitamente si disabilita totalmente il traffico in ingresso tranne che per alcune specifiche porte necessarie. Quindi prima di tirare su le difese definiamo correttamente almeno la regola per poter accedere alla macchina in modo da non rimanere bloccati fuori dall’ambiente una volta chiuso il traffico in entrata.
In base alla tipologia di firewall in uso è possibile creare regole più o meno restrittive per abilitare/disabilitare il traffico su determinate porte o per specifici IP/classi di IP.
Di seguito un elenco dei comandi principali per la gestione del firewall tramite UFW o iptables:
UFW: Debian/Ubuntu
Attiva il firewall:
sudo ufw enable
Disattiva il firewall:
sudo ufw disable
Ripristina le regole di default, cancellando quelle esistenti:
sudo ufw reset
Blocca tutto il traffico in ingresso:
sudo ufw default deny incoming
Consente tutto il traffico in uscita:
sudo ufw default allow outgoing
Consente SSH (porta 22 TCP di default):
sudo ufw allow ssh
Consente HTTP (porta 80 TCP):
sudo ufw allow 80/tcp
Consente HTTPS (porta 443 TCP):
sudo ufw allow 443/tcp
Blocca la porta 23 (Telnet):
sudo ufw deny 23
Blocca la porta 23 solo in TCP:
sudo ufw deny 23/tcp
Consente DNS su UDP porta 53:
sudo ufw allow 53/udp
Consente tutto il traffico da IP specifico:
sudo ufw allow from 192.168.1.100
Consente SSH solo da subnet 192.168.1.0/24:
sudo ufw allow from 192.168.1.0/24 to any port 22
Blocca tutto il traffico da una subnet:
sudo ufw deny from 203.0.113.0/24
Consente traffico TCP per un range di porte:
sudo ufw allow 5000:5010/tcp
Visualizza regole in modo sintetico:
sudo ufw status
Visualizza regole con numerazione utile per cancellazioni:
sudo ufw status numbered
Rimuove una regola specifica:
sudo ufw delete allow 22/tcp
Rimuove la regola numero 3 (utilizzando numero da `status numbered`):
sudo ufw delete 3
Iptables: Red Hat/Centos
Abilitare il servizio iptables per l’avvio automatico:
sudo systemctl enable iptables
sudo systemctl start iptables
Mostra le regole attive con dettagli, pacchetti e byte conteggiati:
sudo iptables -L -v -n
Salvare le regole per renderle persistenti al riavvio:
sudo service iptables save
Blocca tutto il traffico in ingresso di default:
sudo iptables -P INPUT DROP
Consente tutto il traffico in uscita di default:
sudo iptables -P OUTPUT ACCEPT
Blocca traffico inoltrato di default:
sudo iptables -P FORWARD DROP
Consente traffico specifico ad es. SSH:
sudo iptables -A INPUT -p tcp --dport 22 -j ACCEPT
Consentire traffico su porta specifica ad es. HTTP (80):
sudo iptables -A INPUT -p tcp --dport 80 -j ACCEPT
Bloccare traffico da IP specifico:
sudo iptables -A INPUT -s 192.168.1.10 -j DROP
Rimuovere una regola specifica(dopo averla trovata nella lista numerata, ad esempio la regola 3):
sudo iptables -D INPUT 3
Resettare tutte le regole iptables:
sudo iptables -F
Limitare il numero di connessioni simultanee (per esempio su porta 80):
sudo iptables -A INPUT -p tcp --dport 80 -m connlimit --connlimit-above 10 -j REJECT
Si rimanda sempre alla man page dei relativi servizi (man ufw/man iptables) per avere una visione completa delle possibili personalizzazioni delle rules.
Disabilitare i servizi inutilizzati
I servizi presenti sul server ma non necessari possono essere una porta aperta per gli utenti non autorizzati. Procedi quindi a disabilitare tutti i servizi e i demoni non essenziali consentendo l’esecuzione solo di quei programmi di cui si necessita.
È possibile verificare quali servizi sono in esecuzione su quali porte tramite il comando:
netstat -npl
Inoltre, per i sistemi che eseguono systemd è possibile eseguire il comando:
systemctl list-units --type=service
per ottenere una lista di tutti i servizi in esecuzione.
Nel caso siano presenti servizi di cui non si ha bisogno è possibile disabilitarli tramite il comando:
sudo systemctl disable nome-servizio
sudo systemctl stop nome-servizio
Configurazione di SSH
SSH è uno dei punti più vulnerabili di un server, quindi è fondamentale configurarlo correttamente per evitare attacchi di tipo brute force.
Il file di configurazione da modificare si trova al path /etc/ssh/sshd_config e di seguito le modifiche consigliate:
Disabilitare il login come root tramite SSH impostando:
PermitRootLogin no
ricordarsi di verificare che sia stato già creato un altro utente con il quale si riesce correttamente ad accedere al sistema come detto in precedenza.
Personalizzare la porta in uso dal servizio SSH modificando la riga:
#Port 22
basterà rimuovere il commento alla riga cancellando # e modificare il numero della porta con quello desiderato.
Anche in questo caso è necessario fare attenzione che la porta che si va ad impostare sia abilitata in ingresso sul firewall al fine di evitare l’impossibilità di accedere alla macchina.
Disabilitare l’autenticazione con password modificando la relativa riga come segue:
PasswordAuthentication no
Disabilitando tale opzione sarà possibile utilizzare altri metodi di autenticazione, il consigliato tramite chiavi SSH.
Usa l’autenticazione tramite chiavi SSH anziché password come accennato in precedenza.
È necessario generare in precedenza una chiave SSH sul dispositivo locale tramite il comando:
ssh-keygen -t rsa -b 4096
Successivamente sarà necessario digitare la posizione del file in cui salvare la chiave privata. Non scrivendo niente verrà salvato nel path di default:
/home/$user/.ssh/id_rsa
La chiave pubblica sarà salvata nella stessa posizione, sotto lo stesso nome, ma con l’estensione .pub.
Verrà così generata una coppia di chiavi, una pubblica e una privata per l’utente.
In seguito copiare solo la chiave pubblica sul server:
ssh-copy-id user@server_ip
mentre la chiave privata rimane sul proprio dispositivo.
Poi, riavviare SSH per rendere effettive le modifiche apportate:
sudo systemctl restart sshd
Per una maggiore sicurezza occorre limitare gli accessi a SSH da indirizzi IP specifici tramite regole del firewall come spiegato precedentemente.
Backup e gestione dei log
I backup regolari sono essenziali per garantire il recupero dei dati in caso di attacco.
È consigliabile quindi eseguire backup regolari dei dati possibilmente salvati su servizi terzi rispetto il server per una migliore ridondanza dei dati.
Inoltre, la gestione dei log è importante per monitorare attività sospette. Per ogni servizio presente sul server è possibile configurare il path in cui trovare il file di log e anche la verbosità di scrittura, cioè il livello di dettaglio delle informazioni scritte nel file di log.
I livelli di verbosità che solitamente possono essere configurati sono:
TRACE (o DEBUG): informazioni dettagliate, passo passo, sull’esecuzione del codice.
INFO: messaggi informativi su operazioni normali, come l’avvio e la chiusura di servizi.
WARN: avvisi su situazioni non ottimali ma non critiche.
ERROR: segnalazioni di errori che richiedono attenzione.
FATAL (o CRITICAL): errori gravi che portano all’interruzione del sistema.
La scelta del livello di verbosità del logging è una decisione strategica che dipende dalle esigenze specifiche del sistema e dal contesto in cui viene utilizzato.
Prendendo ad esempio la navigazione di un sito web e i possibili log relativi alle attività del web server, un logging verboso potrebbe includere dettagli su ogni richiesta HTTP, ogni accesso al database e ogni operazione di file. Un logging meno verboso potrebbe limitarsi a registrare gli errori di autenticazione, gli errori SQL e gli accessi che hanno generato errori 500.