Quando si parla di sicurezza delle API, autenticazione e autorizzazione vengono spesso considerate la stessa cosa. In realtà sono due controlli distinti, entrambi fondamentali per proteggere dati, account e funzionalità di un'applicazione.
Un'API può verificare correttamente l'identità di un utente e consentirgli comunque di accedere a informazioni che non dovrebbe vedere. Allo stesso modo, può applicare regole di autorizzazione corrette ma accettare richieste senza verificare adeguatamente chi le sta effettuando.
Capire la differenza tra autenticazione e autorizzazione nelle API è essenziale per individuare errori che possono avere conseguenze concrete sulla sicurezza di un servizio.
Cos'è l'autenticazione?
L'autenticazione è il processo attraverso il quale un sistema verifica l'identità dichiarata da un soggetto.
In un'applicazione web, questo soggetto può essere una persona, un'applicazione o un servizio automatico.
Alcuni meccanismi comuni sono:
- username e password;
- autenticazione multifattore (MFA);
- sessioni gestite tramite cookie;
- API key;
- token di accesso;
- protocolli e sistemi basati su OAuth 2.0 o OpenID Connect, a seconda del caso d'uso.
Immaginiamo che un utente effettui il login in un portale clienti. Il sistema verifica le sue credenziali e, se sono valide, crea una sessione o rilascia un token utilizzabile nelle richieste successive.
Da quel momento, l'applicazione può riconoscere l'utente nelle operazioni successive, entro i limiti e le condizioni previsti dal sistema.
Ma c'è un dettaglio fondamentale: sapere chi sta effettuando una richiesta non significa che quella persona possa accedere a qualsiasi risorsa.
Cos'è l'autorizzazione?
L'autorizzazione stabilisce quali operazioni un'identità può eseguire e su quali risorse.
Dopo aver riconosciuto un utente, l'applicazione deve verificare se possiede i permessi necessari per compiere l'azione richiesta.
Per esempio, un utente potrebbe essere autorizzato a:
- visualizzare il proprio profilo;
- modificare i propri dati;
- consultare gli ordini associati al proprio account;
- scaricare determinati documenti.
Lo stesso utente potrebbe non avere il permesso di modificare il profilo di un altro cliente, accedere agli ordini altrui o gestire le impostazioni amministrative del servizio.
L'autorizzazione può dipendere dal ruolo, dalle proprietà della risorsa, dal rapporto tra utente e oggetto oppure dal contesto della richiesta.
Un esempio concreto
Consideriamo un'API per un negozio online, con un endpoint che restituisce i dettagli di un ordine:
GET /api/orders/4821
Authorization: Bearer <access_token>
Il token permette al server di identificare l'utente che effettua la richiesta.
Supponiamo che l'ordine 4821 appartenga al cliente A. Se il cliente B invia la stessa richiesta con il proprio token valido, il server non dovrebbe restituire i dettagli dell'ordine di A.
L'API deve verificare entrambe le condizioni:
- Il token è valido e identifica correttamente il cliente B?
- Il cliente B ha il permesso di consultare l'ordine
4821?
Se il sistema verifica solo la prima condizione, potrebbe esporre informazioni personali, indirizzi di spedizione, dettagli degli acquisti o altri dati riservati.
In questo caso l'autenticazione funziona, ma l'autorizzazione è difettosa.
Perché un token valido non garantisce l'accesso corretto
Un errore comune consiste nel considerare un token valido come un'autorizzazione generale.
Un token di accesso consente al sistema di riconoscere un'identità e, in base al protocollo e alle informazioni contenute, può contribuire a determinare le autorizzazioni. Non significa però che ogni richiesta effettuata con quel token debba essere accettata.
L'applicazione deve controllare il contesto di ogni operazione.
In particolare, dovrebbe verificare:
- quali risorse può consultare l'identità;
- quali azioni può eseguire;
- se il ruolo o gli attributi dell'utente sono sufficienti;
- se il token è destinato a quel servizio e possiede gli scope necessari;
- se l'utente mantiene i permessi richiesti al momento dell'operazione.
Anche un JWT firmato correttamente non sostituisce questi controlli. La firma può permettere di verificarne l'integrità e l'origine, ma non dimostra automaticamente che l'utente abbia diritto a leggere uno specifico ordine o modificare una determinata risorsa.
I principali modelli di autorizzazione
Non tutte le applicazioni gestiscono i permessi allo stesso modo. Esistono diversi modelli, che possono anche essere combinati.
Role-Based Access Control (RBAC)
Nel modello RBAC, i permessi vengono associati a ruoli.
Per esempio:
- Utente: consulta i propri dati.
- Operatore: gestisce determinate richieste.
- Amministratore: accede alle funzioni di amministrazione.
È un modello intuitivo, ma non sempre sufficiente. Due utenti con lo stesso ruolo potrebbero avere accesso a risorse differenti.
Attribute-Based Access Control (ABAC)
ABAC valuta attributi dell'utente, della risorsa e del contesto.
Una regola potrebbe consentire l'accesso a un documento soltanto se l'utente appartiene al reparto corretto e il documento rientra nel suo ambito operativo.
Questo modello offre maggiore flessibilità, ma richiede regole progettate e verificate con attenzione.
Autorizzazione basata sulla proprietà della risorsa
In molte API, la regola principale è che un utente possa accedere esclusivamente agli oggetti che gli appartengono o che è autorizzato a gestire.
È il caso di ordini, fatture, ticket di assistenza, documenti privati e progetti.
Questo controllo deve essere applicato sul server a ogni operazione pertinente, non soltanto nell'interfaccia grafica.
Gli errori più frequenti nella sicurezza delle API
1. Controllare i permessi soltanto nel frontend
Nascondere un pulsante o una pagina agli utenti non autorizzati migliora l'esperienza d'uso, ma non protegge direttamente l'API.
Un client può inviare richieste senza utilizzare l'interfaccia prevista. Per questo motivo, i controlli di autorizzazione devono essere applicati anche sul server.
2. Fidarsi dell'identificativo inviato dal client
Se una richiesta contiene un parametro come user_id, account_id o order_id, il server non dovrebbe presumere che l'utente abbia diritto ad accedere alla risorsa identificata.
Il valore deve essere verificato rispetto all'identità autenticata e alle regole applicabili.
3. Applicare permessi generici a tutte le operazioni
Un utente potrebbe avere il diritto di leggere una risorsa senza poterla modificare o eliminare.
È importante distinguere le operazioni di lettura, modifica, cancellazione e amministrazione, applicando i permessi necessari a ciascuna di esse.
4. Non verificare gli scope dei token
Nelle API che utilizzano OAuth 2.0, gli scope possono limitare le operazioni consentite a un token.
Un token valido ma privo dello scope richiesto non dovrebbe essere sufficiente per completare l'operazione.
Gli scope, comunque, non sostituiscono i controlli sulle singole risorse: possono essere necessari entrambi.
5. Dimenticare le operazioni meno comuni
Gli endpoint di esportazione, download, duplicazione, modifica massiva o amministrazione possono avere controlli meno accurati rispetto alle funzionalità principali.
Anche questi percorsi devono essere inclusi nei test.
Come verificare correttamente autenticazione e autorizzazione
Una verifica efficace deve considerare diversi scenari, non soltanto il caso di un utente legittimo.
In un ambiente autorizzato, è utile provare almeno queste situazioni:
- [ ] Richiesta senza credenziali: il sistema deve applicare la politica prevista per l'endpoint.
- [ ] Token non valido o scaduto: la richiesta deve essere gestita correttamente.
- [ ] Token valido ma senza i permessi necessari: l'operazione deve essere negata.
- [ ] Utente che tenta di accedere a una risorsa di un altro account: il sistema deve verificare la proprietà o l'autorizzazione.
- [ ] Utente con ruolo limitato che tenta un'operazione amministrativa: l'operazione deve essere rifiutata.
- [ ] Token con scope insufficiente: l'accesso deve rispettare le restrizioni previste.
- [ ] Operazioni di modifica o cancellazione: i controlli devono essere applicati anche quando non si tratta di semplici richieste di lettura.
Le risposte HTTP devono essere coerenti con il comportamento dell'API. In generale, 401 Unauthorized indica che l'autenticazione manca o non è valida, mentre 403 Forbidden indica che la richiesta è stata compresa ma non è consentita. In alcune situazioni un servizio può utilizzare 404 Not Found per non rivelare l'esistenza di una risorsa: la scelta deve essere coerente con il modello di sicurezza e con il contratto dell'API.
È importante non fermarsi ai codici di risposta: occorre verificare che non vengano restituiti dati riservati nel corpo della risposta, negli header o in altri canali.
Una checklist per sviluppatori e responsabili IT
Per migliorare la sicurezza di un'API:
- Definisci chiaramente le identità e i ruoli previsti.
- Elenca le risorse e le operazioni disponibili.
- Stabilisci chi può leggere, creare, modificare ed eliminare ciascun tipo di risorsa.
- Applica i controlli sul server, senza fidarti delle limitazioni del frontend.
- Verifica proprietà, ruoli, scope e contesto per ogni operazione pertinente.
- Automatizza i test negativi, cioè quelli che verificano che un accesso non autorizzato venga rifiutato.
- Registra gli eventi di sicurezza utili, evitando di inserire nei log password, token o dati sensibili non necessari.
- Ripeti le verifiche quando cambiano endpoint, ruoli o logiche applicative.
Conclusione
Autenticazione e autorizzazione risolvono due problemi differenti: la prima permette di verificare chi sta effettuando una richiesta, la seconda determina che cosa quella persona o quel servizio può fare.
Confondere questi controlli può portare a esposizioni di dati anche in applicazioni che utilizzano token, HTTPS e sistemi di login moderni.
Con MiHakero puoi esaminare domini, applicazioni web, API e servizi pubblici alla ricerca di esposizioni e problemi rilevabili dall'esterno. Per una valutazione completa della sicurezza delle API, queste verifiche vanno affiancate a test autorizzati delle logiche di autenticazione e dei permessi applicativi.