Per poter attivare il protocollo HTTPS e quindi ricorrere alla suite di protocolli TLS/SSL è necessario innanzi tutto disporre di un opportuno certificato X.509. Un articolo dettagliato che illustra il processo necessario per ottenere un certificato è disponibile qui.
Configurazione
L'attivazione di HTTPS per un blocco server avviene specificando il parametro "ssl" per la direttiva listen. Ad esempio:
# ...
server {
listen 443 ssl;
#...
}
Inoltre, è necessario specificare il percorso del certificato e della relativa chiave privata. Ricordiamo infatti che i dati contenuti nel file certificato sono pubblici, ma affinchè il server possa fare uso di TLS/SSL ed autenticarsi presso i client ha la necessità di accedere anche alla chiave privata corrispondente. Come suggerito dal nome, la chiave privata dovrebbe essere memorizzata in un file con accesso riservato. Un attaccante che disponga di entrambi i file può infatti spacciarsi per il server autentico.
I percorsi dei file certificato e chiave privata sono specificati dalle direttive ssl_certificate e ssl_certificate_key, anch'esse contenute nel blocco server:
# ...
server {
listen 443 ssl;
server_name www.miosito.com;
ssl_certificate miosito.com.crt;
ssl_certificate_key miosito.com.key;
#...
}
In alcuni casi è possibile che sia il certificato che la chiave privata vengano distribuiti dalla CA nello stesso file (in genere in formato PEM). In questo caso, è sufficiente specificare lo stesso nome di file per entrambe le direttive:
# ...
server {
listen 443 ssl;
server_name www.miosito.com;
ssl_certificate miosito.com.crt;
ssl_certificate_key miosito.com.crt;
#...
}
Per ragioni di sicurezza si può voler configurare il server Nginx per supportare solo alcuni tra i possibili cifrari e protocolli inclusi in TLS/SSL. Si pensi ad esempio al caso in cui una vulnerabilità 0-day venga resa nota. Una possibile contromisura in attesa di una patch potrebbe essere quella di disabilitare i protocolli affetti.
Per specificare i protocolli da utilizzare in HTTPS si ricorre alla direttiva ssl_protocols. Ad esempio, il blocco seguente abilita solo TLS 1.1 e 1.2, disabilitando TLS 1.0 e TLS 1.3 (laddove supportato):
# ...
server {
listen 443 ssl;
server_name www.miosito.com;
ssl_certificate miosito.com.crt;
ssl_certificate_key miosito.com.crt;
ssl_protocols TLSv1.1 TLSv1.2;
}
Analogamente, è possibile specificare gli algoritmi di cifratura da utilizzare con la direttiva ssl_ciphers. Ad esempio:
# ...
server {
listen 443 ssl;
server_name www.miosito.com;
ssl_certificate miosito.com.crt;
ssl_certificate_key miosito.com.crt;
ssl_protocols TLSv1.1 TLSv1.2;
ssl_ciphers HIGH:!aNULL:!MD5;
}
Il formato utilizzato dalla direttiva ssl_ciphers è lo stesso utilizzato dalla libreria openssl, dato che essa è alla base dell'implementazione di HTTPS in Nginx. L'elenco dei cifrari supportati può essere mostrato utilizzando il comando di shell "openssl ciphers".
Per maggiori informazioni sui cifrari attivi per default consigliamo di consultare la documentazione ufficiale di Nginx, disponibile qui.
Ottimizzazioni
L'utilizzo di HTTPS richiede maggiori risorse computazionali rispetto ad HTTP, non solo perché il traffico deve essere cifrato prima di essere inviato al client, ma soprattutto per la fase di handshake che come sappiamo, ricorre alla crittografia asimmetrica. Pertanto, una prima semplice ottimizzazione consigliata consiste nell'attivare il keepalive per il proprio server HTTPS. In questo modo, i client cercheranno di riutilizzare le connessioni TCP già instaurate per effettuare richieste successive, invece di aprire una nuova connessione per ogni richiesta. Ciò consente di minimizzare il numero di handshake necessari, riducendo l'impatto sulla CPU del server.
Inoltre, è possibile agire sui parametri che regolano il comportamento della cache delle sessioni di Nginx. La cache delle sessioni contiene i parametri TLS/SSL prodotti dagli ultimi handshake. Grazie ad essa, è possibile velocizzare la fase di handshake riutilizzando i parametri già negoziati in precedenza dagli stessi client, invece di ripetere un handshake completo ogni volta. Nginx permette di regolare sia la dimensione della cache in MB che il timeout (tempo trascorso il quale una sessione viene rimossa dalla cache), utilizzando rispettivamente le direttive ssl_session_cache ed ssl_session_timeout del blocco http.
Ad esempio, per impostare la cache delle sessioni a 10 MB, con un timeout di sessione di 10 minuti, si può configurare il blocco http come segue:
# ...
http {
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
server {
listen 443 ssl;
server_name www.miosito.com;
keepalive_timeout 70;
ssl_certificate miosito.com.crt;
ssl_certificate_key miosito.com.crt;
ssl_protocols TLSv1.1 TLSv1.2;
ssl_ciphers HIGH:!aNULL:!MD5;
# ...
}
# ...
}
Utilizzare sia HTTP che HTTPS
Sebbene l'utilizzo di HTTP sia ormai fortemente sconsigliato in favore di una massiccia adozione di HTTPS, vi sono dei casi in cui può essere necessario attivare entrambi i protocolli. A tal scopo, è sufficiente aggiungere una seconda direttiva listen, specificando la porta 80/TCP (HTTP) invece della 443/TCP (HTTPS). Ad esempio:
# ...
server {
listen 80;
listen 443 ssl;
server_name www.miosito.com;
ssl_certificate miosito.com.crt;
ssl_certificate_key miosito.com.crt;
ssl_protocols TLSv1.1 TLSv1.2;
ssl_ciphers HIGH:!aNULL:!MD5;
}
Certificati e virtual host name based
Un problema noto nell'utilizzo di HTTPS con i virtual host name based "affligge" anche Nginx. Nel caso in cui siano presenti più blocchi server, seppur configurati opportunamente ciascuno con il proprio certificato, si verifica infatti un comportamento inatteso: i client ricevono sempre lo stesso certificato, a prescindere dal sito richiesto, causando il fallimento dell'autenticazione del server.
Ad esempio, si supponga di aver configurato il server nel modo seguente:
# ...
server {
listen 443 ssl;
server_name www.miosito.com;
ssl_certificate www.miosito.com.crt;
# ...
}
# ...
server {
listen 443 ssl;
server_name www.miosito.org;
ssl_certificate www.miosito.org.crt;
# ...
}
Collegandosi al sito miosito.org, il client riceverà comunque il certificato associato a miosito.com, che chiaramente risulterà invalido in quanto assegnato ad un dominio diverso. Il problema, risiede nel fatto che HTTP gira "sopra" TLS/SSL: affinchè il client possa richiedere un host in particolare (usando l'header HTTP Host, per l'appunto), la connessione TLS/SSL deve essere stabilita. Tuttavia, per stabilire la connessione è necessario effettuare un handshake e quindi ricorrere ad un certificato. Nginx in questo caso seleziona quello di default, ovvero il primo specificato.
L'approccio più comunemente adottato in passato per la risoluzione del problema consiste nell'utilizzo di virtual host IP-based invece che name based: si assegna ad ogni sito un indirizzo IP diverso. I nomi di dominio associati ai due siti verranno risolti rispettivamente sui due IP, così da superare l'ambiguità nell'individuazione del sito richiesto ancor prima di aver effettuato l'handshake TLS/SSL.
Esistono altre soluzioni, largamente utilizzate specialmente nell'ambito dell'hosting condiviso, laddove l'assegnazione di un indirizzo IP dedicato per ogni sito ospitato potrebbe non essere possibile. La prima consiste nell'utilizzare un certificato con più nomi host nel campo SubjectAltName. Questo permette di utilizzare lo stesso certificato per più host.
La soluzione oggi più adottata consiste nel superare il problema alla radice, ovvero consentendo al client di specificare l'host del sito al quale collegarsi durante l'handshake. Ciò è possibile grazie all'estensione del protocollo TLS nota come SNI, ovvero Server name Indication Extension. La SNI è disponibile in Nginx a partire dalla versione 0.8.21 ed è largamente supportata da browser e librerie HTTP da diversi anni.