Autenticazione e identità digitale: MFA, OAuth, SSO, JWT e come si attaccano
Introduzione
L’autenticazione è il processo con cui un sistema verifica che tu sia chi dici di essere. Sembra semplice, eppure è uno dei campi più complessi e più attaccati della sicurezza informatica. La maggior parte delle breach aziendali inizia con credenziali compromesse: password rubate, token intercettati, sessioni dirottate.
Capire come funzionano i meccanismi di autenticazione moderni (e dove sono vulnerabili), è essenziale sia per chi costruisce sistemi che per chi li difende o li attacca.
I tre fattori di autenticazione
L’autenticazione si basa su tre categorie fondamentali, note come i tre fattori:
graph TD
A[Fattori di autenticazione] --> B["Qualcosa che SAI\nPassword, PIN, risposta\na domanda segreta"]
A --> C["Qualcosa che HAI\nSmartphone, token hardware\nSmart card, chiave fisica"]
A --> D["Qualcosa che SEI\nImpronta digitale\nFace ID, iride, voce\nbiometria comportamentale"]
Ognuno ha punti di forza e debolezze:
| Fattore | Punti di forza | Debolezze |
|---|---|---|
| Qualcosa che sai | Nessun hardware necessario | Rubabile, dimenticabile, prevedibile |
| Qualcosa che hai | Fisicamente presente | Perdibile, rubabile fisicamente |
| Qualcosa che sei | Non trasferibile | Non modificabile se compromessa, falsi positivi/negativi |
Multi-Factor Authentication (MFA)
L’MFA richiede due o più fattori distinti per autenticarsi. Se un attaccante ruba la password, non basta, ha bisogno anche del secondo fattore.
sequenceDiagram
participant U as Utente
participant S as Server
U->>S: Username + Password (fattore 1: qualcosa che sai)
S->>U: Credenziali corrette. Inserisci il codice OTP
U->>U: Apre app authenticator sul telefono
U->>S: Codice OTP a 6 cifre (fattore 2: qualcosa che hai)
S->>U: Accesso concesso
Secondo Microsoft, l’MFA blocca il 99,9% degli attacchi automatizzati di account takeover.
TOTP: Time-based One-Time Password
Il meccanismo più diffuso per il secondo fattore. L’app authenticator (Google Authenticator, Authy, Aegis) genera un codice a 6 cifre che cambia ogni 30 secondi, basandosi su:
TOTP = HMAC(chiave_segreta, timestamp_corrente / 30)
La chiave segreta viene condivisa una volta sola al momento della configurazione (il QR code che scansioni). Né il server né il client trasmettono mai la chiave, solo il codice derivato da essa, che è valido solo per 30 secondi.
SMS OTP vs App Authenticator
L’OTP via SMS è meglio di niente, ma è vulnerabile:
SIM Swapping: l’attaccante convince l’operatore telefonico di essere la vittima e trasferisce il numero su una sua SIM. Tutti gli SMS OTP arrivano all’attaccante. Casi documentati di SIM swapping contro account di criptovalute con perdite milionarie.
SS7 Vulnerabilities: il protocollo SS7 usato dalle reti telefoniche ha vulnerabilità note che permettono a chi vi ha accesso (governi, servizi di intelligence, criminali con accesso a carrier compiacenti) di intercettare SMS da qualsiasi numero.
L’app authenticator non ha questi problemi perché non usa la rete telefonica, usa solo l’orologio del dispositivo e la chiave segreta condivisa.
FIDO2 / WebAuthn: il futuro dell’MFA
FIDO2 è lo standard più sicuro disponibile oggi. Usa crittografia a chiave pubblica invece di codici condivisi:
sequenceDiagram
participant U as Utente
participant B as Browser
participant S as Server
participant K as Chiave FIDO2 (hardware o dispositivo)
Note over U,K: Registrazione
S->>B: Challenge di registrazione
B->>K: Genera coppia chiavi per questo dominio
K->>U: Verifica biometrica o PIN
U->>K: Confermato
K->>B: Chiave pubblica + attestation
B->>S: Registra chiave pubblica
Note over U,K: Login
S->>B: Challenge casuale
B->>K: Firma il challenge
K->>U: Verifica biometrica o PIN
U->>K: Confermato
K->>B: Challenge firmato con chiave privata
B->>S: Verifica firma con chiave pubblica
S->>U: Accesso concesso
Vantaggi chiave di FIDO2:
- La chiave privata non lascia mai il dispositivo
- La firma è vincolata al dominio: resistente al phishing (un sito fake non ottiene mai la firma corretta)
- Non ci sono segreti condivisi da rubare dal server
- Nessun codice da digitare: esperienza utente migliore
Sessioni e cookie
Dopo l’autenticazione, il server deve ricordare che l’utente è autenticato. HTTP è stateless, ogni richiesta è indipendente. La soluzione sono le sessioni.
sequenceDiagram
participant U as Browser
participant S as Server
U->>S: POST /login {username, password}
S->>S: Verifica credenziali
S->>S: Crea sessione ID: "abc123xyz..."
S->>U: 200 OK\nSet-Cookie: session=abc123xyz; HttpOnly; Secure; SameSite=Strict
U->>S: GET /dashboard\nCookie: session=abc123xyz
S->>S: Cerca "abc123xyz" nella session store
S->>U: 200 OK (pagina dashboard)
Attributi di sicurezza dei cookie
HttpOnly: il cookie non è accessibile via JavaScript (document.cookie). Protegge dal furto del cookie tramite XSS.
Secure: il cookie viene trasmesso solo su connessioni HTTPS. Protegge dall’intercettazione su canali non cifrati.
SameSite: controlla quando il cookie viene inviato nelle richieste cross-site.
Strict: mai inviato nelle richieste cross-site, protegge da CSRFLax: inviato solo per navigazione top-level (click su link)None: inviato sempre (richiede Secure)
Session Hijacking
Se un attaccante ottiene il cookie di sessione, può impersonare l’utente senza conoscere la password.
Vettori di furto del cookie:
- XSS: script iniettato legge
document.cookie(prevenuto da HttpOnly) - Sniffing: sessione trasmessa su HTTP non cifrato (prevenuto da Secure)
- Man-in-the-Middle: intercettazione del cookie in transito
- Malware sul client: keylogger o stealer che legge i cookie direttamente dal browser
JWT: JSON Web Token
I JWT sono un meccanismo di autenticazione stateless: invece di memorizzare la sessione sul server, il server emette un token che il client porta con sé in ogni richiesta.
Struttura del JWT
Un JWT è composto da tre parti codificate in Base64url, separate da punti:
header.payload.signature
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.
eyJzdWIiOiJ1c2VyMTIzIiwicm9sZSI6ImFkbWluIiwiZXhwIjoxNjgwMDAwMDAwfQ.
SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
Header: algoritmo di firma e tipo di token
{ "alg": "HS256", "typ": "JWT" }
Payload: i claim, dati sull’utente e sulla sessione
{ "sub": "user123", "role": "admin", "exp": 1680000000 }
Signature: verifica che il token non sia stato modificato
HMAC-SHA256(base64url(header) + "." + base64url(payload), chiave_segreta)
Come funziona l’autenticazione JWT
sequenceDiagram
participant U as Client
participant S as Server
U->>S: POST /login {username, password}
S->>S: Verifica credenziali
S->>S: Genera JWT firmato con chiave segreta
S->>U: 200 OK {token: "eyJ..."}
U->>U: Memorizza token (localStorage o cookie)
U->>S: GET /api/data\nAuthorization: Bearer eyJ...
S->>S: Verifica firma del JWT
S->>S: Legge claims dal payload
S->>U: Dati richiesti
Il server non memorizza nulla, può verificare qualsiasi JWT con la sua chiave segreta. Scalabile orizzontalmente.
Vulnerabilità dei JWT
Algorithm confusion (alg: none): alcune implementazioni difettose accettano JWT con algoritmo “none”, nessuna firma. Un attaccante può modificare il payload (es. "role": "admin") e firmare con “none”. Il server accetta il token modificato.
RS256 to HS256 confusion: se il server supporta sia RSA che HMAC, un attaccante può prendere la chiave pubblica del server (pubblica per definizione), modificare il token payload, e firmarlo con HMAC usando quella chiave pubblica come segreto. Se il server non valida l’algoritmo correttamente, accetta il token falsificato.
Weak secret: JWT firmati con HMAC-SHA256 con un segreto debole possono essere craccati offline. Un attaccante che ha un token valido può fare brute force del segreto.
Missing expiration: JWT senza campo exp o con scadenza molto lunga rimangono validi anche dopo il logout o il cambio password, non c’è modo di invalidarli senza mantenere una blacklist (che vanifica il vantaggio stateless).
OAuth 2.0
OAuth 2.0 è un protocollo di autorizzazione (non autenticazione) che permette a un’applicazione di accedere a risorse di un altro servizio per conto dell’utente, senza condividere le credenziali.
Il caso d’uso classico: “Accedi con Google” su un sito di terze parti.
sequenceDiagram
participant U as Utente
participant App as Applicazione terze parti
participant Auth as Server di autorizzazione (Google)
participant API as Resource Server (Google API)
U->>App: Clicca "Accedi con Google"
App->>U: Redirect a Google con client_id e scope
U->>Auth: Login su Google + consenso a condividere dati
Auth->>U: Redirect all'app con authorization code
U->>App: authorization code (via browser)
App->>Auth: authorization code + client_secret
Auth->>App: access token + refresh token
App->>API: Richiesta con access token
API->>App: Dati dell'utente
Scope: definisce cosa l’applicazione può fare. email profile permette di leggere nome ed email. https://www.googleapis.com/auth/gmail.readonly permette di leggere le email. L’utente vede esplicitamente cosa sta autorizzando.
Access token: valido per poco tempo (ore). Usato per le richieste API.
Refresh token: valido più a lungo (giorni/settimane). Usato per ottenere nuovi access token senza richiedere nuovamente l’autenticazione.
Vulnerabilità OAuth
Authorization Code Interception: se il redirect URI non è validato correttamente, un attaccante può registrare un URI simile e intercettare il codice di autorizzazione.
CSRF sul flusso OAuth: senza state parameter, un attaccante può ingannare l’utente a completare un flusso OAuth con un account controllato dall’attaccante.
Token leakage: access token nei log, negli header Referer, o in URL non devono mai essere esposti.
OpenID Connect (OIDC)
OpenID Connect è un layer di autenticazione costruito sopra OAuth 2.0. Se OAuth dice “puoi accedere a queste risorse”, OIDC dice “questo è chi sei”.
Aggiunge un ID Token (un JWT) al flusso OAuth che contiene informazioni sull’identità dell’utente autenticato: chi è, quando si è autenticato, con quale metodo.
{
"iss": "https://accounts.google.com",
"sub": "110169484474386276334",
"aud": "client_id_dell_applicazione",
"email": "mario.rossi@gmail.com",
"name": "Mario Rossi",
"iat": 1680000000,
"exp": 1680003600
}
Single Sign-On (SSO)
Il SSO permette all’utente di autenticarsi una volta sola e accedere a tutte le applicazioni collegate senza riauthenticarsi per ciascuna.
graph LR
U[Utente] -->|Login una volta| IDP[Identity Provider\nOkta, Azure AD\nKeycloak]
IDP -->|Token valido| A1[App HR]
IDP -->|Token valido| A2[App CRM]
IDP -->|Token valido| A3[App Email]
IDP -->|Token valido| A4[App Jira]
SAML (Security Assertion Markup Language): standard XML-based usato prevalentemente in ambienti enterprise. L’IdP emette asserzioni SAML che le applicazioni verificano.
OIDC/OAuth per SSO: approccio moderno, più leggero e adatto ad applicazioni web e mobile.
Rischio SSO: Single Point of Failure
Il vantaggio dell’SSO è anche il suo rischio principale: se l’account IdP viene compromesso, l’attaccante accede a tutte le applicazioni. Per questo l’MFA sull’IdP è non negoziabile in ambienti enterprise.
Attacchi all’autenticazione: panoramica
graph TD
A[Attacchi all'autenticazione] --> B["Credential stuffing\nPassword rubate da breach"]
A --> C["Brute force\nAttacco dizionario"]
A --> D["Phishing\nPagina login falsa"]
A --> E["MFA bypass\nSIM swap, SS7, fatigue"]
A --> F["Session hijacking\nFurto cookie/token"]
A --> G["OAuth attacks\nCSRF, redirect URI"]
A --> H["JWT attacks\nalg:none, weak secret"]
MFA Fatigue Attack
Un attacco moderno sempre più comune: l’attaccante ha già la password (rubata o crackata). Il sistema invia una notifica push MFA all’utente. L’attaccante invia decine di richieste push in rapida successione, a qualsiasi ora, finché l’utente non accetta per sbaglio, per frustrazione, o per fermare le notifiche.
Microsoft, Cisco, Uber, tutti hanno subito breach iniziati con MFA fatigue.
Difesa: passare a metodi MFA che richiedono un’azione deliberata con context matching (es. FIDO2, o push MFA con number matching, l’utente deve inserire un numero mostrato sul computer, non solo premere “approva”).
Best practice per l’autenticazione sicura
Per gli sviluppatori:
- Non implementare mai da zero: usa librerie e framework consolidati (Passport.js, Spring Security, Django auth)
- Argon2id per hashing delle password
- JWT con scadenza breve + refresh token rotation
- Validazione rigorosa dei redirect URI in OAuth
- Rate limiting sui tentativi di login
- Account lockout con backoff esponenziale
- Log di tutti gli eventi di autenticazione (successi, fallimenti, cambio password)
Per gli utenti:
- Password unica per ogni servizio (password manager)
- MFA su tutti gli account critici, preferibilmente con app authenticator o FIDO2
- Verifica sempre il dominio prima di inserire le credenziali
- Non “approvare” notifiche MFA che non hai richiesto tu
Conclusione
L’autenticazione è il confine tra chi è autorizzato e chi non lo è, e gli attaccanti investono enormi risorse nel attraversarlo. I meccanismi moderni (FIDO2, JWT con rotazione, OAuth ben implementato) sono significativamente più sicuri della semplice password, ma ogni meccanismo ha le proprie vulnerabilità specifiche.
La tendenza del settore è chiara: verso l’autenticazione senza password con Passkey/FIDO2. Nel frattempo, MFA robusto + password manager + sessioni brevi è la combinazione che riduce drasticamente la superficie di attacco.