Tra i siti che bonifichiamo, una parte importante non è stata violata attraverso un plugin vulnerabile ma attraverso la porta principale: qualcuno è entrato in wp-login.php con la password giusta. Una password riutilizzata altrove e finita in una raccolta di credenziali trapelate, oppure rubata da un infostealer sul computer di un collaboratore. Dal punto di vista di WordPress quell’accesso è perfettamente legittimo, e nei log non lascia nulla di anomalo.
Il secondo fattore non rende una password più robusta: la rende insufficiente. Chi l’ha rubata ha in mano metà di quello che serve, e la metà che manca non si trova nella stessa raccolta di dati.
In questo articolo vediamo come funzionano i metodi disponibili su WordPress, cioè codici TOTP, passkey, codici via email e codici di recupero, quale plugin usiamo e perché, il punto che quasi tutti trascurano, ovvero le password applicative che per progetto non passano dal secondo fattore, e come verificare con WP-CLI chi è davvero protetto.
Perché la password da sola non basta più
Gli attacchi alle credenziali di WordPress seguono due strade. La prima è il credential stuffing: coppie di email e password trapelate da altri servizi vengono provate in automatico su migliaia di siti, contando sul fatto che molte persone riutilizzano la stessa password. Non è un attacco a forza bruta: i tentativi per sito sono pochi e mirati, e un limite ai tentativi di accesso li rallenta appena. La seconda sono gli infostealer, malware che sul computer della vittima raccolgono le password salvate nel browser, i cookie di sessione e le configurazioni dei client FTP, come abbiamo raccontato nell’articolo sulle credenziali rubate dal PC dello sviluppatore.
In entrambi i casi la password è corretta, e nessuna regola di complessità avrebbe aiutato. Il secondo fattore è efficace proprio perché introduce qualcosa che non si trova nello stesso posto della password: un segreto su un telefono, una chiave crittografica legata al dispositivo. Va detto subito, però, che non copre tutto. Se un infostealer ruba il cookie di una sessione già autenticata, l’attaccante entra senza passare dal login, e quindi senza passare dal secondo fattore. Per questo il 2FA si accompagna sempre a sessioni di durata ragionevole e alla revoca delle sessioni quando si sospetta un furto.
TOTP: il codice che cambia ogni trenta secondi
Il metodo più diffuso è il codice a tempo generato da un’app di autenticazione, standardizzato nella RFC 6238. Al momento della configurazione il sito genera un segreto casuale e lo mostra sotto forma di codice QR; l’app lo memorizza. Da quel momento sito e app calcolano, ciascuno per conto proprio, un HMAC del segreto e dell’ora corrente divisa in intervalli di trenta secondi, e ne estraggono sei cifre. Se i due risultati coincidono, chi sta accedendo possiede il segreto. Nulla viene trasmesso tra il sito e il telefono dopo la configurazione, e per questo il TOTP funziona anche senza rete.
Due conseguenze pratiche. La prima: il segreto è conservato nel database del sito, nei metadati dell’utente. Chi ha accesso al database, per esempio tramite una SQL injection o un backup esposto, può generare i codici; il TOTP protegge dal furto della password, non dalla compromissione del server. La seconda: l’orologio del server deve essere sincronizzato, altrimenti i codici risultano sbagliati per ragioni che nessuno capisce al primo colpo. Un servizio NTP attivo, come chrony o systemd-timesyncd, risolve.
Anche l’app merita un pensiero. Molte app di autenticazione offrono il backup dei segreti su un account cloud, comodo quando si cambia telefono: in quel caso la sicurezza del secondo fattore diventa quella dell’account cloud, che a sua volta dovrebbe essere protetto da un secondo fattore diverso. E il segreto mostrato nel codice QR va trattato come una password: una schermata inviata in chat a un collega per «configurare anche lui l’accesso» vale quanto la chiave.
Passkey e WebAuthn: il fattore che il phishing non ruba
Il TOTP ha un limite che conviene conoscere: il codice si digita a mano, e una pagina di phishing ben fatta può chiederlo e inoltrarlo al sito vero entro i trenta secondi di validità. Le passkey eliminano il problema alla radice. Si basano sullo standard WebAuthn del W3C: il dispositivo crea una coppia di chiavi per ogni sito, conserva la chiave privata e firma una sfida del server solo se il dominio della pagina corrisponde a quello per cui la chiave è stata creata. Su un dominio di phishing la firma semplicemente non avviene. Le linee guida NIST SP 800-63B riconoscono esplicitamente WebAuthn come resistente al phishing, e altrettanto esplicitamente escludono da questa categoria i codici che si digitano a mano, TOTP compresi.
Le passkey sono credenziali WebAuthn che possono essere sincronizzate tra i dispositivi dello stesso utente attraverso il gestore di password del sistema operativo o di terze parti, e si sbloccano con impronta, volto o PIN. Le chiavi hardware, come quelle USB o NFC, sono la variante non sincronizzabile: più scomode, ma legate a un oggetto fisico. Per un amministratore WordPress entrambe sono un salto di qualità rispetto al TOTP, e il server conserva solo chiavi pubbliche, inutili a chi dovesse leggere il database.
Codici via email e codici di recupero
Il codice inviato via email è il metodo più facile da attivare, perché non richiede nessuna app, ed è meglio di niente contro il credential stuffing. Ha però un difetto strutturale: la casella email è spesso protetta dalla stessa password, o è quella usata per reimpostare la password del sito. Chi controlla la casella controlla entrambi i fattori. Non a caso le linee guida del NIST stabiliscono che l’email non debba essere usata come canale di autenticazione fuori banda. Lo consideriamo accettabile per utenti a basso privilegio, non per gli amministratori.
I codici di recupero sono l’opposto: non servono ad accedere ogni giorno, ma a non restare chiusi fuori quando il telefono si rompe o si perde. Sono una manciata di codici monouso, generati una volta e da conservare fuori dal computer principale, in un gestore di password o stampati in un luogo sicuro. Un amministratore senza codici di recupero, e senza un secondo amministratore che possa intervenire, è una richiesta di assistenza urgente che aspetta solo di accadere.
Il plugin Two Factor e i suoi limiti
Esistono molti plugin per il secondo fattore, spesso inclusi in suite di sicurezza più ampie. Quello che installiamo di preferenza è Two Factor, sviluppato dalla comunità di WordPress come feature plugin: fa una cosa sola, ha un codice leggibile e non aggiunge pannelli o servizi esterni. Supporta app di autenticazione TOTP, codici via email e codici di recupero; il vecchio supporto FIDO U2F è stato rimosso perché i browser non lo supportano più. Per le passkey si affianca un’estensione separata, Two-Factor Provider: WebAuthn, che aggiunge chiavi hardware e autenticatori di piattaforma come Windows Hello e Touch ID.
Il limite principale di Two Factor è che non impone nulla: ogni utente sceglie se attivarlo dal proprio profilo, e un amministratore che non lo fa resta protetto dalla sola password. Il plugin espone dei filtri, come two_factor_providers per restringere i metodi disponibili, ma l’obbligo per ruolo va aggiunto, con un plugin che lo preveda o con poche righe di codice, come vedremo più avanti. Two Factor, inoltre, introduce un ritardo crescente dopo ogni codice sbagliato, che raddoppia a ogni errore fino a un massimo predefinito di quindici minuti, così che provare tutte le combinazioni di sei cifre non sia praticabile. Chiunque scelga, conviene verificare tre cose: che il 2FA si applichi anche a XML-RPC, che i codici di recupero esistano e che ci sia un limite ai tentativi sul secondo fattore.
Password applicative: la porta che il 2FA non chiude
Dalla versione 5.6 WordPress include le password applicative: credenziali generate dal profilo utente per far accedere applicazioni esterne, come un’app mobile o un servizio di pubblicazione, all’API REST e a XML-RPC. Non funzionano nel modulo di login, sono revocabili una per una e, per impostazione predefinita, sono disponibili solo sui siti in HTTPS. Soprattutto, per progetto, non passano dal secondo fattore: sono pensate per programmi che non possono leggere un codice dal telefono.
Questo è corretto e utile, ma ha una conseguenza che nelle bonifiche vediamo spesso: una password applicativa creata da un attaccante durante la compromissione sopravvive al cambio della password dell’utente e all’attivazione del 2FA, e continua a dare accesso completo all’API REST con i privilegi di quell’utente. Lo stesso vale per quelle dimenticate da integrazioni dismesse da anni. C’è poi XML-RPC: nel core accetta anche la password normale, e un plugin di 2FA serio deve bloccarla e ammettere solo le password applicative. Two Factor lo fa per impostazione predefinita per gli utenti che hanno attivato il secondo fattore, ma è una delle prime cose da verificare con qualunque plugin.
# Tutte le password applicative del sito, con ultimo utilizzo e IP
for u in $(wp user list --field=ID); do
wp user application-password list "$u" --fields=uuid,name,created,last_used,last_ip --format=csv \
| sed "1d; s/^/$u,/"
done
# Revocarne una, o tutte quelle di un utente
wp user application-password delete 12 6633824d-c1d7-4f79-9dd5-4586f734d69e
wp user application-password delete 12 --all
Se il sito non ha integrazioni che le usano, la scelta più semplice è disattivarle del tutto con un filtro in un mu-plugin; se servono solo a un utente tecnico, si possono limitare a quello:
<?php
// wp-content/mu-plugins/password-applicative.php
// Disattivarle ovunque:
add_filter( 'wp_is_application_passwords_available', '__return_false' );
// Oppure consentirle solo a un utente di integrazione:
// add_filter( 'wp_is_application_passwords_available_for_user', function ( $available, $user ) {
// return 'integrazione-gestionale' === $user->user_login;
// }, 10, 2 );
Rendere il 2FA obbligatorio e verificarlo con WP-CLI
Un 2FA facoltativo protegge chi è già attento. Per gli amministratori, e per chiunque possa installare plugin o modificare file, deve essere obbligatorio. Con Two Factor basta un mu-plugin che, finché un utente con privilegi di amministrazione non ha configurato un metodo, lo riporti al proprio profilo a ogni pagina della bacheca:
<?php
// wp-content/mu-plugins/2fa-obbligatorio.php
add_action( 'admin_init', function () {
if ( ! class_exists( 'Two_Factor_Core' ) || wp_doing_ajax() ) {
return;
}
$user = wp_get_current_user();
if ( ! user_can( $user, 'manage_options' ) || Two_Factor_Core::is_user_using_two_factor( $user ) ) {
return;
}
global $pagenow;
if ( 'profile.php' !== $pagenow ) {
wp_safe_redirect( admin_url( 'profile.php#two-factor-options' ) );
exit;
}
} );
Non è un blocco al login, che sarebbe rischioso da improvvisare, ma impedisce di usare la bacheca senza configurare il secondo fattore. La verifica periodica si fa da riga di comando: Two Factor registra il metodo principale di ogni utente nel metadato _two_factor_provider, e un utente amministratore senza quel valore è un amministratore senza 2FA. Nello stesso controllo conviene chiudere le sessioni aperte di chi è stato appena messo in regola, così che nessun cookie precedente resti valido:
# Amministratori ed editor, con il metodo 2FA principale (vuoto = nessuno)
for id in $(wp user list --role=administrator --field=ID) $(wp user list --role=editor --field=ID); do
login=$(wp user get "$id" --field=user_login)
metodo=$(wp user meta get "$id" _two_factor_provider 2>/dev/null)
echo "$id $login ${metodo:-NESSUN 2FA}"
done
# Chiudere tutte le sessioni di un utente (serve un nuovo login, quindi il 2FA)
wp user session destroy mario.rossi --all
L’elenco degli amministratori è anche l’occasione per chiedersi se tutti debbano esserlo: un account di un’agenzia che non lavora più al sito, o un utente creato per un intervento una tantum, è un rischio con o senza 2FA. Se nell’elenco compaiono nomi che nessuno riconosce, il problema è un altro, e ne parliamo nell’articolo sugli amministratori fantasma.
Recupero, hosting e registrar: il resto della catena
Prima di rendere obbligatorio il 2FA conviene scrivere la procedura di recupero, perché verrà usata. Se un amministratore perde il telefono e non ha i codici di recupero, un altro amministratore può disattivargli il 2FA dal profilo; se l’amministratore è uno solo, resta la riga di comando, con cui si può rimuovere il metadato del metodo principale o disattivare temporaneamente il plugin. Entrambe le operazioni vanno fatte solo dopo aver verificato l’identità di chi chiede, per un canale diverso dall’email, e registrate: una richiesta urgente di «togliere il 2FA» è anche una tecnica classica di ingegneria sociale.
Infine, il 2FA su WordPress protegge WordPress. Chi ottiene l’accesso al pannello dell’hosting, all’SFTP o al database non passa dal login del sito, e chi entra nel pannello del registrar può spostare l’intero dominio, e con esso il sito e la posta, su server controllati da altri. Lo stesso secondo fattore, preferibilmente una passkey o una chiave hardware, va attivato ovunque ci sia una credenziale capace di modificare il sito: hosting, registrar, repository del codice, casella email dell’amministratore. È uno dei punti della nostra checklist dopo una bonifica, insieme alla revoca delle password applicative e delle sessioni.
Se hai attivato il 2FA dopo un accesso sospetto, ricorda che l’attaccante potrebbe aver lasciato altro: password applicative, utenti nascosti, mu-plugin o cron che il secondo fattore non tocca. In questi casi possiamo verificare il sito per te, a livello applicativo e di sistema, con una bonifica garantita in 6, 12 o 24 ore per ogni installazione WordPress.