La mia esperienza con SPID e Poste Italiane

Questa settimana ho deciso di attivare lo SPID (Sistema Pubblico di Identità Digitale).

Cosa è lo SPID

L’identità digitale SPID è rappresentata da un username e una password che vi permettono di autenticarvi sui siti della Pubblica Amministrazione (PA). I suoi usi sono molteplici e sta prendendo sempre più piede per le comunicazioni online tra cittadino e PA.

Cosa serve per ottenere le credenziali SPID

I requisiti per fare domanda per lo SPID sono, per i cittadini italiani:

  • Essere maggiorenni
  • Indirizzo email
  • Numero di telefono cellulare abilitato alla ricezione SMS
  • Documento di identità
  • Tessera sanitaria

La documentazione ufficiale si trova sul sito governativo di SPID.

I providers

Sul sito governativo di SPID, si trova anche (a fine pagina) la lista dei provider che offrono la possibilità di ottenere un’identità SPID, con una serie di funzionalità offerte (o meno) che caratterizzano ogni provider.

La scelta del provider SPID è importante: a seconda del provider scelto si devono sostenere dei costi; sempre a seconda del provider, la fase di identificazione varia: da remoto oppure di persona.

Un passo indietro: la fase di identificazione

Una volta scelto un provider ed essersi registrati sul sito dello stesso per l’ottenimento delle credenziali SPID, è necessaria una fase di identificazione in cui il provider attesta che chi fa richiesta di SPID sia effettivamente la persona in questione. Questa fase è svolta da un umano e può essere svolta di persona [vi recate in un’agenzia del provider scelto oppure a domicilio] oppure da remoto [via webcam].

La scelta del provider

Facendo un po’ di ricerche online, la mia selezione tra i provider disponibili ha portato alla seguente shortlist:

  • Poste Italiane [identificazione di persona]
  • Sielte [identificazione remota]
  • TIM [identificazione remota]

In un momento di ricerca successivo, ho deciso di scegliere Poste Italiane [PosteID] per:

  • condizioni contrattuali
  • politica di gestione dei dati personali
  • supporto a SPID3 [SPID offre tre livelli di sicurezza, e solo alcuni provider supportano fino al terzo livello]

NOTA: come si dice in gergo, your mileage may vary: alcuni provider offrono anche autenticazione remota tramite smart card o altri meccanismi di autenticazione. Il provider che ho scelto io potrebbe non essere l’opzione più conveniente per chi legge.

La mia esperienza con Poste Italiane: più che positiva

Mi sono registrato sul sito di Poste Italiane [PosteID], ho letto le condizioni contrattuali e ho fornito una copia dei miei documenti. Seguendo le istruzioni ho prenotato un appuntamento per il giorno successivo presso un’agenzia di Poste Italiane.

Arrivato in agenzia, ho fatto la scansione del QR code fornito per l’appuntamento all’apposito totem; dopo 30 secondi, sono stato servito. L’impiegato ha verificato i miei documenti e che fossi il titolare della domanda di SPID. Nessun problema riscontrato.

Una volta che la mia domanda è stata accolta, la mia identità SPID è diventata subito attiva. Ho colto l’occasione per provarla sul sito di Poste e su quello della Regione Lombardia.

Esperienza più che positiva.

Secure your SSH server against brute-force attacks with Fail2ban

The problem: SSH can be brute-forced

I usually leave an SSH server on a dedicated port on every server I administer and, as you may recall, I even linked two well-written guides to properly configure and harden SSH services.

Now, Internet is a notoriously bad place: scanners and exploiters have always been there, but brute-forcers are on the rise and SSH is one of the services that is heavily targeted by them. Let’s gather some data:

quebec:/var/log # ag "Invalid user" auth.log.2 auth.log.3 | wc -l
4560
quebec:/var/log # head -n 1 auth.log.3 | cut -d " " -f 1-3
May  8 07:39:01
quebec:/var/log # tail -n 1 auth.log.2 | cut -d " " -f 1-3
May 21 07:39:01

So, even if my SSH is allowing only PubkeyAuthentication, in the timespan of two weeks there has been 4560 brute-force attemps (~325 attempts per day).

This is annoying and potentially insecure, depending on your configuration. What can we do?

The solution: using Fail2ban

I have recently read some posts about this problem, and luckily (for us) there are multiple solutions to this problem: the most popular one is Fail2ban. To cut a long story short, the idea behind Fail2ban is to monitor log files of the monitored services and keep track of which IP addresses are trying to use a brute-force attack to use the service. If the same IP address causes a number of bad events in the specified time frame, Fail2ban bans that IP (using netfilter/iptables) for a configure d time amount.

So I just need to install Fail2ban and I am ready to go:

# zypper in fail2ban

NO. NO. NO. You will not be protected against brute-force attacks if you just install it without configuring it.

You MUST configure it!

Let’s take a step back. The core of Fail2ban is the configuration:

# "bantime" is the number of seconds that a host is banned.
bantime  = 600

# A host is banned if it has generated "maxretry" during the last "findtime"
# seconds.
findtime = 600
maxretry = 5

These are the default values. And brute-forcers know them, so they can time accordingly their attempts not to break these limits (or to begin again their attempts after the bantime). Again, let’s gather some data. I noticed a frequent brute-forcer IP and follow his data:

quebec:/var/log # ag "Invalid user" auth.log.2 auth.log.3 | ag <IP> | cut -d " " -f 1-3

auth.log.2:8106:May 21 03:34:22
auth.log.2:8112:May 21 03:43:41
auth.log.2:8116:May 21 03:53:00
auth.log.2:8120:May 21 04:02:18
auth.log.2:8126:May 21 04:11:47
auth.log.2:8132:May 21 04:21:08

Can you believe it? It was just on the edge of the 600 seconds between every attempt!

Key concept: outsmarting brute-forcers

The key concept here is to provision a personalized version of your specific choosing of these values, in order to outsmart the brute-forcers (which is easily done). In order to do so, do not modify Fail2ban’s default config file (/etc/fail2ban/fail2ban.conf) but rather just override the defaults in another config file that you can create with:

quebec:/etc/jail2ban # awk '{ printf "# "; print; }' /etc/fail2ban/jail.conf | sudo tee /etc/fail2ban/jail.local

Your customizations have to be defined in /etc/fail2ban/jail.local, so your selected bantime, findtime and maxretry must go there.

Further customization

Some useful findings:

  • you can selectively whitelist a group of IPs, hosts, subnets, etc. in order to not being banned when accessing services from whitelisted IPs (e.g. ignoreip = <VPN subnet>/8 <another VPN subnet>/16)
  • you can receive an email everytime someone is banned with their whois (with good relief from the system administrator)
  • if you are using WordPress, you can extend Fail2ban to monitor failed WordPress login attempts with a WordPress plugin (did I mention that Fail2ban not only monitors sshd? It also monitors nginx, apache, vsftpd and other services)
  • you can have Fail2ban automatically sends the banned IPs to a shared blacklist (I have never used this)

Disadvantages

Everything seems perfect now, but what are the disadvantages of it? Given that Fail2ban reasons in terms of banning single IPs, it cannot protect you against distributed brute-force attacks. In this case Fail2ban is pretty useless and other solutions should be implemented that depends from case to case.

Fun with Python powered telnetd honeypot

Reason: hardening, serendipity and curiosity

As you already know, in the past weeks I hardened all of my boxes: while doing it, I flushed all iptables/ipfw rules, changed the default policy to DROP and take it from there to enable every rule as soon as I need it. Whilst Ubuntu uses ufw as a fronted for iptables, Fedora uses firewalld.

After setting up UFW, by looking at journalctl I can see all rejected connections, like:

Sep 03 11:54:16 -- kernel: [UFW BLOCK] IN=eth0 OUT= MAC=XX SRC=XX DST=XX LEN=60 TOS=0x00 PREC=0x00 TTL=51 ID=24286 DF PROTO=TCP SPT=36864 DPT=23 WINDOW=5808 RES=0x00 SYN URGP=0

As we all know, Internet is a wild place. In this particular case, someone was trying to connect to my box on port 23/tcp (telnetd, is still there someone who uses it?).

My curiosity begin to increase. Let’s have a look on which ports I rejected most of the connections:

/var/log # zgrep -ho "DPT=\w*" syslog* | cut -d "=" -f 2 | sort | uniq -c | sort -nr | head -n 10
    # no. of attempts | port
   1009 23
    969 3128
    330 22
    248 8080
    168 80
    158 5060
    151 3389
    111 1825
     94 1433
     77 123

(as I am running an httpd-ssl server on this host, connections to 443/tcp port are not showed).

Idea

Ok, so mostly of the wild connectors out there try to telnetd to my host (and all other hosts on the Internet). Again, my curiosity prevails: how about a fake telnetd server written in Python with twisted? As I always wanted to have a look with twisted, seems a good idea. The main idea here is to write a simple telnetd server that:

  • shows the root prompt and waits for commands
  • answers with fake answers to some UNIX commands (like uname and id)
  • answers to all other commands with Command not found or with nothing (like that the command was accepted). The choice between answering in the former way rather than the latter is random
  • record everything that is written by the attacker

This is a very simple and diminished concept of an Honeypot and leads to the code I published the code on GitHub under telnetd_honeypot.

Just for the sake of increased security, I did not want to run this program as root (not because I do not trust it, just because it’s a bad practice!). But we need to run it as root, because we need superuser powers to open a listening socket on all ports <1024, especially 23/tcp.

Sounds like a dead end? Not at all. Meet rinetd. We set up a bounce redirection from 23/tcp to the port on which our basic honeypot is listening (8023/tcp), launched by an unprivileged user (again: for security. Feel free to spin up a Docker container just to launch the honeypot).

Demo

% python telnetd.py &
[1] 15171
% telnet localhost 8023
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
/ # uname -a
Linux f001 3.13.3-7-high-octane-fueled #3000-LPG SMPx4 Fri Jun 31 25:24:23 UTC 2200 x86_64 x64_86 x13_37 GNU/Linux
/ # id
uid=0(root) gid=0(root) groups=0(root)
/ # ls
/ # ps auxw
bash: ps: command not found

Results

The results are quite fascinating. By analizing our file produced by our honeypot runned just for 3 days we can see the most typed commands on the other side of the client:

~ % cut -d " " -f 6- telnetd_honeypot.log | sort | uniq -c | sort -nr | head -n 3  
921 sh
918 /bin/busybox ZORRO
879 root

We might notice that most of the lines are common passwords guessing, while the busybox invocation with the parameter ZORRO is mysterious: even Googling, I have no clues about it. Sounds like a custom firmware? There are also multiple attempt to inject a shellcode (which I am not publishing for obvious reasons).

There are also funny entries:

08/20/2015 12:43:10 AM INFO: 79.x.x.x knock knock neo

Anyone else has seen Neo?

The results are pretty interesting and deserve a further investigation. Upon running just for 3 days, I gather enough results to satisfy my curiosity. The honeypot described here is just a starting point, hoping to gather more contributors to this fascinating problem. With the addition of Docker containers, the future possibilities are quite infinite. Happy investigating!

Hardening services: let’s review our config files

It’s hardening Sunday here: I reviewed the config files of my main daemons (nginx, openvpn, tinc, sshd) with the help of two resources that I want to share with you, fellow readers.

First of all, a guide dedicated exclusively to hardening ssh: from using public key authentication only (I strictly encourage it!) to the selection of which ciphers ssh should use (there is theory behind, so read it!).

The second guide, is a guide for hardening all services, from web servers to VPN concentrators (divided by vendor): a worth reading guide. Every option is very well detailed and discussed, for all you nitpickers like me.

So, take aside 2 hours, read the theory, then adopt the changes you think would benefit your setup. Happy hardening!

Update: if you are using OSX, do not use default ssh toolset that ships with OSX: it is not updated and it does not have ssh-copy-id to distribute your public key among your ssh servers. More that that, OSX default ssh does not support ecdsa which is the main crypto algorithm that the linked guides are using.
Solution: brew install homebrew/dupes/openssh and adjust your PATH accordingly.

Ruby e OSX: problemi coi certificati SSL durante l’installazione delle gem

Nella nuova versione dell’installer di RubyGems è presente un check di sicurezza sul certificato SSL del sito da cui si scaricano le gem che si stanno per installare.
Questo può comportare un errore durante l’installazione di una qualsiasi gem:

ERROR: Could not find a valid gem 'rails' (= 3.2.14), here is why: Unable to download data from https://rubygems.org/ - SSL_connect returned=1 errno=0 state=SSLv3 read server certificate B: certificate verify failed

Sono disponibili due strade per risolvere il problema:

  • creare un file ~/.gemrc contentente :ssl_verify_mode: 0. Questa soluzione, oltre a non essere dipendente dall’utente con cui si installano le gems, è fortemente sconsigliata: infatti, disabilita il controllo del certificato SSL del sito da cui si prelevano le gems, con immaginabili problematiche di security. Usate questa soluzione solo in ambiente di test.
  • importare il certificato system-wide:
    	cert_file=$(ruby -ropenssl -e 'puts OpenSSL::X509::DEFAULT_CERT_FILE')
    security find-certificate -a -p /Library/Keychains/System.keychain > "$cert_file"
    security find-certificate -a -p /System/Library/Keychains/SystemRootCertificates.keychain >> "$cert_file"

    	

HTTPS e le applicazioni di terze parti: attenzione!

“È sufficiente usare HTTPS per essere sicuri: protegge la comunicazione cifrando il traffico e usando certificati validati da CA riconosciute”.

SBAGLIATO. Spesso si sente pronunciare questa frase, ma non è del tutto vero: ho recentemente letto con molta attenzione un paper presentato alla conferenza CCS 2012, una conferenza dedicata alla Computer Security. Il paper ha un titolo curioso: “The Most Dangerous Code in the World: Validating SSL Certificates in Non-Browser Software“.

Si parla di HTTPS/SSL in contesti in cui è richiesta una comunicazione cifrata tra due sistemi (trasmissione di dati sensibili); distinguiamo due tecnologie lato end-user per accedere a sistemi HTTPS: un browser (accediamo, per esempio, ad un sito di e-commerce e facciamo il checkout in modalità protetta) oppure un’applicazione 3rd party (per esempio, lo stesso sito di e-commerce tramite un’app dedicata a smartphone/tablets).

Bene: i browser non sono in discussione e non risentono dei problemi presentati nel paper. Invece, le libraries SSL (due nomi su tutti: OpenSSL e JSSE) che solitamente vengono utilizzate nei back-end o utilizzate dagli sviluppatori per costruire applicazioni che accedono a risorse tramite HTTPS soffrono di alcuni problemi che rendono l’intera comunicazione vulnerabile a diversi tipi di attacchi, come il famoso man-in-the-middle. In breve, il problema principale è il processo di validazione dei certificati SSL (solitamente: si risale la chain fino alla root CA). Se lato client non viene effettivamente fatta la validazione del certificato presentato dal server, il client è una facile preda! Questa non-validazione è causata da molteplici fattori:

  • pigrizia dei developers che, nell’utilizzare queste librerie, disabilitano controlli fondamentali durante la fase di sviluppo (perché si usano dei certificati self-signed) che poi dovrebbero essere riabilitati al go-live. Ma così non accade;
  • utilizzo sbagliato delle API che settano impostazioni non congruenti (setto il check per la verifica del certificato ma poi non ricordo/non so/non è specificato che devo leggere un’ulteriore return code per verificare se la validazione è andata a buon fine).

Nel paper sono presenti alcuni esempi interessanti con tanto di codice reverse engineered.

Quali sono i rimedi?

  • Come sviluppatori: configurare correttamente i parametri di validazione SSL della libreria che si usa, controllando anche i valori di ritorno della validazione se devono essere richiamati tramite un’ulteriore chiamata alle API della library utilizzata
  • Come sviluppatori: fare sempre un testing utilizzando certificati SSL non verificati.

La lettura del paper è davvero molto interessante ed è vivamente consigliata per tutti quelli che hanno a che fare con lo sviluppo di applicazioni 3rd party che accedono a risorse esposte tramite HTTPS/SSL.

WordPress plugin: Chap Secure Login

Ho recentemente installato Chap Secure Login, un plugin molto utile per WordPress che risolve un problema da non sottovalutare: l’autenticazione su un canale non cifrato quale HTTP (è buona norma usare HTTPS quando si effettua uno scambio di informazioni riservate, quali password, numeri di carta di credito, etc. per evitare che eventuali eavesdropper possano carpire le informazioni scambiate ed utilizzarle a vostro svantaggio).

Essendo il problema molto interessante, e risolto dal plugin in modo brillante, ho deciso di interessarmi al suo funzionamento. Partiamo dal presupposto: dovete fare un login tramite HTTP e non volete che la vostra password passi in chiaro. Come procedere?

Il plugin modifica il processo di login, trasmettendo in chiaro (tra server e client) solo l’username, mentre per la password adotta un meccanismo CHAP per trasmettere in modo sicuro la password (in particolare: il processo di login andrà in errore la prima volta, mentre la seconda funzionerà correttamente). In particolare, la login si traduce prima in uno scambio di chiave di sessione al primo login, dopodiché il client userà la chiave segreta per cifrare la password e trasmettere il risultato al server. A questo punto il server confronta il valore ottenuto con la password dell’utente, e procederà all’autenticazione se la password è corretta.

Facebook e la navigazione HTTPS da abilitare

Mi sono accorto che Facebook offre un’opzione molto utile e assolutamente da abilitare. Infatti, sotto Account > Impostazioni Account > Protezione dell’Account troviamo un’opzione per abilitare la navigazione HTTPS quando possibile.

 

Io l’ho abilitata subito, per proteggermi da sniffer indiscreti durante la mia navigazione su Facebook, e consiglio di abilitarla anche a voi. Personalmente, ho sempre percepito la necessità di un’opzione del genere: il login in plain HTTP presente su Facebook mi ha sempre lasciato un po’ perplesso.

Apache: come nascondere il banner contenente la versione, il packaging, etc. [ServerTokens]

Ispirato dalla filosofia “security by obscurity” [sicurezza tramite segretezza], ho sempre avuto l’abitudine di limitare le informazioni rivelate dai server Apache che amministro. Solitamente (= di default), Apache rivela le seguenti informazioni:

  • Versione, major build [1, 2]
  • Packaging [Ubuntu, Fedora, etc.]
  • Eventuali moduli installati [mod_security, mod_python, etc.]
  • Versione estesa [2.0.47]

Un po’ troppe per i miei gusti… Quindi limito la quantità di informazioni che Apache rivela al mondo in questo modo [istruzioni personalizzate per Ubuntu]:

  • Diventando root, modificate il file /etc/apache2/conf.d/security
  • Andate alla riga ServerTokens: # ServerTokens
    # This directive configures what you return as the Server HTTP response
    # Header. The default is 'Full' which sends information about the OS-Type
    # and compiled in modules.
    # Set to one of:  Full | OS | Minimal | Minor | Major | Prod
    # where Full conveys the most information, and Prod the least.
    #
    ServerTokens Minimal

    Come vi viene spiegato, potete scegliere il livello di granularità delle informazioni rivelate, andando da Full (che rivela tutte le informazioni possibili) fino a Prod, che non rivela quasi nulla (solo che usate Apache). Sui miei server, di solito, scelgo una via di mezzo e di conseguenza imposto ServerTokens a Minimal.

  • Salvate e riavviate Apache: sudo /etc/init.d/apache2 reload