Autenticazione a due fattori su WordPress: TOTP, passkey e password applicative

TOTP, passkey, codici via email e di recupero: come proteggere gli accessi a WordPress, e perché le password applicative non passano dal secondo fattore.

Un modulo di accesso con nome utente, password e campo del codice di verifica in cui si sta digitando un codice di sei cifre; accanto, un telefono che mostra lo stesso codice con il conto alla rovescia dei secondi rimasti e una scheda con un’impronta digitale, simbolo delle passkey

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.

Matrice che confronta quattro metodi di accesso, cioè solo password, password con codice via email, password con codice TOTP e passkey o chiave hardware WebAuthn, rispetto a quattro minacce: credential stuffing, password rubata da un infostealer, pagina di phishing che inoltra i dati in tempo reale e casella email compromessa. La sola password non protegge da nessuna. Il codice via email protegge da stuffing e infostealer ma non dal phishing in tempo reale né dalla casella compromessa. Il TOTP protegge da stuffing, infostealer e casella compromessa ma non dal phishing in tempo reale. La passkey protegge da tutte e quattro. Una fascia rossa in basso ricorda che il furto del cookie di una sessione già aperta aggira tutti i metodi.
Ogni metodo chiude alcune strade e ne lascia aperte altre; nessuno protegge una sessione già rubata.

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.

Schema del funzionamento del TOTP. Alla configurazione, il sito genera un segreto e lo mostra come codice QR, che l’app sul telefono memorizza; il sito lo salva nei metadati dell’utente. A ogni accesso, sito e app calcolano separatamente la stessa formula: il segreto e il numero dell’intervallo corrente, cioè l’ora Unix divisa per 30, entrano in un HMAC-SHA1, il risultato viene troncato a sei cifre. Il sito confronta il codice digitato con il proprio calcolo, tollerando qualche intervallo di scarto. Due note: l’orologio del server deve essere sincronizzato, e chi legge il database può generare i codici.
Sito e app non si parlano: calcolano la stessa funzione dello stesso segreto e dello stesso orologio.

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 );
Schema delle vie d’accesso a un sito WordPress e di quali passano dal secondo fattore. Il modulo wp-login.php chiede password e secondo fattore. L’API REST e XML-RPC con password applicativa non chiedono il secondo fattore, per progetto. XML-RPC con la password normale, accettata dal core, deve essere bloccato dal plugin di 2FA. Un cookie di sessione già valido, per esempio rubato da un infostealer, non passa più dal login. Fuori da WordPress, il pannello dell’hosting, SFTP e SSH, il database e il registrar del dominio hanno credenziali proprie, che richiedono un 2FA separato.
Il 2FA protegge il login interattivo: password applicative, sessioni già aperte e accessi fuori da WordPress vanno gestiti a parte.

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.

IL PROSSIMO PASSO

Il tuo sito
è infetto?

Ogni ora conta: il malware continua a colpire visitatori e clienti, e Google, l’hosting e le blacklist non aspettano. Scegli il piano, paga online e condividi gli accessi: la bonifica parte subito, a prezzo fisso e con tempi garantiti.

Richiedi la bonifica

Ti porta ai piani: bonifica entro 6, 12 o 24 ore dalla ricezione degli accessi.

MANAGED SERVER S.R.L.info@rimozionemalware.com +39 02 5656 9681

Lunedì – venerdì · 09:30 – 19:30

Preferisci un modulo online?
Contattaci su managedserver.it ↗