Commento alla domanda 1 della sessione pomeridiana dell'esame Registered Information Security Specialist, primavera 2024 (Reiwa 6) — JWT alg=none, autorizzazione API e mitigazione WAF temporanea

· · Specialista registrato in sicurezza delle informazioni, Specialista di sicurezza registrato, API, Sicurezza delle API, JWT, Autenticazione, Autorizzazione, WAF, Log4Shell, Sicurezza delle informazioni, Vulnerabilità, IPA

«Verifichiamo la firma del JWT, quindi all’identificativo utente si può fidarsi.»

Quella affermazione è giusta solo a metà.

La domanda 1 della sessione pomeridiana (PM) dell’esame Registered Information Security Specialist di primavera 2024 (Reiwa 6) è costruita intorno a un’API chiamata da un’app per smartphone.1 Al successo dell’autenticazione viene emesso un JWT, e quel JWT viene allegato alle chiamate verso l’API che recupera e aggiorna le informazioni utente. A prima vista, è un allestimento del tutto ordinario.

Tuttavia la valutazione fa emergere i quattro problemi seguenti.

  1. Cambiare l’alg dell’intestazione JWT in none fa passare un JWT non firmato.
  2. Tenere un JWT valido ma cambiare mid in un identificativo utente diverso permette di leggere o aggiornare le informazioni di qualcun altro.
  3. Aggiungere un status=paid non documentato trasforma un utente del piano gratuito in un utente a pagamento.
  4. Il codice di autenticazione a quattro cifre recapitato per e-mail si può brute-forzare senza limite ai tentativi.

Tutti e quattro sembrano «vulnerabilità adiacenti all’autenticazione», ma le cause non sono le stesse. Ciò che viene rotto sono confini distinti: integrità del token, autorizzazione a livello di oggetto, autorizzazione a livello di proprietà e limite al ritmo dei tentativi.

La seconda metà della domanda aggiunge ancora un altro tema. Viene divulgata una vulnerabilità critica in una libreria open source ampiamente usata, che consente a un attaccante di eseguire codice da remoto abusando di JNDI Lookup. Non esistono ancora né una correzione né una regola WAF finita. Nel frattempo, la domanda chiede come confermare l’impatto, dove il WAF dovrebbe guardare e perché la modalità WAF iniziale dovrebbe essere «rilevamento» anziché «blocco».

Questo articolo usa come fondamento le risposte modello ufficiali2 e il commento di valutazione3, e percorre non solo la risposta a ciascuna domanda ma perché quella è la risposta, e quanto più rigorosamente la si dovrebbe progettare in pratica.

Panoramica della domandaMostra il confine di fiducia rotto a ogni stadio - codice di autenticazione, JWT, autorizzazione API e vulnerabilità della libreriaNessun limite tentativiAmmette alg=noneSi fida di midstatus=paidJNDI/LDAP/HTTPApp utenteCodice a 4 cifreEmissione JWTAPI utenteUscita dei logLibreria vulnerabileEsecuzione remota di codiceLeggere/aggiornare dati di un altroCambiare lo stato di fatturazione

Figura 1: Panoramica della domanda. A ogni stadio viene rotto un confine di fiducia diverso.

1. Prima di tutto, la conclusione

  • La proprietà di un’API RESTful di non tenere stato di sessione si chiama assenza di stato (stateless). Non significa che il server non tenga affatto un database o uno stato utente
  • Un codice di autenticazione a quattro cifre ha 10.000 valori possibili. A 10 tentativi al secondo, un attaccante ha successo in media dopo 5.000 tentativi, cioè 500 secondi — meno del periodo di validità di 10 minuti, quindi la sola scadenza non lo ferma
  • La misura minima contro alg=none è confermare che l’alg dell’intestazione JWT non è NONE. In pratica, però, si dovrebbe fissare l’insieme degli algoritmi ammessi lato server
  • Anche con un JWT valido, al mid della richiesta non ci si deve fidare. O si fa coincidere l’identificativo utente dentro il JWT con mid, o, più in sicurezza, non si accetta affatto mid dal client e si determina il bersaglio dal JWT
  • Aggiungere status=paid è un problema di Mass Assignment, in cui proprietà fuori specifica vengono associate dritto all’oggetto interno. Si usa il DTO di aggiornamento come elenco consentito, e non si lascia mai all’utente cambiare lo stato di fatturazione
  • La risposta modello per la misura anti brute-force è una logica che blocca l’account quando il numero di fallimenti consecutivi supera una soglia. In pratica si aggiungono anche ritardi graduati e controlli per origine
  • Per confermare l’impatto di una vulnerabilità critica appena divulgata, anziché emettere un comando distruttivo, si registrano gli accessi all’index.html di un server di test per confermare che l’esecuzione remota di codice ci arriva davvero
  • Poiché la stringa d’attacco è portata in un’intestazione HTTP, il bersaglio di ispezione del WAF è Header. Come espressione regolare che gestisce lo scambio di maiuscole e minuscole, si usa qualcosa come \W[jJ][nN][dD][iI]\W
  • Il vantaggio di avviare il WAF in modalità «rilevamento» è che impedisce di bloccare il traffico di business legittimo per un falso positivo. Quando arriva un alert, si esamina se è un attacco genuino, poi si passa alla modalità blocco una volta affinata la regola
  • Il WAF è solo una misura temporanea; la correzione di fondo è aggiornare la libreria colpita a una versione patchata

2. Come lo scenario si mappa sulle domande

Lo scenario è nell’azienda G, che lancia un nuovo servizio sanitario. Gli utenti inseriscono dati come i pasti e il peso corporeo tramite un’app per smartphone e ricevono valutazioni del rischio per la salute e consigli dietetici. Il sistema è costruito sul cloud, combinando un API gateway, elaborazione event-driven e un database gestito.

Il fascicolo della domanda astrae i nomi specifici di prodotti e servizi. Questo articolo altrettanto non riproduce i diagrammi né il testo dell’IPA, ma parafrasa solo la struttura necessaria a capire le domande.

Domanda Tema Capitolo di questo articolo
Domanda 1 La natura delle API RESTful Capitolo 4
Domanda 2(1) Tempo per brute-forzare il codice a 4 cifre Capitolo 5
Domanda 2(2) JWT alg=none Capitolo 6
Domanda 2(3) Accedere a un altro utente tramite mid Capitolo 7
Domanda 2(4) Il difetto che accetta uno status fuori specifica Capitolo 8
Domanda 2(5) Misure anti brute-force Capitolo 9
Domanda 3(1) Confermare in sicurezza che esiste una vulnerabilità Capitolo 11
Domanda 3(2)(3) Dove guarda il WAF, e l’espressione regolare Capitolo 12
Domanda 3(4) Il vantaggio della modalità rilevamento e come gestirla Capitolo 13

Il commento di valutazione nota che il tasso complessivo di risposte corrette era circa nella media. Segnala però anche che il tasso di risposte corrette era un po’ più basso per la misura contro la manomissione JWT nella domanda 2(2) e per il meccanismo necessario sul server di verifica nella domanda 3(1). Né l’una né l’altra si risolvono col solo vocabolario. Bisogna seguire quale valore ha cambiato l’attaccante, in quale elaborazione è confluito e dove è finito per essere considerato affidabile.

3. Non è solo un «problema di autenticazione»

Disporre l’intera domanda per confine di fiducia dà quanto segue.

[Identificativo utente / password]
          |
          v
[Controllo del codice a 4 cifre] ---- nessun limite tentativi ----> brute-force
          |
          v
[Emettere JWT]
          |
          v
[Libreria JWT] ------- ammette alg=none ------> manomissione dell'identificativo
          |
          v
[API utente]
    |             |
    |             +-- passa status in blocco ----> difetto di autorizzazione a livello di proprietà
    |
    +-- si fida di mid -----------------------> difetto di autorizzazione a livello di oggetto

[Registrare input esterno]
          |
          v
[Libreria vulnerabile] ---- JNDI/LDAP/HTTP ------> esecuzione remota di codice

La distinzione più importante qui è la seguente.

Controllo Domanda che pone Esempio rotto in questo scenario
Autenticazione Chi sei Brute-force del codice a 4 cifre
Verifica del token Quelle informazioni di identità sono state manomesse alg=none
Autorizzazione a livello di oggetto Questo utente può accedere ai dati di questo utente Scambio di mid
Autorizzazione a livello di proprietà Questo campo può essere cambiato status=paid
Confine da input a esecuzione L’input esterno viene interpretato come comando JNDI Lookup

Il successo di un controllo non è mai un motivo per saltare il successivo. Un utente con un JWT valido non è necessariamente autorizzato a leggere i dati di qualcun altro. Un utente autorizzato ad aggiornare i propri dati non è necessariamente autorizzato a cambiare anche il proprio stato di fatturazione.

Una volta che si riescono a separare questi stadi, la risposta a ciascuna domanda smette di essere qualcosa da memorizzare.

La differenza tra autenticazione e autorizzazioneL'autenticazione conferma il soggetto, l'autorizzazione conferma ciò che a quel soggetto è consentito fareAutenticazionechi seiAutorizzazionecosa ti è consentito

Figura 2: La differenza tra autenticazione e autorizzazione. Prima viene l’autenticazione; l’autorizzazione è un controllo distinto.

4. Domanda 1 — cosa significa «senza stato»

La domanda 1 chiede uno dei principi di progetto delle API RESTful: la proprietà di non effettuare gestione di sessione.

La risposta è assenza di stato (stateless).

Assenza di stato significa che il server non ha bisogno di ricordare lo stato conversazionale della richiesta precedente, perché ogni richiesta da sola porta tutto il necessario per elaborarla. In questa domanda, l’app per smartphone allega un JWT all’intestazione Authorization su ogni richiesta. Il server verifica quel JWT e da esso identifica l’utente di quella richiesta.

Una lettura errata comune è prendere «senza stato» come «il server non tiene affatto stato». In realtà tiene di solito lo stato seguente.

  • Il database che memorizza le informazioni utente e i dati di salute
  • Lo stato di fatturazione
  • Il valore del codice di autenticazione, la scadenza e il conteggio dei fallimenti
  • La chiave di firma del JWT
  • Le informazioni di revoca, per i progetti che usano un elenco di revoca
  • Log e registri di audit

Ciò che non tiene è lo stato di sessione lato server che esiste solo per continuare una conversazione, come presupposto da cui dipende ogni chiamata API.

Essere senza stato non migliora automaticamente nemmeno la sicurezza. Inviare il JWT a ogni richiesta facilita la scalabilità orizzontale, ma se la verifica JWT è sbagliata, quell’errore si propaga in modo uniforme su ogni nodo. Una proprietà architetturale e la correttezza di sicurezza sono due cose diverse.

5. Domanda 2(1) — un codice a quattro cifre si spezza in media in 500 secondi

L’API di autenticazione invia un numero a quattro cifre per e-mail una volta che identificativo e password coincidono. Poi emette un JWT una volta che identificativo e codice a quattro cifre coincidono. Il codice è valido 10 minuti dalla generazione.

Nella valutazione erano possibili 10 tentativi al secondo. La domanda chiede quanti secondi ci vogliono, in media, per sfondare.

Il calcolo è «metà dello spazio dei candidati»

Un numero a quattro cifre, compreso lo zero iniziale, ha le seguenti 10.000 possibilità.

0000, 0001, 0002, ... , 9999

Se la risposta corretta è estratta in modo uniforme a caso, un attaccante che prova i candidati in ordine senza ripetizioni raggiunge quella corretta, in media, dopo metà dello spazio dei candidati.

Numero medio di tentativi = 10.000 / 2 = 5.000
Tempo medio               = 5.000 / 10 tentativi al secondo = 500 secondi

Quindi lo spazio b è 500.

Il caso peggiore richiede fino a 1.000 secondi, ma la domanda chiede la media. E il periodo di validità del codice è 600 secondi — più lungo del tempo medio di rottura di 500 secondi. Ecco perché si giudica «probabile che venga spezzato».

Sensazione di scala del codice di autenticazione a 4 cifreProvare 10.000 candidati a 10 al secondo dà in media 5.000 tentativi e 500 secondi, meno del periodo di validità di 600 secondiMedia 10.000 / 2 = 5.000 tentativi500 secondi è meno di 600 secondi10.000 candidatiTempo medio di rottura 500 secondiPeriodo di validità 600 secondiSi può spezzare nel periodo di validità

Figura 9: Sensazione di scala del codice di autenticazione a quattro cifre. Provare in media metà dello spazio dei candidati lo spezza entro il periodo di validità.

Accorciare solo il tempo di scadenza perde se lo spazio dei candidati è piccolo

La forza di un codice di autenticazione non è determinata né dal solo numero di cifre né dal solo periodo di validità.

Numero di tentativi possibili durante il periodo di validità
= tentativi al secondo × periodo di validità
= 10 × 600
= 6.000 tentativi

Provando valori non ripetuti in ordine, un attaccante può controllare il 60% delle 10.000 possibilità entro il periodo di validità. Impostare un tempo di scadenza non è sufficiente da solo se il numero di tentativi non è limitato.

L’attuale NIST SP 800-63B richiede almeno sei cifre per i segreti a breve termine usati nell’autenticazione out-of-band, e impone un limite al ritmo dei tentativi ogni volta che il segreto ha meno di 64 bit di entropia. Chiede anche di non usare l’e-mail per l’autenticazione out-of-band.4 La risposta d’esame lavora entro la specifica data di un codice a quattro cifre inviato per e-mail, ma per un progetto nuovo in pratica quella premessa stessa andrebbe riconsiderata.

6. Domanda 2(2) — alg=none è un problema di «lasciare che l’attaccante scelga il metodo di verifica»

Il JWT in questa domanda è composto da tre parti: intestazione, payload e firma.

base64url(header).base64url(payload).base64url(signature)

L’intestazione registrava RS256 come algoritmo usato per la firma. Il payload contiene l’identificativo utente, l’ora di emissione e la scadenza.

Il valutatore ha cambiato le due cose seguenti.

  1. Cambiare l’alg dell’intestazione da RS256 a NONE.
  2. Cambiare l’identificativo utente del payload in un utente diverso.

Inviando quel JWT, la verifica è riuscita e ha permesso al valutatore di impersonare qualcun altro.

Flusso dell'attacco JWT alg=noneCambiare alg in none in un JWT valido e riscrivere l'identificativo utente fa passare la richiestaCambiare alg dell'intestazione in noneSalta la verifica della firmaJWT validoalg=RS256user=user01JWT manomessoalg=noneuser=user02Il server lo accettacome user02

Figura 3: Flusso dell’attacco JWT alg=none. L’attaccante sceglie l’algoritmo di verifica.

none non è un refuso

RFC 7519 definisce un «Unsecured JWT» — un JWT senza firma né cifratura, il cui alg è none.5 Quindi il valore none non è qualcosa che semplicemente non esiste nella specifica.

Il problema è che un’API che avrebbe dovuto accettare solo JWT firmati ha accettato il none specificato dall’attaccante.

Scritto in modo concettuale, il processo vulnerabile è così.

1. Leggere l'intestazione JWT.
2. Guardare l'alg scritto nell'intestazione e scegliere il metodo di verifica.
3. Se alg è none, non verificare la firma.
4. Fidarsi dell'identificativo utente nel payload.

La forza stessa della sicurezza viene scelta da un input che l’attaccante controlla.

La risposta d’esame

La domanda chiede, in 20 caratteri o meno ciascuno, quali dati la libreria Q corretta dovrebbe verificare, e cosa quella verifica dovrebbe controllare.

La risposta modello è la seguente.

Voce Nocciolo della risposta
Dati da verificare Il valore specificato nell’alg dell’intestazione JWT
Cosa verificare Che non è NONE

Come correzione diretta della vulnerabilità descritta nella domanda, è corretta.

In pratica, non accontentarsi di «tutto tranne NONE»

Qui bisogna separare la risposta d’esame dalla raccomandazione pratica.

RFC 8725 afferma che una libreria JWT dovrebbe far specificare al chiamante un insieme di algoritmi ammessi, e che nulla al di fuori di quell’insieme può essere usato.6 In altre parole, l’idea è questa.

Approccio sbagliato:
  Accettare se token.header.alg != "none"

Approccio giusto:
  Accettare solo se è contenuto in serverConfig.allowedAlgorithms
  es. allowedAlgorithms = ["RS256"]

Rifiutare solo none può comunque lasciare altri algoritmi deboli, o la possibilità di confusione di algoritmo in cui uno schema a chiave pubblica viene scambiato per uno a chiave simmetrica. Il principio è non continuare ad aggiungere condizioni negative su cosa accettare, ma fissare un insieme stretto e positivo di ciò che è consentito.

La verifica JWT dovrebbe confermare non solo l’algoritmo ma, a seconda del caso d’uso, almeno anche quanto segue.

Voce Cosa confermare
Firma Si può verificare con la chiave e l’algoritmo attesi
iss È un emittente fidato
aud Il token è stato emesso per questa API
exp È entro il periodo di validità
nbf Non è prima del tempo «non valido prima»
sub o identificativo utente È un soggetto valido nell’applicazione
Tipo di token Non si confonde un ID token con un access token, ecc.

In questa domanda il nome della chiave del payload è user, ma in pratica si dovrebbe o usare lo sub standard o definire chiaramente il significato di un claim personalizzato.

Verifica JWT sicura contro non sicuraLa verifica non sicura dipende da alg, quella sicura usa un elenco consentito lato serverVerifica sicuraAlgoritmi ammessi configurati sul serveres. RS256Confermare che l'alg dell'intestazione JWTè nell'elenco consentitoVerificare firma, iss, aud, expVerifica non sicuraLeggere alg dall'intestazione JWTAccettare se alg è none

Figura 4: Verifica sicura contro non sicura. In pratica si fissa un insieme stretto di algoritmi ammessi.

Base64url non è cifratura

C’è un altro malinteso comune sui JWT. Intestazione e payload sono rappresentati in base64url, ma quella non è cifratura. Chiunque può decodificarli e leggerli.

Ciò che la firma garantisce, e solo quando verifica correttamente, è che il contenuto non è stato manomesso dall’emissione. Non significa che informazioni personali che si vogliono tenere segrete possano andare nel payload di un JWT firmato.

7. Domanda 2(3) — anche con un JWT valido, cambiare mid ha letto i dati di qualcun altro

Poi un attacco che non manomette il JWT stesso.

L’API utente riceve un identificativo utente chiamato mid su GET o PUT. Il modulo comune P recupera o aggiorna, nel database, le informazioni utente legate a quel mid.

La struttura dell’attacco è semplice.

Identificativo utente nel JWT: user01    <- un JWT firmato correttamente
mid della richiesta:           user02   <- cambiato dall'attaccante

La firma del JWT è valida, quindi l’autenticazione ha successo. Ma l’API si fida di mid=user02 così com’è e restituisce le informazioni di user02.

È un caso da manuale di ciò che l’OWASP API Security Top 10 2023 chiama Broken Object Level Authorization (BOLA). Ogni volta che si accede ai dati usando un identificativo di oggetto che l’utente ha specificato, l’autorizzazione per quello specifico oggetto va controllata ogni volta.7

L'attacco BOLAUsare un JWT valido cambiando il mid della richiesta in un identificativo utente diversoJWT user01mid user02Si fida di midAttaccanteAPI utenteRestituisce i dati di user02 dal DB

Figura 5: L’attacco BOLA. L’autenticazione passa, ma l’autorizzazione non è mai stata controllata.

La risposta della domanda

La sottolineatura 2 nella tabella 5 chiede, in 40 caratteri o meno, l’elaborazione da aggiungere alla chiamata nel modulo comune P.

La risposta modello è:

Logica che verifica se l’identificativo utente contenuto nel JWT coincide con il valore di mid

Il vantaggio di verificare dentro il modulo comune P è che lo stesso controllo di autorizzazione è facile da applicare sia a GET sia a PUT, e a qualsiasi API futura che usi P. Copiare lo stesso confronto in ogni schermata o endpoint individualmente significa che mancherà da qualche parte.

Un progetto più sicuro è non accettare affatto mid

Per un’API che recupera o aggiorna solo le informazioni del chiamante, non c’è bisogno di accettare un identificativo utente dal client.

GET /users/me
Authorization: Bearer <JWT>

Lato server, il soggetto viene estratto dal JWT già verificato.

principal = validateJwt(request.authorization)
userId = principal.subject
return repository.getUser(userId)

Lo stesso vale per gli aggiornamenti.

principal = validateJwt(request.authorization)
input = validateProfileUpdate(request.body)
repository.updateProfile(
    userId = principal.subject,
    name = input.name,
    age = input.age
)

Un controllo di confronto protegge se lo si scrive. Ma un progetto che non accetta mai l’identificativo bersaglio dall’esterno riduce l’esistenza stessa della classe di bug in cui si dimentica di scrivere quel confronto.

Se un amministratore deve operare sulle informazioni di un altro utente, si separa così.

PUT /users/me                  Per gli utenti generali
PUT /admin/users/{userId}      Per gli amministratori

Per il percorso amministratore, si richiedono permessi distinti, logging di audit e, se serve, una riautenticazione. Questo rende i confini della politica di autorizzazione molto più visibili di «aggiungere un’eccezione all’API utente generale solo per gli amministratori».

Come prevenire BOLAInvece di usare il mid della richiesta, decidere o controllare il bersaglio dal subject del JWTGET /users/me + JWTPrendere sub dal JWTCoincideNon coincideNessun midUtenteAPISe mid è presentecoincide con subRestituire i propri datiRifiuto 403Ricerca DB per JWT sub

Figura 6: Come prevenire BOLA. O non si accetta mid, o lo si controlla contro il subject del JWT.

Distinguere autenticazione e autorizzazione in una frase

Sia all’esame sia in pratica, la formulazione seguente aiuta.

  • Autenticazione: chi sei
  • Autorizzazione: cosa quella persona può fare

Una verifica della firma JWT riuscita arriva solo fino a «al soggetto che questo token rappresenta ci si può fidare». Se «quel soggetto può leggere user02» va confermato a parte.

8. Domanda 2(4) — status=paid è un difetto di autorizzazione a livello di proprietà

La specifica dell’API utente definisce i seguenti come parametri di aggiornamento.

mid   Identificativo utente
name  Nome
age   Età

Tuttavia il valutatore ha aggiunto il valore seguente, che non è nella specifica.

status=paid

Lo stato di un utente del piano gratuito è poi passato a quello di un utente pagante.

Secondo la domanda, il servizio L non convalidava i parametri ricevuti; li passava tutti dritti al modulo comune P, che era costruito in modo da poter aggiornare il database direttamente.

La risposta per lo spazio c è modulo comune P.

Mass AssignmentUno status=paid fuori specifica viene aggiunto e applicato in blocco all'oggetto internoL'attaccante aggiunge status=paidAssociazione automaticaSalvato nel DBSpecifica APImid / name / ageCorpo della richiestaModulo comune PStato di fatturazione cambiato in paid

Figura 7: Mass Assignment. Una proprietà fuori specifica viene applicata in blocco all’oggetto interno.

La differenza da BOLA

Lo scambio di mid del capitolo precedente e questa aggiunta di status sembrano simili, ma la granularità protetta è diversa.

Vulnerabilità Cosa cambia l’attaccante Cosa andrebbe davvero controllato
Scambio di mid L’oggetto bersaglio Questo utente può accedere a questo record utente
Aggiunta di status Una proprietà dentro l’oggetto Questo utente può cambiare questo campo

L’OWASP API Security Top 10 2023 tratta quest’ultima come Broken Object Property Level Authorization, includendo in questa categoria ciò che prima si chiamava Mass Assignment.8

«Mettere il JSON dritto nell’entità» è pericoloso

L’implementazione vulnerabile, in modo concettuale, è così.

entity = repository.find(body.mid)
bindAllProperties(entity, body)
repository.save(entity)

Anche se la schermata ha campi di input solo per name e age, un attaccante può costruire la richiesta HTTP direttamente. L’assenza di un campo dall’interfaccia non è un confine di sicurezza.

Un’implementazione sicura rende espliciti i campi aggiornabili.

input = parseExactSchema(body, fields = ["name", "age"])

entity = repository.find(authenticatedUserId)
entity.name = input.name
entity.age  = input.age
repository.save(entity)

Qui contano due cose.

  1. Il tipo di input per l’aggiornamento dovrebbe contenere solo i campi che un utente è autorizzato a cambiare.
  2. Anziché ignorare in silenzio i campi sconosciuti fuori specifica, si rifiutano come errore se possibile.

Ignorare in silenzio i campi sconosciuti nasconde il fatto che un attacco è fallito, ma fa anche perdere errori di implementazione del client e segni di un attacco. Salvo un motivo di compatibilità, rifiutare con uno schema stretto rende più facile l’indagine.

Autorizzazione a livello di proprietàIl DTO di aggiornamento tiene solo un elenco consentito, e le proprietà sconosciute vengono rifiutateValidazione dello schemaNoPercorso dedicatoDTO di aggiornamento - elenco consentitonameageCorpo della richiestaSolo campiammessi presentiAggiornare name/age dell'entitàRestituire un erroreServizio di pagamentonotifica verificataAggiornare status=paid

Figura 8: Autorizzazione a livello di proprietà. Si restringono i campi aggiornabili con un elenco consentito, e si cambia lo stato di fatturazione solo tramite un percorso distinto.

status dovrebbe cambiare solo dal risultato del pagamento

status=paid non è parte del profilo utente. È uno stato derivato da un fatto lato server: che il pagamento è riuscito.

Aggiornamento del profilo utente
  -> si possono cambiare solo name / age

Notifica verificata dal servizio di pagamento
  -> far coincidere paymentId
  -> prevenire l'elaborazione duplicata
  -> cambiare status in paid

Anche se memorizzato nella stessa colonna del database, l’autorità di cambiarlo e il percorso usato per cambiarlo sono cose diverse. Usare un’entità interna direttamente come tipo di input di un’API esterna cancella quel confine.

9. Domanda 2(5) — le misure anti brute-force tengono il conteggio dei fallimenti come stato

Per il brute-force del codice a quattro cifre, lo spazio d nella tabella 5 chiede, in 30 caratteri o meno, l’elaborazione che ci appartiene. La soglia è 10.

La risposta modello è:

Logica che blocca l’account quando il numero di fallimenti consecutivi supera la soglia

Questo non contraddice l’assenza di stato della domanda 1. Non tenere lo stato conversazionale di una chiamata API come sessione server, e persistere il conteggio dei fallimenti necessario a una decisione di sicurezza, sono due cose diverse.

Con e senza un limite al ritmo dei tentativiSenza un limite il codice si spezza in media in 500 secondi, ma un limite al conteggio dei fallimenti rallenta di colpo l'attaccoCon un limiteLa velocità d'attacco crollaAccount bloccatoBlocco dopo 10 fallimentiRitardo graduatoSenza un limiteCirca 500 secondiL'autenticazione ha successo10 tentativi al secondo

Figura 10: Con e senza un limite al ritmo dei tentativi. Un limite al conteggio dei fallimenti può fermare in pratica il brute-force.

In pratica, non affidarsi solo al blocco permanente

Un limite di tentativi per account è necessario, ma se un attaccante conosce l’identificativo di qualcun altro, può fallire di proposito 10 volte per chiudere fuori l’utente legittimo. In pratica, quindi, si combina quanto segue.

Controllo Ruolo
Conteggio dei fallimenti per account Ferma il brute-force contro un singolo account
Attese graduate Tollerano gli errori di input di un utente legittimo e intanto rallentano l’attacco
Controlli per IP di origine, dispositivo, ASN, ecc. Reprimono attacchi che provano poche volte contro molti account
Decisioni basate sul rischio Applicano restrizioni più forti per regioni, dispositivi o velocità insolite
Notificare l’utente Fa notare all’utente un attacco o il proprio errore
Una procedura di ripristino sicura Impedisce che il canale di sblocco diventi esso stesso una via d’attacco

Inoltre, quando un codice viene reinviato, il conteggio dei fallimenti non deve essere azzerato — altrimenti un attaccante può rifornire il proprio budget di tentativi ogni volta che chiama l’API di reinvio. L’attuale NIST SP 800-63B richiede altrettanto che il conteggio dei fallimenti non venga azzerato nemmeno quando si genera un nuovo segreto di autenticazione.4

Rendere il codice di autenticazione monouso

La domanda si concentra sul tempo di scadenza, ma in pratica servono anche i seguenti.

  • Invalidare subito un codice riuscito.
  • Rifiutare il riutilizzo dello stesso codice.
  • Non lasciare il codice stesso nei log.
  • Fare in modo che la risposta non permetta di inferire, dal successo o fallimento del controllo del codice, se un utente esiste.
  • Mettere un limite di tentativi anche sull’API di invio del codice.

Finché si usa un segreto breve, la sicurezza non può essere lasciata alla sola generazione casuale.

Misure per il codice di autenticazioneOltre a cifre e scadenza, si protegge con limiti ai tentativi, rifiuto del riutilizzo, notifica e altroCodice di autenticazioneAumentare le cifreAccorciare la scadenzaLimite al ritmo dei tentativiInvalidare dopo il successoNon azzerare i fallimenti al reinvioNon lasciare il codice nei logControllo per origine

Figura 11: Misure per il codice di autenticazione. Si combinano cifre e scadenza con il controllo dei tentativi e le pratiche operative.

10. Distinguere le quattro parti della domanda 2 in una pagina

I punti della domanda 2 facili da confondere, organizzati per il valore che l’attaccante controllava.

Attacco Valore che l’attaccante ha cambiato Ciò di cui non ci si sarebbe dovuti fidare Correzione di fondo
Manomissione JWT alg dell’intestazione JWT, identificativo utente del payload L’algoritmo di verifica dichiarato dal token stesso Fissare gli algoritmi ammessi lato server
Leggere le informazioni di un altro utente mid della richiesta L’identificativo bersaglio specificato dal client Farlo coincidere con il subject del JWT, o determinare l’identificativo bersaglio dal JWT
Promuovere a utente a pagamento status fuori specifica Tutte le proprietà associate automaticamente Rendere le proprietà aggiornabili un elenco consentito
Spezzare il codice a 4 cifre Candidati di otp Tentativi di autenticazione illimitati Aggiungere limiti al ritmo dei tentativi, ritardi e decisioni di rischio

Conta non raggruppare tutto questo come «convalidare l’input».

  • alg è una politica crittografica.
  • mid è autorizzazione a livello di oggetto.
  • status è autorizzazione a livello di proprietà.
  • otp è resistenza all’indovinamento online.

Anche dentro la stessa richiesta HTTP, il motivo per cui ciascuno va protetto è diverso.

11. Domanda 3(1) — confermare l’esecuzione remota di codice senza causare danni

Dopo il lancio del servizio, viene divulgata una vulnerabilità critica V nella libreria H, una libreria open source ampiamente usata. La sequenza di eventi della domanda è la seguente.

  1. L’attaccante mette una stringa contenente un JNDI Lookup in un’intestazione HTTP e la invia.
  2. Il server bersaglio registra quel valore.
  3. La libreria vulnerabile valuta il JNDI Lookup e interroga il server LDAP dell’attaccante.
  4. La risposta LDAP restituisce l’URL del server HTTP dell’attaccante.
  5. Il server bersaglio recupera il file di classe ed esegue il comando.

Con il nome specifico del prodotto omesso, questo si legge come un attacco di tipo Log4Shell (CVE-2021-44228). Anche la descrizione di Apache presenta la vulnerabilità come una in cui, se un attaccante controlla i messaggi di log o i parametri, può eseguire codice arbitrario caricato da un server LDAP.9

Flusso di conferma per una vulnerabilità di tipo Log4ShellUsare un callback innocuo per confermare se la catena da JNDI all'esecuzione remota di codice passa davveroIniettare un payload jndi/ldapin x-api-versionJNDI LookupRisposta URL HTTPRegistrare il GETConfermare la raggiungibilitàAttaccanteServer vulnerabileElaborazione dei logServer LDAP malevoloServer HTTP malevoloindex.htmlServer di testlog di accessoVulnerabilità confermata

Figura 12: Flusso di conferma per una vulnerabilità di tipo Log4Shell. La raggiungibilità si conferma registrando un accesso HTTP anziché emettendo un comando distruttivo.

Il codice di verifica innesca solo un accesso HTTP innocuo

L’azienda G esegue un codice di verifica che non ha impatto sul sistema, per confermare se la vulnerabilità V è sfruttabile dall’esterno. L’unico comando che il codice di verifica emette è il recupero dell’index.html del server di test.

La domanda 3(1) chiede cosa va implementato sul server di test per confermare che il comando è stato eseguito.

La risposta modello è:

Un meccanismo che registra e consente di confermare gli accessi all’index.html del server di test

Se il log di accesso del server web registra un GET dal server bersaglio, ciò conferma, come minimo, che è passata la catena seguente.

Richiesta HTTP esterna
  -> elaborazione dei log
  -> JNDI Lookup
  -> risposta LDAP
  -> recupero della classe
  -> esecuzione del comando di verifica
  -> accesso HTTP al server di test

Perché «mostrare testo sullo schermo» non basta

Il bersaglio dell’attacco è il server. Non c’è garanzia che qualcosa cambi sullo schermo del browser dell’utente. Inoltre, anche dove la vulnerabilità esiste, la comunicazione in uscita a metà strada può essere bloccata da un firewall.

Registrare l’accesso lato server di test produce una prova osservabile che il server bersaglio ha davvero raggiunto il mondo esterno.

Quando si esegue questo tipo di verifica in pratica, si osservano sempre i seguenti.

  • Ottenere un’autorizzazione esplicita dal proprietario del sistema bersaglio.
  • Usare un metodo di verifica senza impatto sulla produzione, o con un impatto accettabilmente piccolo.
  • Non usare comandi distruttivi come scritture, cancellazioni o cambi di configurazione.
  • Gestire il dominio e il server di verifica in proprio.
  • Registrare ora della verifica, origine, bersaglio e callback atteso.
  • Smontare eventuali server LDAP o HTTP temporanei e le credenziali dopo la verifica.

«Confermare che l’esecuzione di codice arbitrario è possibile» e «eseguire codice arbitrario pericoloso» non sono la stessa cosa. Si tengono gli effetti collaterali al minimo necessario per l’obiettivo.

12. Domanda 3(2)(3) — il WAF ispeziona l’intestazione HTTP

Il WAF del servizio N consente di scegliere GET, POST, PUT, ANY, Header, COOKIE o Multipart come bersaglio di ispezione.

Il codice d’attacco va nel valore di un’intestazione HTTP chiamata x-api-version. Quindi gli spazi e e f nella tabella 6 sono entrambi Header.

Mappare il luogo dato nel testo direttamente sul bersaglio di ispezione del WAF

Qui si tratta meno di conoscenze generali e più di leggere il flusso dei dati nel testo della domanda.

Dove è collocata la stringa d'attacco:
  intestazione x-api-version
          |
          v
Bersaglio di ispezione del WAF:
  Header

Non è un parametro GET né un corpo POST. Anziché guardare l’elenco di funzioni del WAF e scegliere ANY perché «sembra un attacco», si risponde con il luogo in cui il testo della domanda dice che l’attaccante ha messo il valore.

Gestire lo scambio di maiuscole e minuscole

La prima proposta era, in modo concettuale, la regola seguente.

Header  \Wjndi\W  blocca
Header  \Wldap\W  blocca

Ma scambiare il caso, come in jNdI, elude un pattern che coincide solo con le minuscole.

La risposta modello per la domanda 3(3) è una delle seguenti.

\W[jJ][nN][dD][iI]\W
\W(j|J)(n|N)(d|D)(i|I)\W

Nel fascicolo della domanda la barra rovesciata può sembrare un segno yen nel glifo della locale giapponese, ma come espressione regolare è \W. \W coincide con qualsiasi carattere diverso da alfanumerici e underscore. Nella sintassi di JNDI Lookup, caratteri non-parola come ${ e : appaiono subito prima e dopo jndi, e il pattern è scritto per catturare anche quelli.

La stessa idea si può applicare per rendere anche il lato ldap insensibile al caso.

\W[lL][dD][aA][pP]\W

Non trattare questa regex come una «contromisura Log4Shell completa»

L’esame chiede un’espressione regolare che gestisce la tecnica di elusione mostrata nel testo della domanda. Gli attacchi reali possono coinvolgere spezzettamento di stringhe, Lookup alternativi, encoding, altri protocolli e altre variazioni difficili da coprire con le sole firme.

Il posizionamento pratico, quindi, è il seguente.

  1. Bloccare temporaneamente i pattern d’attacco già noti con il WAF.
  2. Investigare se la libreria colpita è davvero presente.
  3. Limitare LDAP, RMI e traffico HTTP in uscita non necessario.
  4. Aggiornare a una versione patchata.
  5. Dopo l’aggiornamento, controllare comunque i log e investigare se è avvenuta una compromissione.

Il WAF è uno strato che compra tempo fino a quando è disponibile una versione patchata.

Dove sta il WAFIl WAF è uno strato di mitigazione temporanea, la correzione di fondo è aggiornare la libreria a una versione patchataRegola WAFrilevamento/bloccoLimitare il traffico in uscitaVulnerabilità critica divulgataConfermare l'impattoMitigazione temporaneaFermare temporaneamente il pattern d'attaccoChiudere la via di abusoAggiornare alla libreria patchataRevisione a posteriori e prevenzione

Figura 13: Dove sta il WAF. Il WAF compra solo tempo fino all’arrivo di una patch; la correzione di fondo è l’aggiornamento.

13. Domanda 3(4) — perché iniziare con «rilevamento»

Per la regola WAF aggiornata, Z, uno specialista di sicurezza registrato, consiglia di impostare la modalità su «rilevamento» anziché «blocco» per un periodo fisso dopo l’ingresso in produzione.

La domanda chiede, in 25 caratteri o meno ciascuno, il vantaggio di usare la modalità rilevamento e cosa andrebbe fatto per minimizzare i danni.

La risposta modello è:

Voce Nocciolo della risposta
Vantaggio Può prevenire un blocco causato da un falso positivo
Cosa fare Esaminare se è un attacco ogni volta che si riceve un alert

La modalità rilevamento non è una modalità «non fare niente»

In modalità rilevamento, il traffico che coincide con la regola viene comunque lasciato passare, ma viene registrato e viene alzato un alert. Anche se la stringa jndi o ldap capita di comparire dentro una chiamata API legittima, il business non si ferma subito.

In cambio, il lato operativo deve fare quanto segue.

Alert ricevuto
   |
   v
Controllare la richiesta in questione
   |
   +-- Traffico legittimo -> restringere la regola, considerare un'eccezione
   |
   +-- Attacco             -> isolare il bersaglio, conservare i log, investigare l'impatto, passare al blocco

Se nessuno guarda gli alert, la modalità rilevamento non ha alcun effetto protettivo. Il rilevamento funziona solo accoppiato a un processo operativo che osserva e giudica.

Il percorso dal rilevamento al blocco

Una procedura di rollout tipica è la seguente.

  1. Far girare la modalità rilevamento sul traffico reale.
  2. Classificare gli hit come falsi positivi o veri positivi.
  3. Affinare intestazione bersaglio, percorso, API, confini di parola e così via.
  4. Confermare che l’impatto sul traffico legittimo è accettabile.
  5. Passare alla modalità blocco.
  6. Monitorare il numero di blocchi e l’impatto sul business.

Questo, però, è il principio per i tempi ordinari. Quando una vulnerabilità è critica, viene già sfruttata attivamente e non esiste un’alternativa, l’interruzione causata da una compromissione può essere giudicata superiore a quella causata da un falso positivo, e si sceglie invece di bloccare fin dall’inizio. Nello scenario d’esame, si sceglie prima la modalità rilevamento per confermare che il servizio può continuare a operare come prima.

Dalla modalità rilevamento WAF alla modalità bloccoOsservare gli alert in modalità rilevamento, affinare i falsi positivi, poi passare alla modalità bloccoTraffico realeFalso positivoAttaccoModalità rilevamentoAlert alzatoAttacco ofalso positivoAffinare la regolaPassare alla modalità bloccoMonitorare il numero di blocchi e l'impatto sul business

Figura 14: Dal rilevamento al blocco. Prima si osserva e si affina, si conferma che l’impatto è accettabile, poi si passa al blocco.

14. Il WAF è una misura temporanea; l’aggiornamento è la correzione di fondo

Nella domanda, il sito ufficiale della libreria H non aveva ancora né una correzione né un workaround temporaneo, e persino la regola WAF completa del provider cloud avrebbe richiesto fino a 72 ore. Quindi l’azienda G conferma l’impatto da sé e blocca provvisoriamente almeno i pattern già individuati.

Questa sequenza è la forma di base della risposta a un incidente.

Stadio Scopo Risposta in questa domanda
Confermare l’impatto Giudicare se la propria organizzazione è davvero a rischio Confermare lo sfruttamento dall’esterno con un callback innocuo
Mitigazione temporanea Comprare tempo fino a una correzione Regole WAF, rilevamento/blocco, restrizioni del traffico in uscita
Correzione di fondo Rimuovere la causa vulnerabile Aggiornare a una libreria patchata
Revisione a posteriori Controllare se è già stata sfruttata Investigare i log di WAF, applicazione, DNS, proxy e altri
Prevenire il ripetersi Accelerare la decisione successiva Inventario delle dipendenze, SBOM, procedura di aggiornamento, canale di contatto

«Non sappiamo se lo usiamo» è la maggiore fonte di ritardo

Nella domanda, anche quando l’azienda G chiede all’azienda F se usa la libreria H, la risposta richiede tempo perché serve un’analisi dettagliata della configurazione.

In pratica, se si inizia a cercare file JAR solo dopo che è stata divulgata una vulnerabilità critica, la risposta ritarda. Si dovrebbe avere almeno quanto segue in tempo di pace.

  • Un inventario delle dipendenze dirette e transitive.
  • I componenti e le versioni effettivamente inclusi negli artefatti.
  • Su quali servizi, container e dispositivi sono distribuiti.
  • Una procedura per aggiornare una libreria dipendente e ricostruire/ridistribuire.
  • Un canale di contatto per approvare cambiamenti di emergenza.
  • Le destinazioni ammesse per il traffico in uscita, e l’impatto di bloccarle.
  • Dove sono conservati i log e come cercarli.

Un SBOM non è l’obiettivo in sé. È un indice per rispondere, in poco tempo, a «quali sistemi in esecuzione colpisce questa vulnerabilità».

Non smettere di investigare una volta aggiornati

Si può già essere stati attaccati intorno al momento in cui la vulnerabilità è stata divulgata. Aggiornare a una versione patchata ferma lo sfruttamento futuro, ma non cancella credenziali già compromesse né una backdoor già piantata.

Per una vulnerabilità di tipo Log4Shell, si investigano almeno i seguenti angoli.

  • Richieste HTTP contenenti stringhe sospette che indicano JNDI o LDAP.
  • Comunicazione dal server applicativo verso LDAP, RMI o HTTP esterni.
  • Avvio di processi figlio insoliti.
  • Creazione di JAR, classi, script o eseguibili sospetti.
  • Accesso a credenziali cloud o variabili d’ambiente.
  • Autenticazione, cambi di permesso e trasferimenti in uscita intorno al momento dell’aggiornamento.

Conta non concludere «non siamo stati attaccati» dai soli log del WAF. Ci sono percorsi interni che non passano mai dal WAF, e log che in passato non sono stati conservati.

15. Un modo di leggere che rende più facile prendere punti all’esame

Questa domanda è meno un test di conoscenze e più un esercizio nel leggere il divario tra specifica e implementazione.

15.1 Separare «specifica» e «implementazione» nelle tabelle

Nel problema di status, un valore assente dalla specifica API passa nell’implementazione.

Specifica:
  mid / name / age

Implementazione:
  inviare tutti i parametri ricevuti a P

Una volta visibile questo divario, diventa chiaro che lo spazio c è il modulo comune P.

15.2 Tracciare una riga sotto il valore che l’attaccante ha cambiato

Il valore cambiato in ciascun attacco è il seguente.

  • alg dell’intestazione JWT
  • Identificativo utente del payload JWT
  • Parametro API mid
  • status fuori specifica
  • otp dell’API di autenticazione
  • Intestazione HTTP x-api-version

Quasi ogni domanda chiede «dove andrebbe verificato quel valore».

15.3 Riportare la risposta ai termini del testo della domanda

In pratica si possono chiamare «BOLA», «Mass Assignment» e «rate limiting». Ma ciò che la domanda chiede è un’elaborazione concreta allineata alla struttura del testo.

Un esempio scarso:

Eseguire l’autorizzazione in modo appropriato.

Un buon esempio:

Verificare se l’identificativo utente contenuto nel JWT coincide con il valore di mid.

Un esempio scarso:

Adottare misure anti brute-force.

Un buon esempio:

Bloccare l’account quando il numero di fallimenti consecutivi supera la soglia.

Conoscere il solo nome astratto non produce una risposta valutabile entro il limite di caratteri.

15.4 Per il WAF, seguire «dove è stato messo»

Il bersaglio di ispezione del WAF si decide non indovinando dal tipo di attacco, ma da dove è stata collocata la stringa d’attacco.

Messo nell'intestazione x-api-version
        ↓
Il bersaglio di ispezione è Header

La nota del commento di valutazione che il tasso di risposte corrette della domanda 3(1) era un po’ basso è anche perché molte risposte non coincidevano con il flusso d’attacco della figura 6. Già ridisegnare la sequenza d’attacco con le frecce rivela cosa andrebbe osservato.

16. Una checklist per le revisioni API nel mondo reale

Una checklist per riportare questa domanda in revisioni reali di progetto e di codice.

Verifica JWT

  • L’algoritmo di firma ammesso è fissato nella configurazione del server.
  • none e gli algoritmi inattesi vengono rifiutati.
  • Firma, iss, aud, exp e nbf vengono verificati a seconda del caso d’uso.
  • ID token, access token e refresh token non vengono confusi tra loro.
  • C’è una procedura per la rotazione delle chiavi e la revoca.
  • Informazioni che devono restare riservate non vengono messe nel payload JWT.

Autorizzazione a livello di oggetto

  • Cambiare l’identificativo dentro una richiesta non può raggiungere i dati di un altro utente.
  • L’autorizzazione è applicata su elenco, dettaglio, aggiornamento, cancellazione e download allo stesso modo.
  • L’autorizzazione è applicata in uno strato comune che raggiunge i dati, non nella schermata.
  • Per le API solo-sé, si è considerato di derivare l’identificativo bersaglio dal token.
  • Le operazioni di amministratore usano una politica distinta dall’API utente generale.

Autorizzazione a livello di proprietà

  • Il tipo di input esterno e l’entità del database sono tenuti separati.
  • I campi aggiornabili sono enumerati come elenco consentito.
  • Le proprietà fuori specifica vengono rifiutate o sottoposte ad audit.
  • Stati come permessi, fatturazione, approvazione e proprietà non si possono cambiare dall’input utente.
  • Anche le risposte escludono proprietà riservate non necessarie.

Tentativi di autenticazione

  • C’è un limite al conteggio dei fallimenti per account.
  • C’è ritardo graduato e controllo per origine.
  • Il conteggio dei fallimenti non viene azzerato quando il codice viene riemesso.
  • Il codice di autenticazione si può usare una volta sola.
  • Codici di autenticazione e password non vengono lasciati nei log.
  • La procedura di sblocco/ripristino non è essa stessa una via di autenticazione più debole.

Vulnerabilità critiche in librerie dipendenti

  • I servizi in esecuzione si possono mappare alle versioni delle dipendenze.
  • C’è una procedura per verificare l’impatto con un metodo innocuo.
  • Si possono applicare misure temporanee come regole WAF e restrizioni in uscita.
  • C’è un processo operativo in cui un responsabile rivede gli alert di rilevamento.
  • C’è un percorso di rilascio di emergenza per aggiornare a una versione patchata.
  • I log vengono investigati per un possibile sfruttamento prima dell’aggiornamento.

17. I due volti dei componenti condivisi, come si vedono attraverso questa domanda

Questa domanda presenta due componenti condivisi: la libreria di gestione JWT Q e il modulo comune P.

I componenti condivisi hanno grandi vantaggi.

  • Correggere un posto propaga la correzione a ogni API che lo usa.
  • La logica di autorizzazione e verifica non deve essere duplicata in ogni funzione.
  • I bersagli di test si possono consolidare.
  • I formati di log e di audit si possono unificare.

Dall’altro lato, anche gli errori si propagano per tutto il sistema.

  • Se la libreria Q accetta alg=none, ogni API che usa JWT diventa vulnerabile.
  • Se il modulo comune P accetta un mid o uno status arbitrari, sia GET sia PUT diventano vulnerabili.
  • Se la libreria vulnerabile H è usata nelle fondamenta, ogni percorso che registra intestazioni HTTP diventa parte della superficie d’attacco.

Ciò che va condiviso, quindi, non è il mero accesso ai dati. Bisogna condividere gli invarianti di sicurezza stessi, e verificare in modo rigoroso quel componente condiviso in isolamento.

Per esempio, si rende il contratto di P come segue.

P.getOwnUser(authenticatedSubject)
P.updateOwnProfile(authenticatedSubject, ProfileUpdate{name, age})

È più sicuro non esporre un’API di basso livello come la seguente direttamente ai chiamanti ordinari.

P.getUser(arbitraryMid)
P.updateUser(arbitraryMap)

Quest’ultima serve solo a un insieme limitato di percorsi, come l’elaborazione amministrativa. Consegnare quella libertà di basso livello a ogni API produce un progetto che dipende dal fatto che ogni singolo chiamante la usi correttamente, ogni volta.

18. Sintesi

La domanda 1 PM di primavera 2024 (Reiwa 6) è una domanda che legge a uno a uno i temi della sicurezza delle API.

Usare un JWT non significa che l’autenticazione è sicura. Lasciare che l’attaccante scelga l’algoritmo di firma permette di riscrivere l’identificativo utente.

Una firma JWT valida non significa che l’autorizzazione è corretta. Fidarsi del mid della richiesta permette a un utente legittimo di accedere alle informazioni di qualcun altro.

Potere aggiornare il proprio oggetto non significa che ogni proprietà può essere cambiata. L’associazione automatica di stato interno come status permette di riscrivere permessi o stato di fatturazione.

Avere una scadenza sul codice di autenticazione non significa che resiste al brute-force. Bisogna calcolare lo spazio dei candidati e la velocità dei tentativi, e limitare il numero di fallimenti.

Mettere una regola nel WAF non significa che la vulnerabilità è corretta. Rilevamento e blocco comprano solo tempo; si conferma l’impatto e alla fine si aggiorna la libreria.

Mappatura delle vulnerabilità alle misureMappa ciascuna vulnerabilità al suo confine di fiducia e alla sua misuraVerifica del tokenAutorizzazione a livello di oggettoAutorizzazione a livello di proprietàControllo dei tentativi di autenticazioneDa input a esecuzioneManomissione JWTFissare l'algoritmo ammessoScambio di midConfrontare col subject JWT / mid non servestatus=paidRendere il DTO di aggiornamento un elenco consentitoBrute-force del codice a 4 cifreLimite fallimenti / ritardoVulnerabilità di tipo Log4ShellAggiornamento libreria / WAF

Figura 15: Mappatura delle vulnerabilità alle misure. Si separa la correzione in base a quale confine è stato rotto.

Un solo principio attraversa l’intera domanda.

Non fare mai del successo del controllo precedente un motivo per saltare il confine di fiducia successivo.

Gli articoli precedenti di questa serie trattano la vulnerabilità XSS memorizzato della domanda 1 della sessione pomeridiana dell’autunno 2023 (Reiwa 5) e l’esfiltrazione di dati dal Wi-Fi per gli ospiti della domanda 2 della sessione pomeridiana dell’autunno 2023 (Reiwa 5). Per una visione di cosa controllare su un sito web nel suo insieme, si veda anche Usare la «Guida per rendere sicuro il proprio sito web» dell’IPA come checklist.

Sintesi finaleMostra che il successo del controllo precedente non è mai un motivo per saltare il confine di fiducia successivoAutenticazione riuscitaVerifica della firma JWTAutorizzazione a livello di oggettoAutorizzazione a livello di proprietàLimite al ritmo dei tentativiConfine da input a esecuzioneWAF / aggiornamento libreria

Figura 16: Sintesi finale. I confini di fiducia si controllano a stadi, e nessuno può essere saltato.

Riferimenti

  1. IPA, Fascicolo delle domande pomeridiane dell’esame Registered Information Security Specialist, primavera 2024 (Reiwa 6). Il testo della domanda su cui si basa questo articolo. 

  2. IPA, Risposte modello pomeridiane dell’esame Registered Information Security Specialist, primavera 2024 (Reiwa 6). La risposta modello ufficiale per ciascuna domanda. 

  3. IPA, Commento di valutazione pomeridiano dell’esame Registered Information Security Specialist, primavera 2024 (Reiwa 6). Una spiegazione dei tassi di risposte corrette e degli errori comuni. 

  4. NIST, SP 800-63B: Authentication and Authenticator Management. Fissa il numero di cifre per i segreti a breve termine, i limiti al ritmo dei tentativi, il conteggio dei fallimenti alla riemissione e il non usare l’e-mail per l’autenticazione out-of-band, tra gli altri requisiti.  2

  5. RFC Editor, RFC 7519: JSON Web Token (JWT). La specifica dei JWT, compreso l’Unsecured JWT e alg=none

  6. RFC Editor, RFC 8725: JSON Web Token Best Current Practices. Il BCP che fissa l’insieme degli algoritmi ammessi e la verifica di emittente, subject e audience, tra le altre pratiche. 

  7. OWASP, API1:2023 Broken Object Level Authorization. Spiega la necessità di controllare l’autorizzazione per ogni identificativo di oggetto che l’utente specifica. 

  8. OWASP, API3:2023 Broken Object Property Level Authorization. Spiega i difetti di autorizzazione a livello di proprietà, compreso il Mass Assignment, e le relative misure. 

  9. Apache Logging Services, Security. Spiega l’impatto di CVE-2021-44228, l’esecuzione di codice via JNDI e LDAP e le versioni corrette. 

Articoli recenti con gli stessi tag per approfondire argomenti vicini.

Queste pagine collocano l’argomento in un contesto più ampio di servizi e decisioni.

L’articolo è direttamente collegato ai servizi seguenti.

Sviluppo di siti web

Perché nelle API per i membri e nelle integrazioni con lo smartphone, la verifica JWT, l'autorizzazione a livello di oggetto e la restrizione delle proprietà aggiornabili si traducono direttamente nella sicurezza del sistema web stesso.

Domande frequenti

Domande che ricorrono nelle consulenze sull’argomento dell’articolo.

Perché un attaccante può impersonare qualcun altro anche se la verifica della firma JWT è riuscita?
In questa domanda, la libreria di gestione JWT accettava l'alg dell'intestazione JWT esattamente come lo specificava l'attaccante, trattando un JWT con alg=none come valido senza alcuna firma. Quindi anche riscrivendo l'identificativo utente nel payload la verifica passa. La risposta d'esame è verificare l'alg dell'intestazione JWT e confermare che il suo valore non è NONE. In pratica, però, rifiutare solo NONE non basta. Si fissano gli algoritmi ammessi all'uso — per esempio RS256 — nella configurazione lato server, così che l'algoritmo dichiarato dal token non venga mai usato direttamente per la scelta. Si dovrebbero anche verificare emittente, audience, scadenza, subject e così via, a seconda del caso d'uso.
Confrontare l'identificativo utente dentro il JWT con il mid della richiesta basta come misura di autorizzazione?
Ai fini della risposta di questo esame, sì. Verificare, nel modulo comune P, che l'identificativo utente contenuto nel JWT coincida con mid ferma un attacco che specifica il mid di qualcun altro. Tuttavia, per un'API che gestisce solo le informazioni del chiamante, in pratica è più sicuro non accettare affatto mid dal client e determinare invece l'identificativo utente dal subject del JWT già verificato. Usare qualcosa come GET /users/me o PUT /users/me rende più difficile che la logica di confronto manchi e basta. Un'API in cui un amministratore opera su un altro utente va separata in un endpoint proprio, con una politica di autorizzazione propria.
Perché la sola convalida ordinaria dell'input non ferma l'attacco che aggiunge status?
Perché anche se si convalida la lunghezza di name o l'intervallo di age, ciò non serve a nulla se status — un campo che non andava accettato in primo luogo — viene associato automaticamente e passato dritto all'oggetto interno. Il problema non è il formato di un valore; è l'autorizzazione a livello di proprietà, cioè se all'utente è consentito cambiare quella proprietà affatto. Si definiscono solo name e age nel tipo di input per l'aggiornamento, e si rifiutano le proprietà sconosciute. Lo stato di fatturazione va cambiato solo da eventi di cui il server si fida, come un esito di successo del servizio di pagamento.
Il codice di autenticazione a quattro cifre scade dopo 10 minuti — perché è comunque pericoloso?
Perché i candidati da 0000 a 9999 sono solo 10.000, e a 10 tentativi al secondo un attaccante ha successo in media dopo 5.000 tentativi — cioè 500 secondi. Il periodo di validità di 10 minuti è 600 secondi, quindi provare candidati non ripetuti in ordine consente di controllarne 6.000 in quella finestra. Il solo tempo di scadenza non ferma il brute-force. Lo spazio dei candidati, la velocità dei tentativi e il tetto al numero di tentativi vanno progettati insieme.
La misura d'esame è il blocco dell'account — il blocco immediato da solo basta anche in pratica?
No. Lo spazio nella domanda chiede una logica che blocca l'account quando il numero di fallimenti consecutivi supera una soglia, ma un blocco fisso e permanente da solo permette a un attaccante di bloccare di proposito l'account di qualcun altro come denial-of-service. In pratica si combinano un conteggio dei fallimenti per account con attese graduate, valutazione del rischio di origine e dispositivo, notifiche e una procedura di ripristino. Conta anche che emettere un nuovo codice non azzeri il conteggio dei fallimenti.
Qual è il senso di impostare il WAF su rilevamento anziché su blocco?
Significa che il traffico di business legittimo non viene fermato nemmeno quando una stringa normale viene giudicata per errore un attacco. Nella risposta modello, il vantaggio è che si può evitare un blocco causato da un falso positivo, e ciò che va fatto è esaminare se si tratta di un attacco ogni volta che arriva un alert. La modalità rilevamento non è un'impostazione da lasciare stare. Si usa come periodo di osservazione in cui si controllano i log, si filtrano i falsi positivi, si affina la regola e poi si passa al blocco. In un'emergenza in cui una vulnerabilità critica nota viene davvero sfruttata, può essere ragionevole pesare questo contro il rischio di disponibilità e scegliere invece di bloccare fin dall'inizio.
La libreria H in questa domanda è Log4j?
La domanda omette il nome del prodotto, ma la sequenza d'attacco — JNDI Lookup, un server LDAP, il recupero di una classe da un server HTTP, una stringa incorporata in un'intestazione HTTP e un punteggio base CVSS v3.1 alto — si legge naturalmente come un'astrazione di CVE-2021-44228, nota come Log4Shell. Questo articolo spiega quella corrispondenza, ma l'esame non chiede di nominare il prodotto specifico. Si può rispondere solo dalla procedura d'attacco data e dalla specifica del WAF.
Cosa si dovrebbe riportare nella pratica da questa domanda?
Che il successo dell'autenticazione, il fatto che il JWT non sia stato manomesso, l'essere autorizzati ad accedere all'oggetto bersaglio e l'essere autorizzati a cambiare la proprietà bersaglio sono tutti controlli distinti. Oltre a ciò, un codice di autenticazione breve ha bisogno di un limite al ritmo dei tentativi, e per una vulnerabilità critica di libreria si conducono in parallelo la conferma dell'impatto, la difesa temporanea e la correzione di fondo. I punti pratici centrali: consolidare l'autorizzazione in componenti condivisi, rendere lo schema di input un elenco consentito, fissare le condizioni di verifica del JWT lato server e tenere traccia delle librerie dipendenti così da poterle aggiornare.

Profilo dell’autore

Pagina di presentazione dell’autore dell’articolo.

Go Komura

Rappresentante di KomuraSoft LLC

Specializzato nello sviluppo di software Windows, nella consulenza tecnica e nell’analisi dei malfunzionamenti, soprattutto nei progetti con sistemi esistenti e guasti difficili da riprodurre.

Torna al blog