Nessun risultato trovato
Probabilmente molti tra coloro che operano nell’IT e nella sicurezza informatica avranno sentito parlare di OAuth, però forse non tutti sanno cosa si intende per autorizzazione delegata; ebbene, l'autorizzazione delegata è il principio su cui si fonda il sistema OAuth.
OAuth è un'azienda americana che offre servizi di Identity Access Management, che sono in pratica servizi per la gestione degli accessi.
In questo tutorial spiegheremo in cosa consistono questi servizi e che funzione hanno, partendo dal concetto di autorizzazione delegata, che semplificando molto significa poter delegare a un’applicazione la richiesta di autorizzazione d’accesso a un server in generale a un servizio, che normalmente verrebbe inoltrata da un utente.
Prima di entrare nel vivo della materia ripassiamo un paio di concetti che dovrebbe essere noti e chiari ma purtroppo talvolta vengono confusi tra di loro; comunque non è chiaro il confine tra un concetto e l'altro. Si tratta dei concetti di autenticazione e autorizzazione.
L'autenticazione è il processo attraverso cui si verifica se un utente è realmente chi dice di essere; in questa definizione si tira in ballo l’entità per indicare che non è necessariamente l'utente, ma può essere anche un'applicazione, quindi qualsiasi elemento che ha una propria identità.
Invece l'autorizzazione è il processo che concede a un'entità il permesso di accedere a una determinata risorsa, che può essere un file oppure un un servizio o qualunque altra cosa che offre dei dati o delle funzionalità ad altri.
Questo dovrebbe essere abbastanza chiaro; quello che potrebbe essere meno chiaro ai più è qual è la relazione tra autenticazione e autorizzazione, perché spesso il processo di autenticazione viene implicitamente usato per autorizzare l'accesso a una risorsa. In certi contesti questo può avere senso, perché si autentica un utente è già il fatto stesso di averlo autenticato lo rende autorizzato a effettuare operazioni, però in linea di massima i due concetti sono distinti.
Per chiarire meglio facciamo un esempio della vita reale: una persona acquista un biglietto per un volo in aereo e il suo nome viene scritto sul biglietto stesso; quando il passeggero va all’imbarco, l'operatore gli chiede un documento perché deve verificare l’identità della persona, cioò che il passeggero sia effettivamente quello scritto sul biglietto. Questa operazione è un’autenticazione. Identificato il passeggero, l’assistente di terra lo autorizza a salire a bordo; tale autorizzazione deriva non tanto dall’identificazione, quanto dal fatto che il passeggero ha un biglietto valido.
Se invece di prende il caso di un viaggiatore che compera un biglietto del bus, non c’è identificazione perché il biglietto nominativo, ma la condizione per salire a bordo che è abbia il biglietto valido: questa è un’autorizzazione, nel senso che la persona è autorizzata a salire a bordo senza bisogno che sia identificata, ma basta che possegga il biglietto, che rappresenta il mezzo autorizzativo.
Il biglietto non identifica, in questo caso, la persona, ma è solo l’elemento di autorizzazione e prescinde dall’entità che deve essere autorizzata.
Questi concetti di base possiamo applicarli a un caso concreto per introdurre il concetto di autorizzazione delegata, ossia quello rappresentato dall’immagine seguente, che schematizza il caso di un'applicazione che vuole leggere le e-mail dell'utente dal suo account Gmail. Non importa quali siano le elaborazioni richieste: il client potrebbe mostrarle a video, potrebbe fare un controllo antispam oppure trasformarli in messaggi vocali o inviarli via SMS.
Per fare delle elaborazioni c’è bisogno di accedere alle e-mail utente, ma l'applicazione potrebbe fare queste elaborazioni anche se l'utente non è collegato all'applicazione, stessa quindi in maniera completamente sganciata dalla presenza dell'utente.
In questa situazione, la prima soluzione che può venire in mente consiste nel fornire le proprie credenziali all'applicazione, in modo che questa possa impersonare l'utente; tale soluzione è efficace, ma in qualche modo presenta dei rischi di sicurezza, perché innanzitutto chi ci assicura che questa applicazione non abbia delle back door e che magari per effetto di esse utilizzerebbe le nostre credenziali in maniera non appropriata? Naturalmente potrebbe anche essere un problema non intenzionale, ma legato un bug: magari noi formiamo le credenziali ma quella applicazione per un bug espone pubblicamente e qualcun altro ne può approfittare.
Per evitare situazioni del genere bisogna seguire la regola generale che le credenziali si forniscono soltanto all'entità presso la quale è stata registrata l’identità dell’utente, quindi se ci siamo registrati su Google daremo le credenziali esclusivamente a Google, se siamo registrato su Facebook le daremo esclusivamente a Facebook e via di seguito.
Come regola generale non bisogna dare le credenziali ad altre applicazioni, sebbene fino a poco tempo fa, praticamente l'unico modo per far accedere un’applicazione al posto dell’utente fisico era questo; in vero c’erano altri altri sistemi che lo evitavano, ad esempio il protocollo SAML era in qualche misura simile ad OAuth, però era diverso nella tecnologia.
Diciamo che il problema principale è evitare che un utente dia le proprie credenziali ad un'applicazione di terze parti.
Oltre ai problemi di sicurezza esposti, ci possono essere altri ordini di problemi: uno è che quando è in possesso delle credenziali, l'applicazione potenzialmente può fare tutto quello che farebbe l’utente, quindi non solo leggere le e-mail ma anche scriverle, cancellarle e inoltrarle, quindi fornendo le credenziali a un’applicazione le stiamo dando un potere eccessivo rispetto a quello che dovrebbe fare nel caso dell’esempio.
Ma non solo: se corrotta o costruita apposta per farlo, l’applicazione potrebbe cambiare la password e quindi escludere l’utente dall'accesso.
Infine, passare le credenziali all'applicazione non sempre le permette l’accesso: per esempio se Gmail utilizza un’autenticazione a due fattori quindi, quindi oltre al nome utente e password richiede l’introduzione di un codice inviato con un SMS su cellulare, l’applicazione non può leggere il codice e quindi fallisce l’autenticazione.
In sintesi, l'approccio di fornire delle credenziali a un'applicazione terze parti, oltre a essere rischioso potrebbe in certi casi non essere applicabile; la soluzione per questo problema c'è ed è fare ricorso all'autorizzazione delegata, con la quale si delega l'applicazione ad accedere a Gmail o ad altro, per conto dell’utente, quindi l'applicazione è del tutto legittimata ad agire per conto dell’utente però non ne possiede le credenziali.
OAuth è un framework che consente di implementare l’autorizzazione delegata, ossia è un framework di autorizzazione che abilita un'applicazione di terze parti ad ottenere accesso limitato a un servizio http.
Dunque OAuth è un framework, quindi non è una libreria né un software, ma è un’architettura logica in base alla quale possiamo costruire una libreria o un software. Si occupa esclusivamente di autorizzazione, non di autenticare gli utenti ma di autorizzare applicazioni a fare determinate operazioni per conto dell'utente, quindi a dare loro un accesso limitato a ciò che l’utente desidera.
Il fatto che l'accesso sia a un servizio http identifica l’ambito di lavoro: OAuth lavora nell'ambito delle tecnologie web, quindi non lo possiamo utilizzare in ambiti differenti da quelli che non sono basati su http. Ciò non vuol dire che dobbiamo necessariamente utilizzarlo per applicazioni web, ma possiamo utilizzarlo anche per applicazioni desktop, mobile ecc. purché siano basate sul protocollo http.
Vediamo ora quali sono gli attori coinvolti in un’interazione di questo framework: le specifiche di OAuth sono abbastanza precise nel definire i nomi degli attori coinvolti; ci aiutiamo con l’immagine seguente, nella quale abbiamo il resource owner, che praticamente è il proprietario della risorsa e normalmente coincide con l'utente. Poi abbiamo il Resource Server, che sarebbe il server che gestisce le risorse dell'utente; nel nostro caso sarebbe il server di Gmail, ossia quello che che custodisce la risorsa.
Poi c’è il Client, che rappresenta l'applicazione che ha bisogno di accedere alla risorsa; non fatevi ingannare dal nome client, perché è tale dal punto di vista di OAuth ma non vuol dire che l'applicazione debba essere un client, cioè non deve essere necessariamente una single page application perché può essere anche una Web Application eseguita sul server.
Infine abbiamo all'accesso alla risorsa; nel caso che stiamo analizzando sarebbe il server degli account di Google; infatti quando ci si autentica per accedere a Gmail si viene rediretti su account.google.com e quello è il server che gestisce le identità degli utenti. Nello schema in figura, è l’Authorization Server.
Ora che abbiamo definito questi luoghi vediamo cosa succede in una tipica interazione, cioè nel flusso tipico di OAuth 2; ricordiamo che l’intento è quello di consentire a un’applicazione di accedere alle e-mail dell'utente. Con riferimento all’immagine seguente, che propone il tipico flusso di autorizzazione delegata basata sul prodotto OAuth 2, il primo passo (quello indicato dal numero 1) è compiuto dall'utente che accede all'applicazione e accede a una funzionalità che presuppone l'accesso alle sue e-mail su Gmail; l'applicazione si rende conto di non avere i permessi per leggere le e-mail e chiede all'utente di procurare un’autorizzazione per fare ciò.
L'utente si rivolge al server di autorizzazione di Google e gli comunica che l'applicazione ha bisogno di leggere le e-mail su Gmail (2); il Server di autorizzazione fornisce all'utente un codice chiamato l’Authorization code, indicandogli di impostare l’applicazione in modo che quando lo contatta deve presentare tale codice (2).
Al punto 3 l’utente fornisce l’Authorization code all'applicazione, la quale con tale Authorization code contatta direttamente l’ l’Authorization server di Google e questo in cambio dell' l’Authorization code fornisce un Access token, il quale è un'informazione che rappresenta proprio l'autorizzazione all'accesso a Gmail. Tale informazione verrà inserita nelle chiamate http verso Gmail per accedere finalmente alle e-mail. Da quel punto in poi verrà utilizzato sempre sempre tale token e in realtà l'utente può anche sparire, dato che l'applicazione ha il token, che sarebbe il biglietto d’accesso che gli consentirà di accedere alle e-mail.
Nel nostro esempio il biglietto ha delle limitazioni, cioè l'accesso e sarà in sola lettura.
Vediamo ora di scendere nel dettagio del flusso; la cosa importante da notare è che tutte queste comunicazioni avvengono tramite il protocollo http e in particolare https, requisito fondamentale per una comunicazione su canali scuri.
Quando l'utente si presenta al server di autorizzazione di Google, in realtà OAuth non dice in base a che criterio l’ Authorization server deve rilasciare il codice di autorizzazione all'utente, però normalmente il codice autorizzazione autentica l'utente, giusto per essere sicuri che si tratti effettivamente di un utente Google, pertanto all'utente viene mostrata la schermata di login: molto spesso con Google questo non avviene, perché si è già loggati e si vede direttamente l’input di autorizzare o meno; se l'utente ha già una sessione di autenticazione valida naturalmente non verrà richiesta di nuovo e questo è comodo perché consente di evitare una schermata in più (in questo caso l’utente non si rende nemmeno conto di essere passato da Google).
L'altro aspetto da tenere presente è che almeno la prima volta che si autorizza un'applicazione, viene visualizzato il cosiddetto “consent screen” ossia una schermata che avvisa l'utente che l'applicazione ha chiesto di effettuare certe operazioni; nel caso dell’esempio, l’applicazione ha chiesto l'accesso in lettura alle e-mail e se l'utente vede che c'è qualcosa di strano nei permessi richiesti dall' applicazione, può non concedere l’autorizzazione. Pertanto è importante prestare attenzione ai consent screen visualizzati quando si concede l'autorizzazione a un'applicazione di terze parti.
Nel passaggio 4 del nostro diagramma abbiamo visto che l’Authorization code viene scambiato con un Access token e che quest’ultimo rappresenta proprio l'autorizzazione ad accedere alla casella utente di Gmail per conto dell'utente stesso; l'access token non è strettamente vincolato all'applicazione, pertanto chiunque viene in possesso di questo token può accedere a Gmail per conto dell’utente.
A prima vista questo flusso può sembrare un po' macchinoso, perché ad esempio ci si potrebbe chiedere perché si deve passare dall’Authorization code e non si possa dare l’Access token direttamente all'utente affinché l'utente stesso lo inserisca nell'applicazione; ebbene, il tutto è fatto per evitare che l’Access token vada a finire in mani sbagliate, quindi per limitare il più possibile buchi in questo giro che l’Access token dovrebbe fare tra utente e applicazione in sede di autorizzazione. Quindi la complessità di questo flusso è dettata proprio da motivi di sicurezza.
Per la stessa ragione esistono altri flussi oltre a questo tipico, appena illustrato, che si chiama Authorization flow o anche Authorization code grant e che viene utilizzato quando l'applicazione utilizza un cosiddetto Confidential client.
A tale riguardo va precisato che OAuth vede due tipi di client: il Confidential client e il Public client. Il primo è in grado di custodire dei dati sensibili: per esempio un'applicazione web eseguita sul server è un Confidential client perché in grado di mantenere delle informazioni abbastanza protette; il Public client è, ad esempio, una single page application e chiunque può ispezionare il codice di tale applicazione e trattandosi di un'applicazione pubblica è più rischiosa. In quest’ultimo caso l'Authorization flow non è applicabile.
Si è appena accennato all’esistenza di vari flussi di autorizzazione, ossia grant type di OAuth2; ebbene si tratta di:
Il primo è già stato descritto ed è il flusso più indicato per applicazioni web che vengano eseguite su server; il secondo utilizza PKCE, il quale rappresenta un'estensione che garantisce che chi ha chiesto l’Authorization code sarà la stessa entità che lo riscatterà con unAccess token. Questo flusso è adeguato per single page web application, per applicazioni desktop o mobile e prevede l’utilizzo di dati con degli hash, ossia vengono utilizzati degli hash con coppie di chiavi pubbliche e private per rendere meno visibile il dato; questo avviene non tanto nella codifica dei token ma nelle stringhe, che vengono codificate e decodificati in modo che dimostrino che l’entità sia il possessore della chiave.
Poi abbiamo l’Implicit Flow, che è stato uno dei primi flussi: è un po' più semplice rispetto all’Authorization code però attualmente non è raccomandato perché soprattutto con l'evoluzione delle tecnologie client-side (l'evoluzione dei browser) è diventato meno sicuro; è un flusso che si può utilizzare con le single page application ma allo stato attuale è consigliabile utilizzare l’Authorization code + PCKE.
Chiudiamo con Client Credentials, che in realtà è un flusso che non implementa un'autorizzazione delegata vera e propria ma in realtà si basa su delle credenziali assegnate all'applicazione stessa; quindi questo flusso viene utilizzato in genere in contesti come microservice, ossia quando l'applicazione non sta agendo per conto di un utente ma sta reagendo per poco conto proprio.
A questo punto possiamo fare qualche esempio concreto di integrazione tra sistemi e tra applicazioni, che avvicini alla realtà il flusso un po' astratto descritto e schematizzato sinora. Vediamo un esempio della vita reale, che ad esempio è quello che dà la possibilità di pubblicare un post su LinkedIn e contemporaneamente su Twitter. Naturalmente prima di poter effettuare questo, in LinkedIn bisogna abilitare l'integrazione con Twitter, quindi farlo nella sezione apposita proposta nell’immagine seguente, dove Twitter chiede all’utente loggato se vuole che LinkedIn possa accedere al suo account e fare determinate cose. L’autorizzazione si concede con il menu a tendina cerchiato in rosso.
L’immagine propone, a destra, la schermata di consent screen di Twitter, che avvisa di quello che LinkedIn potrebbe fare se lo si autorizza, quindi se l’utente dà l’OK, l'Authorization server di Twitter genera un Authorization code e lo dà all'utente, l'utente lo fornisce a LinkedIn e LinkedIn si procura l'Access token che da quel momento in poi gli permetterà di accedere all’account Twitter e quindi potrà creare dei tweet a nome dell’utente. Con questo flusso, quando l’utente sarà su LinkedIn e dovrà pubblicare un post, potrà scegliere se dovrà essere pubblicato anche su Twitter; LinkedIn lo farà utilizzando l’Access token che gli è stato fornito come entità autorizzata ed allo scopo farà login su Twitter direttamente come entità autorizzata. In quel caso LinkedIn, dopo aver pubblicato il post sulla propria piattaforma, farà una chiamata http a Twitter passando il token.
Un altro esempio di autorizzazione delegata è l’integrazione tra GitHub e Netlify, la quale è una piattaforma di pubblicazione di applicazioni, quindi di hosting; in essa è possibile fare in modo che GitHub utilizzi un Access token per pubblicare una nostra applicazione “pushata” in Github. Quindi quando si esegue il push di un’applicazione su un repository GitHub, automaticamente GitHub utilizza Access token che gli è stato fornito in fase di registrazione e utilizza questo per contattare il server di Netlify e fornirgli il codice dell’applicazione in modo che possa essere pubblicata.
Stabilito che OAuth 2 è un framework di autorizzazione e non si occupa di autenticazione, va precisato che più volte è stato associato al Social login, quindi alla possibilità di accedere a determinati siti web autenticandosi con l’account Facebook, LinkedIn o Twitter.
Ma se OAuth 2 è un framework di autorizzazione, come si concilia con l'autenticazione effettuata tramite Social media (Social network)? In realtà per questo scopo i Social network utilizzano Open ID Connect, che è un livello di autenticazione basato su OAuth 2; in pratica Open ID Connect sfrutta OAuth 2 come sistema alla base del proprio meccanismo di interazione e consente la verifica dell'identità di utente e il recupero dei dati del profilo utente
Con riferimento allo schema dell’immagine seguente, possiamo descrivere il flusso base di Open ID Connect, che è molto più sintetico rispetto al flusso di OAuth 2, descritto qualche immagine indietro: i ruoli sono gli stessi di prima, però in questo caso manca il Resource server (non c’è la risorsa) quindi l'utente accede all'applicazione, questa applicazione chiede all'utente di autenticarsi su Google, per esempio; quindi l'utente va su Google, si autentica e ottiene un nuovo “artefatto” perché mentre nel caso di OAuth 2 otteneva un Authorization code, in questo caso ottiene un ID token che rappresenta l'identità dell'utente.
L’utente presenta l’ID token all'applicazione, la quale sa che l'utente è stato verificato, quindi è stato autenticato e da questo momento in poi l'applicazione restituisce una risposta con un cookie; da quel momento in poi il cookie stabilisce la sessione di autenticazione tra l'applicazione e l’utente
Guardando l’esempio dal punto di vista di OAuth 2, in questo caso la risorsa è l'utente stesso, cioè sono le informazioni dell'utente, solo che le informazioni utente non autorizzano l’utente stesso a fare determinate operazioni; semplicemente testimoniano il fatto che l'utente è stato autenticato. Nell’ID token possono essere inserite informazioni supplementari quali nome, cognome, indirizzo e-mail e fisico, un’eventuale foto dell'utente e così via, quindi informazioni per identificare l'utente. Quindi se un hacker viene in possesso dell’ID token, in realtà l'unica cosa che può fare è avere informazioni su utente e non può usarlo per fare nessun accesso fraudolento.
Qui nel nostro esempio l'utente sarà sempre utente di Google perché l'unico link tra l’applicazione e l'utente di Google è l'ID dell'utente, quindi se l’applicazione necessita di informazioni supplementari, ad esempio riguardo all’utente, che Google non fornisce, le può memorizzare in un database proprio mettendo l'ID utente collegato a Google, però l’utente non è dell’applicazione.
La cosa fa comodo anche all'utente perché non ha bisogno di ricordarsi password diverse da quella di Google; tra l'altro se è già autenticato su Google può accedere all’applicazione senza bisogno di fare una nuova autenticazione (viene implementato il single sign-on in maniera automatica)
La cosa interessante di Open ID Connect è che le sue specifiche sono integrate con con le specifiche di OAuth 2, nel senso che Open ID Connect è stato definito in maniera tale che possa essere integrato con OAuth 2. Quindi tornando al flusso minimo proposto dall’immagine precedente, se l’applicazione oltre ad autenticare ha bisogno di accedere anche a Gmail, per esempio, è possibile integrare Open ID Connect in OAuth e quindi si ritorna al flusso completo di OAuth 2 precedentemente descritto, con l'aggiunta dell’ID token.
Come proposto nello schema dell’immagine seguente, l’icona a pentagono arancione con l'omino bianco rappresenta l’ID token.
In pratica in questo flusso si sta implementando Open ID Connect e OAuth 2 viene scambiato l’Authorization code sia con Access token che con un ID token, però in questo caso il flusso è quello di OAuth 2 e non più quello semplice che abbiamo visto prima, perché ora caso abbiamo di nuovo in ballo l’Access token che è un elemento molto sensibile e da proteggere il più possibile.
Approfondiamo un momento alcuni concetti esposti prima, a partire dall’ID token, che attesta l’avvenuta autenticazione dell'utente e può contenere informazioni sul profilo utente; la struttura della ID token, ossia il formato, è definita dallo standard JWT (Json Web Token) quindi è facilmente accessibile e un'applicazione può analizzarlo facilmente utilizzando una delle varie librerie disponibili (in realtà qualsiasi linguaggio supporta librerie del genere).
È utile segnalare uno strumento interattivo per poter decodificare un JWT in generale: si chiama JWT.io.
Le informazioni sull’utente sono facilmente leggibili, però è l’ID token è firmato, pertanto non è semplice manometterlo perché in esso c'è la firma del server di autorizzazione, quindi le informazioni che si trovano all'interno del JSON Web Token anche se possono essere lette non possono essere modificate.
A questo punto uno sviluppatore potrebbe chiedersi: “ma devo sapere tutti questi dettagli per implementare OAuth 2 oppure Open ID connect nella mia applicazione?”. Naturalmente no: i dettagli devono essere conosciuti da chi implementa una libreria o una ID key per consentire l'accesso OAuth 2 oppure Open ID connect, mentre l'utente a più alto livello, che deve semplicemente integrare due applicazioni, non ha bisogno di conoscere tutti i dettagli di implementazione di OAuth 2 perché può aiutarsi utilizzando una delle diverse librerie che esistono sul mercato (l’immagine seguente ne propone tre).
Invece è opportuno conoscere i flussi e cosa c'è sotto; un po’ come uno sviluppatore web, che deve avere un minimo di conoscenza del protocollo http anche se raramente andrà a creare un messaggio http a basso livello, deve sapere cos'è un messaggio http e quale ne è la struttura generale, cosa sono di header ecc. perché lo aiuta a comprendere come utilizzare una libreria.
Peraltro in questo ambito la conoscenza di base aiuta a non commettere degli errori di livello di sicurezza, per esempio a non utilizzare un flusso Implicit Flow in una single page application perché ci possono essere dei rischi.
Per fare un altro esempio, se si utilizza un ID token al posto dell’Access token non è la cosa migliore perché a seconda del server cui si sta accendendo, quest’ultimo potrebbe anche rifiutare perché non lo riconosce come un token adeguato per effettuare l'operazione su una risorsa.
Non esiste la libreria più indicata, ma in genere è opportuno utilizzare la libreria fornita dall’Identity provider; ad esempio se vogliamo accedere a Gmail, probabilmente la libreria che conviene usare sarà parte dell’SDK di Google, cioè il framework è sempre lo stesso ma mette a disposizione funzionalità specifiche che semplificano certi passaggi.