{"id":277,"date":"2026-06-20T20:36:52","date_gmt":"2026-06-20T20:36:52","guid":{"rendered":"https:\/\/shattered.io\/it\/2026\/06\/20\/ssh-keygen-gestione-chiavi-tutorial\/"},"modified":"2026-06-20T20:38:30","modified_gmt":"2026-06-20T20:38:30","slug":"ssh-keygen-gestione-chiavi-tutorial","status":"publish","type":"post","link":"https:\/\/shattered.io\/it\/2026\/06\/20\/ssh-keygen-gestione-chiavi-tutorial\/","title":{"rendered":"ssh-keygen: Gestire le Chiavi SSH in 12 Step [2026]"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">Ogni anno, migliaia di server vengono compromessi a causa di chiavi SSH mal gestite: chiavi private senza passphrase, algoritmi obsoleti come RSA a 1024 bit rimasti attivi per anni, agent forwarding abilitato su host non fidati. Il protocollo SSH \u00e8 crittograficamente solido, ma la sua sicurezza dipende completamente da come si generano, distribuiscono e ruotano le chiavi. Questa guida ti insegna a usare <strong>ssh-keygen<\/strong> nel modo corretto, dalla scelta dell&#8217;algoritmo alla rotazione periodica, con 12 step pratici e tutti i comandi necessari per avere un setup SSH hardened in 30 minuti.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"perche-la-gestione-delle-chiavi-ssh-e-critica-nel-2026\">Perch\u00e9 la Gestione delle Chiavi SSH \u00e8 Critica nel 2026<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">OpenSSH 10.0, rilasciato il 9 aprile 2025, ha introdotto il key exchange post-quantistico ibrido <code>mlkem768x25519-sha256<\/code> come predefinito per le connessioni client. Questo segnala una svolta epocale nella sicurezza SSH: il protocollo si adatta alla minaccia dei computer quantistici, ma le vulnerabilit\u00e0 legate alla cattiva gestione delle chiavi restano umane, non tecnologiche.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Nel febbraio 2025, il MITRE ha registrato due vulnerabilit\u00e0 critiche in OpenSSH: <strong>CVE-2025-26465<\/strong> affliggeva il client dalla versione 6.8p1 alla 9.9p1 e permetteva un attacco man-in-the-middle quando <code>VerifyHostKeyDNS<\/code> era attivo; <strong>CVE-2025-26466<\/strong> abilitava un DoS pre-autenticazione sulle versioni dalla 9.5p1 alla 9.9p1. Il fix \u00e8 arrivato con OpenSSH 9.9p2. La Singapore Cyber Security Agency ha raccomandato l&#8217;aggiornamento immediato a 9.9p2 e la disabilitazione di <code>VerifyHostKeyDNS<\/code> per chi non usa DNSSEC.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Secondo il rapporto Unit 42 del 2026, l&#8217;89% delle violazioni informatiche coinvolge credenziali compromesse, categoria in cui rientrano a pieno titolo le chiavi private SSH non protette o non ruotate. I pattern pi\u00f9 comuni includono: chiavi senza passphrase trovate in repository Git pubblici, chiavi di ex dipendenti mai rimosse da <code>authorized_keys<\/code>, e agent forwarding usato su host intermedi compromessi per il movimento laterale. Tutti problemi risolvibili con i 12 step di questa guida.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"prerequisiti-e-versioni-richieste\">Prerequisiti e Versioni Richieste<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Prima di iniziare, verifica che l&#8217;ambiente soddisfi questi requisiti. Tutti i comandi sono stati testati su Ubuntu 24.04 LTS, ma funzionano su qualsiasi distribuzione Linux moderna e su macOS 14+.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>OpenSSH 8.0 o superiore<\/strong> (preferibilmente 9.9p2 o 10.0 per le patch di sicurezza 2025). Verifica con <code>ssh -V<\/code><\/li>\n<li><strong>Sistema operativo<\/strong>: Ubuntu 22.04+, Debian 12+, RHEL 9+, macOS 13+, o Windows 10 con OpenSSH nativo<\/li>\n<li><strong>Accesso root o sudo<\/strong> sul server per modificare <code>\/etc\/ssh\/sshd_config<\/code> e gestire il servizio SSH<\/li>\n<li><strong>fail2ban 1.0+<\/strong> (opzionale ma fortemente raccomandato per server esposti su Internet)<\/li>\n<li><strong>Python 3.8+<\/strong> per lo script di audit delle chiavi autorizzate (Step 12)<\/li>\n<li>Una connessione attiva al server remoto e accesso a un terminale locale<\/li>\n<\/ul>\n\n\n\n<pre class=\"wp-block-code\"><code># Verifica la versione di OpenSSH installata localmente\nssh -V\n# OpenSSH_9.9p2, OpenSSL 3.3.2 6 Feb 2024\n\n# Verifica la versione del demone SSH sul server remoto\nssh utente@server \"sshd -V 2>&1 | head -1\"\n\n# Aggiorna all'ultima versione disponibile su Ubuntu\/Debian\nsudo apt update && sudo apt install --only-upgrade openssh-client openssh-server -y\n\n# Su RHEL\/Rocky\/AlmaLinux\nsudo dnf update openssh-clients openssh-server -y<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"confronto-tra-tipi-di-chiavi-ssh-ed25519-rsa-ecdsa\">Confronto tra Tipi di Chiavi SSH: Ed25519, RSA, ECDSA<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">La scelta dell&#8217;algoritmo crittografico per la chiave SSH determina sicurezza, velocit\u00e0 e compatibilit\u00e0. La tabella seguente riepiloga i quattro tipi principali con dati ufficiali basati sulle specifiche <a href=\"https:\/\/www.openssh.com\/releasenotes.html\" target=\"_blank\" rel=\"noopener\">OpenSSH 2025<\/a>:<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table><thead><tr><th>Tipo di Chiave<\/th><th>Dimensione (bit)<\/th><th>Sicurezza equivalente<\/th><th>Velocit\u00e0<\/th><th>Compatibilit\u00e0<\/th><th>Raccomandazione 2026<\/th><\/tr><\/thead><tbody><tr><td><strong>Ed25519<\/strong><\/td><td>256<\/td><td>~128 bit<\/td><td>Molto veloce<\/td><td>OpenSSH 6.5+ (2014)<\/td><td>Scelta migliore: default da OpenSSH 9.5<\/td><\/tr><tr><td><strong>RSA 4096<\/strong><\/td><td>4096<\/td><td>~150 bit<\/td><td>Lenta<\/td><td>Tutti i sistemi<\/td><td>Solo per compatibilit\u00e0 legacy<\/td><\/tr><tr><td><strong>ECDSA 256<\/strong><\/td><td>256<\/td><td>~128 bit<\/td><td>Veloce<\/td><td>OpenSSH 5.7+ (2011)<\/td><td>Accettabile, Ed25519 preferito<\/td><\/tr><tr><td><strong>RSA 2048<\/strong><\/td><td>2048<\/td><td>~112 bit<\/td><td>Moderata<\/td><td>Tutti i sistemi<\/td><td>Minimo accettabile, sconsigliato per nuovi setup<\/td><\/tr><tr><td><strong>DSA<\/strong><\/td><td>1024 fisso<\/td><td>&lt;80 bit<\/td><td>Irrilevante<\/td><td>Deprecato<\/td><td>Rimuovere immediatamente<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">OpenSSH 9.5 (ottobre 2023) ha reso Ed25519 il tipo di chiave generato automaticamente da <code>ssh-keygen<\/code> senza l&#8217;opzione <code>-t<\/code>. Ed25519 usa la curva di Edwards a 25519 bit: produce chiavi di soli 68 byte (contro i 3.401 byte di RSA 4096), firma documenti in microsecondi, ed \u00e8 resistente agli attacchi a canale laterale per design. Per sistemi che devono interoperare con apparecchiature di rete datate o server con OpenSSH precedente alla versione 6.5, usa RSA 4096 come fallback di compatibilit\u00e0. Non usare mai RSA con meno di 2048 bit e rimuovi immediatamente qualsiasi chiave DSA presente.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"step-1-3-generare-una-chiave-ssh-con-ssh-keygen\">Step 1-3: Generare una Chiave SSH con ssh-keygen<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"step-1-generare-una-chiave-ed25519\">Step 1: Generare una Chiave Ed25519<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Apri il terminale e usa il comando seguente per generare una coppia di chiavi Ed25519. L&#8217;opzione <code>-a 100<\/code> aumenta il numero di round KDF (Key Derivation Function) per la protezione della passphrase: ogni tentativo di brute-force richiede 100 round di elaborazione invece dei 16 predefiniti, rallentando gli attacchi di circa 6 volte. Il commento con l&#8217;anno permette di identificare rapidamente le chiavi durante l&#8217;audit e pianificare la rotazione:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># Genera una coppia di chiavi Ed25519 con KDF rinforzato\nssh-keygen -t ed25519 -a 100 -C \"mario.rossi@azienda.it-2026\"\n\n# Output atteso:\n# Generating public\/private ed25519 key pair.\n# Enter file in which to save the key (\/home\/mario\/.ssh\/id_ed25519): [Invio per default]\n# Enter passphrase (empty for no passphrase): [inserisci passphrase forte, min. 20 caratteri]\n# Enter same passphrase again: [ripeti la passphrase]\n# Your identification has been saved in \/home\/mario\/.ssh\/id_ed25519\n# Your public key has been saved in \/home\/mario\/.ssh\/id_ed25519.pub\n# The key fingerprint is:\n# SHA256:xK8j2mNpQ7vR3bL9wE4cF6tY1sA5hD0iU8kJ2mNpQ7v mario.rossi@azienda.it-2026\n# The key's randomart image is:\n# +--[ED25519 256]--+\n# |        .ooo.    |\n# |       . o++ .   |\n# |      . .=E +    |\n# |       .+o+o .   |\n# |      . S*+= .   |\n# +----[SHA256]-----+<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Il <strong>fingerprint SHA256<\/strong> mostrato nell&#8217;output \u00e8 il valore che dovresti annotare e conservare. Quando ti connetti per la prima volta a un server che presenta questo fingerprint come chiave host, puoi verificare out-of-band che la chiave sia autentica. Una passphrase di almeno 20 caratteri, con maiuscole, minuscole, numeri e simboli, \u00e8 la tua ultima linea di difesa se il file della chiave privata viene rubato o copiato senza autorizzazione.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"step-2-generare-una-chiave-rsa-4096-per-sistemi-legacy\">Step 2: Generare una Chiave RSA 4096 per Sistemi Legacy<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Se devi connetterti a sistemi che non supportano Ed25519, come apparecchiature di rete con firmware datato o server con OpenSSH precedente alla versione 6.5, genera una chiave RSA 4096 separata. Usa un nome file distinto per tenerla separata dalla chiave principale Ed25519:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># Genera chiave RSA 4096 per compatibilit\u00e0 legacy\nssh-keygen -t rsa -b 4096 -a 100 -C \"legacy-mario.rossi-2026\" \\\n  -f ~\/.ssh\/id_rsa_legacy\n\n# Verifica le chiavi generate e le loro dimensioni\nls -lh ~\/.ssh\/\n# -rw------- 1 mario mario 3.4K giu 20 10:30 id_rsa_legacy\n# -rw-r--r-- 1 mario mario  743 giu 20 10:30 id_rsa_legacy.pub\n# -rw------- 1 mario mario  411 giu 20 10:30 id_ed25519\n# -rw-r--r-- 1 mario mario  104 giu 20 10:30 id_ed25519.pub\n\n# Mostra il fingerprint di entrambe le chiavi per confronto\nssh-keygen -l -f ~\/.ssh\/id_ed25519.pub\n# 256 SHA256:xK8j... mario.rossi@azienda.it-2026 (ED25519)\nssh-keygen -l -f ~\/.ssh\/id_rsa_legacy.pub\n# 4096 SHA256:AbCd... legacy-mario.rossi-2026 (RSA)<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"step-3-impostare-i-permessi-corretti\">Step 3: Impostare i Permessi Corretti<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">I permessi dei file SSH devono essere esatti. OpenSSH rifiuta le chiavi con permessi troppo permissivi e mostra l&#8217;errore <code>WARNING: UNPROTECTED PRIVATE KEY FILE<\/code>. Questo controllo \u00e8 una misura di sicurezza: se altri utenti del sistema possono leggere la tua chiave privata, questa \u00e8 compromessa a prescindere dalla robustezza crittografica:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># Imposta i permessi corretti (da eseguire dopo ogni nuova chiave)\nchmod 700 ~\/.ssh                      # Directory: solo il proprietario pu\u00f2 accedere\nchmod 600 ~\/.ssh\/id_ed25519           # Chiave privata: solo proprietario in lettura\/scrittura\nchmod 644 ~\/.ssh\/id_ed25519.pub       # Chiave pubblica: leggibile da tutti\nchmod 600 ~\/.ssh\/authorized_keys      # Chiavi autorizzate: solo proprietario\nchmod 600 ~\/.ssh\/config               # Config SSH locale: solo proprietario\n\n# Verifica con ls -la\nls -la ~\/.ssh\/\n# drwx------  2 mario mario 4096 giu 20 10:30 .\n# -rw-------  1 mario mario  411 giu 20 10:30 id_ed25519\n# -rw-r--r--  1 mario mario  104 giu 20 10:30 id_ed25519.pub\n# -rw-------  1 mario mario  104 giu 20 10:30 authorized_keys\n\n# Riparazione automatica dei permessi (utile dopo rsync o backup)\nfind ~\/.ssh -type f -exec chmod 600 {} \\;\nfind ~\/.ssh -type d -exec chmod 700 {} \\;\nchmod 644 ~\/.ssh\/id_ed25519.pub ~\/.ssh\/*.pub 2>\/dev\/null || true<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"step-4-5-installare-la-chiave-pubblica-sul-server-remoto\">Step 4-5: Installare la Chiave Pubblica sul Server Remoto<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Una volta generata la coppia di chiavi in locale, il passo successivo \u00e8 installare la chiave <strong>pubblica<\/strong> sul server remoto. Solo il file <code>.pub<\/code> va copiato sul server: la chiave privata non deve mai lasciare il tuo dispositivo locale, non va mai trasmessa via email, chat, o copiata su server condivisi.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"step-4-usare-ssh-copy-id-metodo-automatico\">Step 4: Usare ssh-copy-id (Metodo Automatico)<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Il comando <code>ssh-copy-id<\/code> \u00e8 lo strumento ufficiale per questa operazione. Si connette al server usando il metodo di autenticazione disponibile (la password, per questa prima volta), crea la directory <code>~\/.ssh<\/code> con permessi 700 se non esiste, e aggiunge la chiave pubblica a <code>~\/.ssh\/authorized_keys<\/code> con i permessi corretti. Evita automaticamente le chiavi duplicate:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># Copia la chiave pubblica sul server remoto (ultima volta con password)\nssh-copy-id -i ~\/.ssh\/id_ed25519.pub mario@192.168.1.100\n\n# Output atteso:\n# \/usr\/bin\/ssh-copy-id: INFO: Source of key(s) to be installed: \"~\/.ssh\/id_ed25519.pub\"\n# \/usr\/bin\/ssh-copy-id: INFO: attempting to log in with the new key(s)\n# \/usr\/bin\/ssh-copy-id: INFO: 1 key(s) remaining to be installed\n# mario@192.168.1.100's password: [ultima volta che inserisci la password]\n# Number of key(s) added: 1\n# Now try logging into the machine, with: \"ssh 'mario@192.168.1.100'\"\n\n# Testa immediatamente la connessione con la nuova chiave\nssh -i ~\/.ssh\/id_ed25519 mario@192.168.1.100\n# Successo: nessuna richiesta di password, solo passphrase della chiave (una volta)\n\n# Su porta non standard (es. 2222)\nssh-copy-id -i ~\/.ssh\/id_ed25519.pub -p 2222 mario@192.168.1.100<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"step-5-metodo-manuale-se-ssh-copy-id-non-e-disponibile\">Step 5: Metodo Manuale se ssh-copy-id non \u00e8 Disponibile<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Su Windows o sistemi dove <code>ssh-copy-id<\/code> non \u00e8 disponibile, puoi installare la chiave pubblica manualmente con questi comandi equivalenti che replicano esattamente la stessa operazione:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># Metodo manuale: Linux\/macOS con pipe diretta (una sola riga)\ncat ~\/.ssh\/id_ed25519.pub | ssh mario@192.168.1.100 \\\n  \"mkdir -p ~\/.ssh && chmod 700 ~\/.ssh && \\\n   cat >> ~\/.ssh\/authorized_keys && chmod 600 ~\/.ssh\/authorized_keys && \\\n   echo 'Chiave aggiunta con successo'\"\n\n# Metodo manuale: PowerShell su Windows\n$pubkey = Get-Content \"$env:USERPROFILE\\.ssh\\id_ed25519.pub\" -Raw\nssh mario@192.168.1.100 \"mkdir -p ~\/.ssh && chmod 700 ~\/.ssh && echo '$pubkey' >> ~\/.ssh\/authorized_keys && chmod 600 ~\/.ssh\/authorized_keys\"\n\n# Verifica che la chiave sia stata aggiunta correttamente\nssh mario@192.168.1.100 \"cat ~\/.ssh\/authorized_keys\"\n# ssh-ed25519 AAAAC3NzaC1lZDI1NTE5... mario.rossi@azienda.it-2026<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"step-6-7-hardening-di-sshd_config-per-la-massima-sicurezza\">Step 6-7: Hardening di sshd_config per la Massima Sicurezza<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">La configurazione del demone SSH \u00e8 il passaggio pi\u00f9 critico di questa guida. Un <code>sshd_config<\/code> con i valori predefiniti espone il server a una superficie d&#8217;attacco evitabile: accesso root diretto, autenticazione con password, numero illimitato di tentativi di login. La configurazione hardened che segue rappresenta la baseline raccomandata per un server esposto su Internet nel 2026, basata sulle linee guida NIST SP 800-53 e sulle best practice della <a href=\"https:\/\/www.openssh.com\/\" target=\"_blank\" rel=\"noopener\">documentazione ufficiale OpenSSH<\/a>.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"step-6-applicare-la-configurazione-hardened\">Step 6: Applicare la Configurazione Hardened<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code># Backup obbligatorio prima di qualsiasi modifica\nsudo cp \/etc\/ssh\/sshd_config \/etc\/ssh\/sshd_config.backup.$(date +%Y%m%d)\n\n# Verifica il backup\nls -lh \/etc\/ssh\/sshd_config*\n# -rw-r--r-- 1 root root 3.2K giu 20 10:00 \/etc\/ssh\/sshd_config\n# -rw-r--r-- 1 root root 3.2K giu 20 10:30 \/etc\/ssh\/sshd_config.backup.20260620\n\n# Apri la configurazione per le modifiche\nsudo nano \/etc\/ssh\/sshd_config<\/code><\/pre>\n\n\n\n<pre class=\"wp-block-code\"><code># \/etc\/ssh\/sshd_config - Configurazione hardened 2026\n# Backup: \/etc\/ssh\/sshd_config.backup.YYYYMMDD\n\n# === AUTENTICAZIONE ===\nPermitRootLogin no                # Mai l'accesso root diretto\nPasswordAuthentication no         # Solo chiavi, nessuna password\nPubkeyAuthentication yes          # Autenticazione a chiave pubblica abilitata\nKbdInteractiveAuthentication no   # Disabilita auth interattiva\nPermitEmptyPasswords no           # Nessuna chiave senza passphrase accettata\n\n# === LIMITI DI CONNESSIONE ===\nMaxAuthTries 3                    # Massimo 3 tentativi per connessione\nLoginGraceTime 20                 # 20 secondi per completare l'autenticazione\nMaxSessions 5                     # Massimo 5 sessioni per connessione multiplexed\nMaxStartups 10:30:60              # Limita connessioni parallele non autenticate\n\n# === TUNNELING E FORWARDING ===\nX11Forwarding no                  # Disabilita X11 forwarding\nAllowTcpForwarding no             # Disabilita TCP forwarding\nAllowAgentForwarding no           # Disabilita agent forwarding\nPermitTunnel no                   # Disabilita tunnel tun\/tap\n\n# === SICUREZZA PROTOCOLLO ===\nIgnoreRhosts yes                  # Ignora file .rhosts (protocollo obsoleto)\nHostbasedAuthentication no        # Nessuna autenticazione basata su hostname\nPermitUserEnvironment no          # Non accettare variabili d'ambiente dal client\nStrictModes yes                   # Controlla permessi file chiavi prima di usarle\n\n# === SESSIONI INATTIVE ===\nClientAliveInterval 300           # Ping ogni 5 minuti\nClientAliveCountMax 2             # Disconnetti dopo 2 ping senza risposta\n\n# === LOGGING ===\nLogLevel VERBOSE                  # Log dettagliato (include fingerprint chiavi usate)\nSyslogFacility AUTH\n\n# === PERFORMANCE ===\nUseDNS no                         # Disabilita reverse DNS lookup (velocizza il login)<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"step-7-validare-e-riavviare-il-servizio-ssh\">Step 7: Validare e Riavviare il Servizio SSH<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Questo step richiede attenzione: un errore in <code>sshd_config<\/code> pu\u00f2 bloccare completamente l&#8217;accesso al server. La sequenza corretta prevede di aprire una sessione di backup PRIMA di riavviare il demone:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># STEP 1: Valida la configurazione senza riavviare\nsudo sshd -t\n# Nessun output = configurazione valida\n# In caso di errori (es: \"Bad configuration option: PermitRooLogin\"):\n# correggi il typo e ripeti il test\n\n# STEP 2: Apri una SECONDA finestra terminale con una connessione SSH attiva\n# Questa sessione rimane aperta come \"paracadute\" durante il riavvio\n# Non chiuderla finch\u00e9 non hai verificato che la nuova configurazione funziona\n\n# STEP 3: Riavvia SSH solo dopo aver aperto la sessione di backup\nsudo systemctl restart sshd\n\n# Verifica lo stato del servizio\nsudo systemctl status sshd\n# Output atteso:\n# Active: active (running) since 2026-06-20 10:35:00 UTC\n\n# STEP 4: Dalla sessione di backup, apri una TERZA finestra e testa la connessione\nssh -i ~\/.ssh\/id_ed25519 mario@192.168.1.100\n# Successo: puoi chiudere la sessione di backup\n# Fallimento: dalla sessione di backup ripristina:\n#   sudo cp \/etc\/ssh\/sshd_config.backup.20260620 \/etc\/ssh\/sshd_config\n#   sudo systemctl restart sshd<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"step-8-proteggere-ssh-con-fail2ban\">Step 8: Proteggere SSH con fail2ban<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Anche con <code>PasswordAuthentication no<\/code>, il demone SSH gestisce connessioni da bot e scanner automatici che consumano risorse e riempiono i log. fail2ban analizza i log di autenticazione e blocca automaticamente gli IP che superano la soglia di tentativi falliti. Con i ban progressivi, ogni recidiva raddoppia la durata del blocco fino a una settimana. Questo \u00e8 lo scudo fondamentale per qualsiasi server SSH esposto su Internet, come documentato dalla <a href=\"https:\/\/www.fail2ban.org\/\" target=\"_blank\" rel=\"noopener\">documentazione ufficiale di fail2ban<\/a>.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># Installazione su Ubuntu\/Debian\nsudo apt update && sudo apt install fail2ban -y\n\n# Su RHEL\/Rocky\/AlmaLinux\nsudo dnf install fail2ban -y\n\n# Crea la configurazione locale (non modificare jail.conf: viene sovrascritto dagli aggiornamenti)\nsudo cp \/etc\/fail2ban\/jail.conf \/etc\/fail2ban\/jail.local<\/code><\/pre>\n\n\n\n<pre class=\"wp-block-code\"><code># \/etc\/fail2ban\/jail.local - Sezione SSH con ban progressivi\n\n[DEFAULT]\nbantime   = 1h           # Primo ban: 1 ora\nfindtime  = 10m          # Finestra di osservazione: 10 minuti\nmaxretry  = 3            # Massimo 3 tentativi falliti\n\n# Ban progressivo: ogni recidiva raddoppia il ban fino a 1 settimana\nbantime.increment  = true\nbantime.factor     = 2\nbantime.maxtime    = 1w\n\n[sshd]\nenabled  = true\nport     = ssh           # Cambia in \"2222\" se usi una porta non standard\nfilter   = sshd\nlogpath  = \/var\/log\/auth.log    # Ubuntu\/Debian\n# logpath = \/var\/log\/secure     # RHEL\/Rocky: decommenta questa, commenta quella sopra\nmaxretry = 3\nbantime  = 12h\nbackend  = systemd<\/code><\/pre>\n\n\n\n<pre class=\"wp-block-code\"><code># Avvia e abilita fail2ban al boot\nsudo systemctl enable --now fail2ban\n\n# Verifica lo stato della jail SSH\nsudo fail2ban-client status sshd\n# Status for the jail: sshd\n# |- Filter\n# |  |- Currently failed: 2\n# |  |- Total failed: 47\n# `- Actions\n#    |- Currently banned: 3\n#    `- Banned IP list: 185.220.101.47 45.95.169.212 94.102.49.193\n\n# Sblocca manualmente il tuo IP se ti sei bannato accidentalmente\nsudo fail2ban-client set sshd unbanip 192.168.1.50\n\n# Controlla i log di fail2ban per analizzare i pattern di attacco\nsudo journalctl -u fail2ban -n 50 --no-pager<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"step-9-10-ruotare-le-chiavi-ssh-in-sicurezza\">Step 9-10: Ruotare le Chiavi SSH in Sicurezza<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">La rotazione delle chiavi SSH \u00e8 il passaggio pi\u00f9 trascurato nelle infrastrutture reali. Chiavi generate anni fa e mai sostituite sono un rischio concreto: un ex dipendente, un laptop rubato non segnalato, o una violazione non rilevata di un repository privato potrebbero aver esposto la chiave privata. La procedura corretta segue sempre il principio &#8220;aggiungi prima, rimuovi dopo&#8221; per non perdere mai l&#8217;accesso durante la transizione.<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table><thead><tr><th>Ambiente<\/th><th>Frequenza Rotazione<\/th><th>Trigger Rotazione Immediata<\/th><th>Note<\/th><\/tr><\/thead><tbody><tr><td>Produzione critica (finanza, PA, salute)<\/td><td>Ogni 90 giorni<\/td><td>Dipendente che lascia, laptop rubato, sospetta compromissione<\/td><td>Usa SSH CA per scalabilit\u00e0<\/td><\/tr><tr><td>Produzione standard<\/td><td>Ogni 6 mesi<\/td><td>Segnalazione di compromissione, cambio team<\/td><td>Registra ogni rotazione in un log<\/td><\/tr><tr><td>Staging e test<\/td><td>Ogni 12 mesi<\/td><td>Fine progetto, ex collaboratori<\/td><td>Rimuovi chiavi dei collaboratori usciti<\/td><\/tr><tr><td>CI\/CD (GitHub Actions, GitLab CI)<\/td><td>Ogni 90 giorni<\/td><td>Sospetto leak nel codice sorgente<\/td><td>Usa chiavi dedicate, scope minimo<\/td><\/tr><tr><td>Sviluppo locale<\/td><td>Ogni 12-24 mesi<\/td><td>Cambio computer, furto, perdita<\/td><td>Una chiave separata per progetto\/host<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"step-9-generare-la-nuova-chiave-e-aggiungerla-al-server\">Step 9: Generare la Nuova Chiave e Aggiungerla al Server<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code># 1. Genera la nuova chiave con anno corrente nel commento per tracciabilit\u00e0\nANNO=$(date +%Y)\nssh-keygen -t ed25519 -a 100 -C \"mario.rossi@azienda.it-${ANNO}\" \\\n  -f ~\/.ssh\/id_ed25519_new\n\n# 2. Aggiungi la NUOVA chiave al server PRIMA di rimuovere la vecchia\n#    (non perdere mai l'accesso durante la transizione)\nssh-copy-id -i ~\/.ssh\/id_ed25519_new.pub mario@192.168.1.100\n\n# 3. Testa la connessione con la nuova chiave specificandola esplicitamente\nssh -i ~\/.ssh\/id_ed25519_new mario@192.168.1.100 \"echo 'Nuova chiave: OK'\"\n\n# 4. Solo dopo aver verificato: rimuovi la vecchia chiave da authorized_keys\n#    Questo esempio rimuove la riga contenente il commento dell'anno precedente\nssh -i ~\/.ssh\/id_ed25519_new mario@192.168.1.100 \\\n  \"grep -v 'mario.rossi@azienda.it-$((ANNO - 1))' ~\/.ssh\/authorized_keys \\\n   > \/tmp\/authorized_keys_new && \\\n   mv \/tmp\/authorized_keys_new ~\/.ssh\/authorized_keys && \\\n   chmod 600 ~\/.ssh\/authorized_keys && \\\n   echo 'Vecchia chiave rimossa'\"<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"step-10-finalizzare-la-rotazione-e-aggiornare-la-configurazione-locale\">Step 10: Finalizzare la Rotazione e Aggiornare la Configurazione Locale<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code># 5. Sostituisci la chiave vecchia con quella nuova nel file system locale\nmv ~\/.ssh\/id_ed25519_new ~\/.ssh\/id_ed25519\nmv ~\/.ssh\/id_ed25519_new.pub ~\/.ssh\/id_ed25519.pub\nchmod 600 ~\/.ssh\/id_ed25519\nchmod 644 ~\/.ssh\/id_ed25519.pub\n\n# 6. Aggiorna ~\/.ssh\/config per usare la nuova chiave (se hai configurazioni specifiche)\ncat > ~\/.ssh\/config << 'EOF'\nHost server-produzione\n    HostName 192.168.1.100\n    User mario\n    IdentityFile ~\/.ssh\/id_ed25519\n    IdentitiesOnly yes          # Usa solo la chiave specificata, non tutto l'agent\n    AddKeysToAgent yes          # Aggiungi la chiave all'agent alla prima connessione\n\nHost server-legacy\n    HostName 10.0.0.50\n    User admin\n    IdentityFile ~\/.ssh\/id_rsa_legacy\n    IdentitiesOnly yes\n\nHost bastion\n    HostName bastion.azienda.it\n    User mario\n    IdentityFile ~\/.ssh\/id_ed25519\n    ControlMaster auto\n    ControlPath ~\/.ssh\/sockets\/%r@%h-%p\n    ControlPersist 600\nEOF\nchmod 600 ~\/.ssh\/config<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"step-11-ssh-certificate-authority-per-ambienti-enterprise\">Step 11: SSH Certificate Authority per Ambienti Enterprise<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Con pi\u00f9 di 10 server o team numerosi, aggiornare manualmente <code>authorized_keys<\/code> su ogni macchina diventa ingestibile e soggetto a errori. Un nuovo collaboratore richiede l'aggiornamento di decine di file; quando lascia l'azienda, devi ricordare di rimuovere la sua chiave da tutti i server. La SSH Certificate Authority risolve entrambi i problemi: i server si fidano di una CA centrale e i certificati utente hanno una scadenza automatica.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># === SETUP SSH CERTIFICATE AUTHORITY ===\n# Eseguire questi comandi su un sistema sicuro, idealmente offline o con accesso HSM\n\n# 1. Crea la CA per la firma delle chiavi utente\nsudo ssh-keygen -t ed25519 -f \/etc\/ssh\/ca_user -C \"SSH User CA 2026\" -a 100\n# \/etc\/ssh\/ca_user     -> chiave privata CA (custodire offline o in HSM)\n# \/etc\/ssh\/ca_user.pub -> chiave pubblica CA (da distribuire ai server)\n\n# 2. Configura ogni server per fidarsi della CA utenti\nsudo install -m 644 \/etc\/ssh\/ca_user.pub \/etc\/ssh\/trusted-user-ca-keys.pem\necho \"TrustedUserCAKeys \/etc\/ssh\/trusted-user-ca-keys.pem\" | sudo tee -a \/etc\/ssh\/sshd_config\nsudo systemctl reload sshd\n\n# 3. Firma la chiave pubblica di un utente (validit\u00e0 52 settimane, scade automaticamente)\nsudo ssh-keygen -s \/etc\/ssh\/ca_user \\\n  -I \"mario-laptop-2026\" \\\n  -n mario \\\n  -V +52w \\\n  ~\/.ssh\/id_ed25519.pub\n# Risultato: ~\/.ssh\/id_ed25519-cert.pub\n\n# 4. Verifica il certificato firmato\nssh-keygen -L -f ~\/.ssh\/id_ed25519-cert.pub\n# Output:\n#   Type: ssh-ed25519-cert-v01@openssh.com user certificate\n#   Public key: ED25519-CERT SHA256:xK8j...\n#   Signing CA: ED25519 SHA256:yR9m... (using ssh-ed25519)\n#   Key ID: \"mario-laptop-2026\"\n#   Valid: from 2026-06-20T10:00:00 to 2027-06-19T10:00:00\n#   Principals: mario\n\n# 5. La connessione funziona senza aggiungere nulla a authorized_keys\nssh mario@192.168.1.100\n# Il server verifica il certificato contro la CA pubblica\n\n# 6. Per revocare: il certificato scade automaticamente dopo 52 settimane\n#    Per revoca immediata: non rinnovare il certificato alla scadenza<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"step-12-audit-delle-chiavi-ssh-autorizzate\">Step 12: Audit delle Chiavi SSH Autorizzate<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">L'ultimo step riguarda la visibilit\u00e0: sapere esattamente quali chiavi sono autorizzate su ogni server. Uno script di audit periodico rivela chiavi orfane (senza commento identificativo), chiavi con algoritmi obsoleti, o chiavi di utenti non pi\u00f9 attivi. Esegui questo audit almeno ogni trimestre e integra i risultati in un processo di remediation automatica per le criticit\u00e0 trovate. La <a href=\"https:\/\/www.man7.org\/linux\/man-pages\/man1\/ssh-keygen.1.html\" target=\"_blank\" rel=\"noopener\">man page di ssh-keygen<\/a> documenta tutte le opzioni disponibili per l'analisi delle chiavi.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>#!\/usr\/bin\/env python3\n\"\"\"\nAudit SSH authorized_keys su server remoti.\nUso: python3 ssh_audit.py\n\"\"\"\n\nimport subprocess\nimport sys\n\n# Lista dei server da auditare\nSERVERS = [\n    \"mario@192.168.1.100\",\n    \"admin@10.0.0.50\",\n    \"deploy@server-prod.azienda.it\",\n]\n\n# Tipi di chiave obsoleti o da monitorare\nOBSOLETE_TYPES = {\"ssh-dss\"}          # DSA: deprecato, rimuovi subito\nLEGACY_TYPES   = {\"ssh-rsa\"}          # RSA: accettabile solo se 4096 bit\n\ndef audit_server(server: str) -> None:\n    print(f\"\\n{'='*60}\")\n    print(f\"Audit: {server}\")\n    print('='*60)\n\n    try:\n        result = subprocess.run(\n            [\"ssh\", \"-o\", \"BatchMode=yes\", \"-o\", \"ConnectTimeout=10\",\n             server, \"cat ~\/.ssh\/authorized_keys 2>\/dev\/null || echo ''\"],\n            capture_output=True, text=True, timeout=15\n        )\n    except subprocess.TimeoutExpired:\n        print(f\"  [ERRORE] Timeout connessione\")\n        return\n\n    if result.returncode != 0:\n        print(f\"  [ERRORE] Connessione fallita: {result.stderr.strip()}\")\n        return\n\n    lines = [l.strip() for l in result.stdout.split(\"\\n\")\n             if l.strip() and not l.startswith(\"#\")]\n\n    if not lines:\n        print(\"  [INFO] Nessuna chiave in authorized_keys\")\n        return\n\n    for i, line in enumerate(lines, 1):\n        parts = line.split()\n        key_type = parts[0] if parts else \"?\"\n        comment  = parts[-1] if len(parts) > 2 else \"\"\n\n        issues = []\n        if key_type in OBSOLETE_TYPES:\n            issues.append(\"TIPO OBSOLETO - rimuovere immediatamente\")\n        if key_type in LEGACY_TYPES:\n            issues.append(\"Tipo legacy - verificare dimensione chiave\")\n        if not comment or \"@\" not in comment:\n            issues.append(\"Nessun identificativo utente nel commento\")\n\n        stato = \"[CRITICO]\" if any(\"immediatamente\" in i for i in issues) \\\n                else \"[ATTENZIONE]\" if issues else \"[OK]\"\n\n        print(f\"  Chiave {i}: {key_type} | {comment or 'N\/A'} {stato}\")\n        for issue in issues:\n            print(f\"    -> {issue}\")\n\nif __name__ == \"__main__\":\n    for s in SERVERS:\n        audit_server(s)\n    print(\"\\n[Fine audit] Risolvi tutte le criticit\u00e0 [CRITICO] entro 24 ore.\")\n\n# Esempio output:\n# ============================================================\n# Audit: mario@192.168.1.100\n# ============================================================\n#   Chiave 1: ssh-ed25519 | mario.rossi@azienda.it-2026 [OK]\n#   Chiave 2: ssh-dss | N\/A [CRITICO]\n#     -> TIPO OBSOLETO - rimuovere immediatamente\n#     -> Nessun identificativo utente nel commento<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"6-errori-critici-nella-gestione-delle-chiavi-ssh\">6 Errori Critici nella Gestione delle Chiavi SSH<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Questi sono i 6 errori pi\u00f9 frequenti nella gestione delle chiavi SSH, basati sui pattern di vulnerabilit\u00e0 documentati da OpenSSH e dai principali vendor di sicurezza nel 2025-2026. Ognuno di questi errori ha causato violazioni reali nelle infrastrutture enterprise:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>Chiave privata senza passphrase.<\/strong> Una chiave privata senza passphrase equivale a una password in chiaro su disco. Se il laptop viene rubato, se un malware legge i file home, o se il file finisce in un backup non cifrato, l'attaccante ha accesso immediato a tutti i server con quella chiave. La soluzione \u00e8 semplice: usa sempre <code>-a 100<\/code> e una passphrase di almeno 20 caratteri. Usa <code>ssh-agent<\/code> per non doverla reinserire a ogni connessione.<\/li>\n<li><strong>Agent forwarding abilitato su host non fidati.<\/strong> Con <code>AllowAgentForwarding yes<\/code> nel server e <code>ForwardAgent yes<\/code> nel client, il server remoto pu\u00f2 usare il tuo ssh-agent per autenticarsi su altri server. Un server intermedio compromesso pu\u00f2 sfruttare il socket dell'agent per muoversi lateralmente su tutta la rete. CVE-2025-26465 ha amplificato questo rischio. Mantieni sempre <code>AllowAgentForwarding no<\/code> in <code>sshd_config<\/code> e <code>ForwardAgent no<\/code> come default in <code>~\/.ssh\/config<\/code>.<\/li>\n<li><strong>Chiavi DSA o RSA sotto i 2048 bit ancora attive.<\/strong> OpenSSH ha deprecato DSA e le chiavi RSA inferiori a 2048 bit. Queste chiavi offrono meno di 80 bit di sicurezza. Identifica le chiavi obsolete con <code>ssh-keygen -l -f ~\/.ssh\/authorized_keys<\/code> e rimuovile subito.<\/li>\n<li><strong>CVE-2025-26465: VerifyHostKeyDNS abilitato senza DNSSEC.<\/strong> Se hai <code>VerifyHostKeyDNS yes<\/code> nella tua configurazione SSH client su OpenSSH dalla versione 6.8p1 alla 9.9p1, sei vulnerabile a un attacco MitM documentato nel febbraio 2025. Soluzione immediata: aggiorna a OpenSSH 9.9p2 e disabilita <code>VerifyHostKeyDNS<\/code> se non usi DNSSEC. Verifica la versione con <code>ssh -V<\/code>.<\/li>\n<li><strong>Chiavi orfane in authorized_keys.<\/strong> Le chiavi di ex dipendenti, collaboratori temporanei, o servizi dismessi rimangono attive per mesi o anni in <code>authorized_keys<\/code>. Ogni chiave non rimossa \u00e8 una porta aperta potenziale. Pianifica una revisione trimestrale con lo script di audit del Step 12 e rimuovi sistematicamente le chiavi non pi\u00f9 necessarie.<\/li>\n<li><strong>PermitRootLogin yes.<\/strong> L'accesso diretto come root via SSH espone l'account pi\u00f9 privilegiato agli attacchi di forza bruta e ai credential stuffing automatizzati. Imposta sempre <code>PermitRootLogin no<\/code> e usa <code>sudo<\/code> dopo l'autenticazione con un account normale. Se i processi di automazione richiedono privilegi root, usa un account di servizio dedicato con sudo specifico e scope minimo.<\/li>\n<\/ol>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"troubleshooting-10-problemi-ssh-comuni-e-le-soluzioni\">Troubleshooting: 10 Problemi SSH Comuni e le Soluzioni<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">La diagnostica SSH richiede di analizzare sia il lato client che quello server. Il comando <code>ssh -vvv<\/code> \u00e8 lo strumento principale: mostra ogni step del processo di autenticazione, da quale chiave viene offerta a perch\u00e9 il server la rifiuta. Usa <code>sudo journalctl -fu sshd<\/code> sul server per i log in tempo reale.<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table><thead><tr><th>Errore \/ Sintomo<\/th><th>Causa Probabile<\/th><th>Soluzione<\/th><\/tr><\/thead><tbody><tr><td><code>Permission denied (publickey)<\/code><\/td><td>Chiave non in authorized_keys, permessi errati, o chiave sbagliata<\/td><td><code>chmod 700 ~\/.ssh && chmod 600 ~\/.ssh\/authorized_keys<\/code> + <code>ssh -vvv<\/code> per debug<\/td><\/tr><tr><td><code>WARNING: UNPROTECTED PRIVATE KEY FILE<\/code><\/td><td>Permessi della chiave privata troppo permissivi<\/td><td><code>chmod 600 ~\/.ssh\/id_ed25519<\/code><\/td><\/tr><tr><td><code>Host key verification failed<\/code><\/td><td>L'host ha cambiato chiave (aggiornamento) o MitM sospetto<\/td><td><code>ssh-keygen -R hostname<\/code> e verifica il fingerprint del server out-of-band<\/td><\/tr><tr><td><code>Too many authentication failures<\/code><\/td><td>ssh-agent offre troppe chiavi, il server raggiunge MaxAuthTries<\/td><td>Aggiungi <code>IdentitiesOnly yes<\/code> e <code>IdentityFile<\/code> specifico in ~\/.ssh\/config<\/td><\/tr><tr><td><code>Connection refused<\/code><\/td><td>SSH non in ascolto, porta errata, o firewall bloccante<\/td><td><code>sudo systemctl status sshd<\/code> + <code>sudo ufw allow ssh<\/code><\/td><\/tr><tr><td>IP bannato da fail2ban<\/td><td>Troppi tentativi falliti dal proprio IP<\/td><td><code>sudo fail2ban-client set sshd unbanip TUO_IP<\/code><\/td><\/tr><tr><td><code>Bad owner or permissions on ~\/.ssh\/config<\/code><\/td><td>Il file config ha permessi troppo aperti<\/td><td><code>chmod 600 ~\/.ssh\/config && chown $USER ~\/.ssh\/config<\/code><\/td><\/tr><tr><td><code>sign_and_send_pubkey: signing failed<\/code><\/td><td>Chiave corrotta o passphrase errata<\/td><td>Verifica con <code>ssh-keygen -l -f ~\/.ssh\/id_ed25519<\/code>; rigenera se corrotta<\/td><\/tr><tr><td>Login lento (10-30 secondi)<\/td><td>Reverse DNS lookup lento da parte del server<\/td><td>Aggiungi <code>UseDNS no<\/code> in sshd_config e riavvia SSH<\/td><\/tr><tr><td><code>Server unexpectedly closed network connection<\/code><\/td><td>sshd crashato, MaxSessions raggiunto, o ban attivo<\/td><td><code>sudo journalctl -u sshd -n 50 --no-pager<\/code> per analizzare i log<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<pre class=\"wp-block-code\"><code># Diagnostica avanzata: ogni step del handshake SSH\nssh -vvv mario@192.168.1.100 2>&1 | grep -E \"(Offering|accepts|fail|debug1: Authen)\"\n\n# Passi chiave nell'output:\n# \"Offering public key: \/home\/mario\/.ssh\/id_ed25519 ED25519 SHA256:...\"\n#   -> Il client sta offrendo questa chiave al server\n\n# \"Server accepts key: \/home\/mario\/.ssh\/id_ed25519 ED25519 SHA256:...\"\n#   -> Il server ha accettato: autenticazione riuscita\n\n# \"Authentications that can continue: publickey\" senza \"accepts key\"\n#   -> La chiave non e' in authorized_keys del server\n\n# \"Permissions ... are too open\" nel log del server\n#   -> Permessi errati su authorized_keys o sulla directory .ssh\n\n# Controlla i log del server in tempo reale durante il tentativo di connessione\nsudo journalctl -fu sshd --no-pager\n# Cerca: \"Accepted publickey for mario\" o \"Failed publickey for mario\"<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"tecniche-avanzate-multiplexing-jump-host-e-fido2\">Tecniche Avanzate: Multiplexing, Jump Host e FIDO2<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Una volta padroneggiata la gestione base delle chiavi, queste tre tecniche avanzate migliorano sicurezza e produttivit\u00e0 nelle infrastrutture complesse senza compromettere il modello di sicurezza stabilito nei passi precedenti.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>SSH Multiplexing<\/strong> condivide una singola connessione TCP tra pi\u00f9 sessioni verso lo stesso host. Riduce il tempo di connessione da 1-2 secondi a meno di 100ms per le sessioni successive e diminuisce il numero di autenticazioni necessarie. Aggiunge queste righe al tuo <code>~\/.ssh\/config<\/code>:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># Multiplexing: aggiunto globalmente a ~\/.ssh\/config\nHost *\n    ControlMaster auto\n    ControlPath ~\/.ssh\/sockets\/%r@%h-%p\n    ControlPersist 600         # Mantieni la connessione 10 minuti dopo la chiusura\n\n# Crea la directory per i socket di controllo\nmkdir -p ~\/.ssh\/sockets && chmod 700 ~\/.ssh\/sockets\n\n# Jump Host: accesso a server interno via bastion, senza agent forwarding\nssh -J mario@bastion.azienda.it mario@server-interno.lan\n\n# In ~\/.ssh\/config (soluzione permanente)\nHost server-interno\n    HostName 10.0.0.100\n    User mario\n    ProxyJump bastion.azienda.it\n    IdentityFile ~\/.ssh\/id_ed25519\n    ForwardAgent no            # MAI agent forwarding attraverso il bastion\n\n# Chiave SSH hardware su YubiKey (OpenSSH 8.2+, richiede YubiKey 5+)\nssh-keygen -t ed25519-sk -O resident -O application=ssh:azienda \\\n  -C \"yubikey-mario.rossi-2026\"\n# La chiave privata non lascia mai il token hardware fisico\n# Installazione identica a una chiave normale: ssh-copy-id installa la .pub<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"progetto-completo-script-di-setup-ssh-sicuro\">Progetto Completo: Script di Setup SSH Sicuro<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Lo script seguente automatizza l'intero processo per un nuovo server: genera la chiave Ed25519 se non esiste, la installa sul server, applica il hardening a <code>sshd_config<\/code> in modo non distruttivo (non sovrascrive la configurazione intera, modifica solo i parametri specificati), e fornisce un report finale. Eseguilo una volta per ogni nuovo server da mettere in produzione:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code\">#!\/bin\/bash\n# ssh-secure-setup.sh - Setup SSH sicuro automatizzato\n# Uso: .\/ssh-secure-setup.sh UTENTE SERVER_IP [PORTA]\n# Esempio: .\/ssh-secure-setup.sh mario 192.168.1.100\n# Porta non standard: .\/ssh-secure-setup.sh mario 192.168.1.100 2222\n\nset -euo pipefail\n\nUTENTE=\"${1:?Specificare utente, es: mario}\"\nSERVER=\"${2:?Specificare IP server, es: 192.168.1.100}\"\nPORTA=\"${3:-22}\"\nCHIAVE=\"$HOME\/.ssh\/id_ed25519\"\nANNO=$(date +%Y)\nEMAIL=\"${USER}@$(hostname -f 2>\/dev\/null | cut -d. -f2- || echo 'local')\"\n\nGREEN='\\033[0;32m'; YELLOW='\\033[1;33m'; RED='\\033[0;31m'; NC='\\033[0m'\nok()   { echo -e \"${GREEN}[OK]${NC} $*\"; }\nwarn() { echo -e \"${YELLOW}[!]${NC} $*\"; }\nerr()  { echo -e \"${RED}[ERRORE]${NC} $*\" >&2; exit 1; }\n\necho \"=== SSH Secure Setup: ${UTENTE}@${SERVER}:${PORTA} ===\"\n\n# Genera chiave Ed25519 se non esiste\nif [[ ! -f \"$CHIAVE\" ]]; then\n    ok \"Generazione chiave Ed25519 (KDF a 100 round)...\"\n    ssh-keygen -t ed25519 -a 100 -C \"${EMAIL}-${ANNO}\" -f \"$CHIAVE\"\n    chmod 600 \"$CHIAVE\" && chmod 644 \"${CHIAVE}.pub\"\nfi\nok \"Chiave: $CHIAVE\"\nok \"Fingerprint: $(ssh-keygen -l -f \"$CHIAVE\" 2>\/dev\/null)\"\n\n# Installa la chiave sul server\nok \"Installazione chiave sul server...\"\nssh-copy-id -i \"${CHIAVE}.pub\" -p \"$PORTA\" \"${UTENTE}@${SERVER}\" || \\\n  err \"ssh-copy-id fallito. Verifica la password o la connettivit\u00e0.\"\n\n# Testa la connessione con la nuova chiave\nssh -i \"$CHIAVE\" -p \"$PORTA\" -o BatchMode=yes -o ConnectTimeout=10 \\\n    \"${UTENTE}@${SERVER}\" \"echo 'Connessione con chiave: OK'\" || \\\n  err \"Test connessione fallito. Debug: ssh -vvv -i $CHIAVE -p $PORTA ${UTENTE}@${SERVER}\"\n\n# Hardening remoto di sshd_config\nok \"Applicazione hardening sshd_config...\"\nssh -i \"$CHIAVE\" -p \"$PORTA\" \"${UTENTE}@${SERVER}\" \"bash -s\" << 'REMOTE'\nset -e\nCONFIG=\"\/etc\/ssh\/sshd_config\"\nBACKUP=\"${CONFIG}.bak.$(date +%Y%m%d)\"\nsudo cp \"$CONFIG\" \"$BACKUP\" 2>\/dev\/null || true\n\nset_param() {\n    local key=\"$1\" val=\"$2\"\n    if sudo grep -qE \"^[[:space:]]*#?[[:space:]]*${key}[[:space:]]\" \"$CONFIG\" 2>\/dev\/null; then\n        sudo sed -i \"s|^[[:space:]]*#\\?[[:space:]]*${key}[[:space:]].*|${key} ${val}|\" \"$CONFIG\"\n    else\n        echo \"${key} ${val}\" | sudo tee -a \"$CONFIG\" > \/dev\/null\n    fi\n}\n\nset_param PermitRootLogin no\nset_param PasswordAuthentication no\nset_param MaxAuthTries 3\nset_param LoginGraceTime 20\nset_param X11Forwarding no\nset_param AllowTcpForwarding no\nset_param AllowAgentForwarding no\nset_param ClientAliveInterval 300\nset_param ClientAliveCountMax 2\nset_param UseDNS no\nset_param LogLevel VERBOSE\n\nif sudo sshd -t; then\n    sudo systemctl restart sshd\n    echo \"sshd_config hardened: OK\"\nelse\n    sudo cp \"$BACKUP\" \"$CONFIG\"\n    echo \"ERRORE: configurazione non valida, backup ripristinato\"\n    exit 1\nfi\nREMOTE\n\nok \"Setup completato per ${UTENTE}@${SERVER}:${PORTA}\"\nwarn \"Testa la connessione in una nuova finestra PRIMA di chiudere questa sessione\"\nwarn \"Prossima rotazione chiave: $(date -d '+6 months' '+%Y-%m-%d' 2>\/dev\/null || date -v+6m '+%Y-%m-%d')\"<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"copertura-correlata\">Copertura Correlata<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"approfondimenti-su-shattered-io\">Approfondimenti su shattered.io<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"\/it\/tls-1-3-nodejs\/\">TLS 1.3 in Node.js: 12 Step per HTTPS Sicuro in 30 Min [2026]<\/a><\/li>\n<li><a href=\"\/it\/oauth2-pkce-nodejs\/\">OAuth 2.0 e PKCE in Node.js: 12 Step in 30 Minuti [2026]<\/a><\/li>\n<li><a href=\"\/it\/nodejs-crypto-module\/\">Node.js Crypto Module: 12 Step per la Crittografia [2026]<\/a><\/li>\n<li><a href=\"\/it\/aes-256-encryption-nodejs\/\">AES-256 Encryption in Node.js: 12 Step [2026]<\/a><\/li>\n<li><a href=\"\/it\/wazuh-tutorial-installazione-siem\/\">Wazuh: Installazione SIEM\/XDR in 12 Step [2026]<\/a><\/li>\n<li><a href=\"\/it\/security\/\">Guida alla Sicurezza Informatica [2026]<\/a><\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"faq-domande-frequenti-su-ssh-keygen-e-chiavi-ssh\">FAQ: Domande Frequenti su ssh-keygen e Chiavi SSH<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"qual-e-il-tipo-di-chiave-ssh-piu-sicuro-nel-2026\">Qual \u00e8 il tipo di chiave SSH pi\u00f9 sicuro nel 2026?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Ed25519 \u00e8 il tipo raccomandato per tutti i nuovi deployment. Offre sicurezza equivalente a circa 128 bit con chiavi da soli 256 bit, firma e verifica pi\u00f9 veloci rispetto a RSA e ECDSA, e produce chiavi di soli 68 byte contro i 3.401 di RSA 4096. OpenSSH lo ha reso il tipo predefinito dalla versione 9.5. RSA 4096 resta accettabile solo per compatibilit\u00e0 con sistemi legacy che non supportano Ed25519. DSA \u00e8 deprecato e disabilitato nelle versioni moderne di OpenSSH.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"devo-usare-una-passphrase-per-la-chiave-ssh\">Devo usare una passphrase per la chiave SSH?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Sempre, per le chiavi personali e di produzione. La passphrase cifra la chiave privata con AES-256 e la protegge se il file viene rubato. Usa <code>ssh-agent<\/code> per non reinserire la passphrase a ogni connessione: la inserisci una volta all'inizio della sessione e l'agent la gestisce in modo sicuro in memoria. L'unica eccezione accettabile sono le chiavi per processi automatizzati (CI\/CD senza interazione umana), dove puoi valutare l'SSH Certificate Authority con certificati a breve scadenza come alternativa pi\u00f9 sicura.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"ogni-quanto-devo-ruotare-le-chiavi-ssh\">Ogni quanto devo ruotare le chiavi SSH?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Per ambienti di produzione standard, ogni 6 mesi \u00e8 la frequenza minima. Per ambienti critici (finanza, pubblica amministrazione, sanit\u00e0) la rotazione trimestrale \u00e8 best practice. Avvia sempre una rotazione immediata in questi casi: dipendente che lascia l'azienda, furto o smarrimento di un dispositivo, vulnerability disclosure che coinvolge il tuo setup SSH, o sospetta compromissione di qualsiasi sistema che aveva accesso alla chiave privata.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"cosa-fa-lopzione-a-in-ssh-keygen\">Cosa fa l'opzione -a in ssh-keygen?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">L'opzione <code>-a<\/code> specifica il numero di round della funzione KDF (Key Derivation Function) usata per derivare la chiave di cifratura dalla passphrase. Con il valore predefinito (16-32 round), il brute-force della passphrase \u00e8 relativamente veloce. Con <code>-a 100<\/code>, ogni tentativo richiede 100 round di elaborazione, rallentando gli attacchi di circa 6 volte rispetto al default. Per chiavi ad alta sicurezza considera valori tra 64 e 256 round, bilanciando sicurezza con il tempo accettabile per sbloccare la chiave a ogni utilizzo.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"come-posso-verificare-quale-chiave-ssh-sta-usando-il-client\">Come posso verificare quale chiave SSH sta usando il client?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Usa il flag <code>-v<\/code> (o <code>-vvv<\/code> per il massimo dettaglio): <code>ssh -v mario@server<\/code>. Nell'output trovi la riga \"Offering public key: \/home\/mario\/.ssh\/id_ed25519 ED25519 SHA256:...\" che mostra quale chiave viene offerta, e \"Server accepts key:...\" se viene accettata. Per vedere tutte le chiavi nell'agent: <code>ssh-add -l<\/code>. Per aggiungere una chiave all'agent con timeout automatico di 8 ore: <code>ssh-add -t 8h ~\/.ssh\/id_ed25519<\/code>.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"ssh-certificate-authority-vs-authorized_keys-quale-scegliere\">SSH Certificate Authority vs authorized_keys: quale scegliere?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Con meno di 5 server e un team piccolo, il sistema tradizionale con <code>authorized_keys<\/code> \u00e8 sufficiente. Oltre questa soglia, la SSH Certificate Authority \u00e8 pi\u00f9 sicura e gestibile: non serve toccare ogni server per aggiungere o revocare utenti, i certificati scadono automaticamente, e l'audit \u00e8 centralizzato. Il costo \u00e8 la complessit\u00e0 iniziale del setup e la necessit\u00e0 assoluta di proteggere la chiave privata della CA come il segreto aziendale pi\u00f9 critico.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"ssh-keygen-funziona-su-windows-nel-2026\">ssh-keygen funziona su Windows nel 2026?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">S\u00ec. OpenSSH \u00e8 incluso nativamente in Windows 10 dalla versione 1809 e in Windows 11. Puoi usare <code>ssh-keygen<\/code>, <code>ssh-copy-id<\/code> e <code>ssh-agent<\/code> da Prompt dei comandi o PowerShell. Le chiavi vengono salvate in <code>C:\\Users\\NomeUtente\\.ssh\\<\/code>. Per usare l'agent su Windows, abilita il servizio \"OpenSSH Authentication Agent\" in Servizi di sistema o con PowerShell come amministratore: <code>Set-Service ssh-agent -StartupType Automatic; Start-Service ssh-agent<\/code>. Consulta la <a href=\"https:\/\/ubuntu.com\/server\/docs\/openssh-server\" target=\"_blank\" rel=\"noopener\">documentazione OpenSSH di Ubuntu<\/a> per ulteriori dettagli sull'installazione cross-platform.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"come-funziona-ssh-agent-e-quando-usarlo\">Come funziona ssh-agent e quando usarlo?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">ssh-agent \u00e8 un demone che mantiene in memoria le chiavi private decifrate per la durata della sessione. Quando SSH necessita di autenticarsi, interroga l'agent invece di richiedere la passphrase all'utente. Avvio: <code>eval \"$(ssh-agent -s)\"<\/code>. Aggiunta chiave: <code>ssh-add -t 8h ~\/.ssh\/id_ed25519<\/code> (il flag <code>-t 8h<\/code> imposta un timeout automatico di 8 ore, dopo il quale l'agent rimuove la chiave dalla memoria). Su macOS il Portachiavi integra l'agent automaticamente e la passphrase viene salvata in modo sicuro nel keychain.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Ogni anno, migliaia di server vengono compromessi a causa di chiavi SSH mal gestite: chiavi private senza passphrase, algoritmi obsoleti come RSA a 1024 bit rimasti attivi per anni, agent\u2026<\/p>\n","protected":false},"author":2,"featured_media":278,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[3],"tags":[],"class_list":["post-277","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-security"],"_links":{"self":[{"href":"https:\/\/shattered.io\/it\/wp-json\/wp\/v2\/posts\/277","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/shattered.io\/it\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/shattered.io\/it\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/shattered.io\/it\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/shattered.io\/it\/wp-json\/wp\/v2\/comments?post=277"}],"version-history":[{"count":1,"href":"https:\/\/shattered.io\/it\/wp-json\/wp\/v2\/posts\/277\/revisions"}],"predecessor-version":[{"id":279,"href":"https:\/\/shattered.io\/it\/wp-json\/wp\/v2\/posts\/277\/revisions\/279"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/shattered.io\/it\/wp-json\/wp\/v2\/media\/278"}],"wp:attachment":[{"href":"https:\/\/shattered.io\/it\/wp-json\/wp\/v2\/media?parent=277"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/shattered.io\/it\/wp-json\/wp\/v2\/categories?post=277"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/shattered.io\/it\/wp-json\/wp\/v2\/tags?post=277"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}