Opzioni di autenticazione per sistemi distribuiti

8

Sono in procinto di progettare 3 componenti che funzioneranno in symphony l'uno con l'altro:

  • Un servizio web RESTful che richiede BasicAuth su HTTPS su tutte le chiamate e che è ciò che effettivamente fa tutto il lavoro pesante per il mio sistema (funziona)
  • Un'interfaccia utente web che traduce le azioni dell'utente finale in chiamate API al suddetto servizio Web; quindi l'interfaccia utente è "supportata da" il WS
  • Uno strumento CLI (command-line interface) che gli sviluppatori possono installare ed eseguire localmente, che traduce anche i comandi nelle chiamate API al WS (da qui anche "backed by" il WS)

Uno dei primi ostacoli che sto tentando di superare riguarda l'autenticazione e l'autorizzazione.

Supponiamo che WS utilizzi un servizio di directory LDAP / (come AD o forse Apache DS) come dominio di autenticazione. Significa che quando una chiamata API arriva sul filo (ad esempio, un HTTPS GET per qualche risorsa), le credenziali BasicAuth vengono estratte dalla richiesta e inoltrate al servizio LDAP per determinare se si tratta di un utente valido o non. Se sono autenticati, diciamo che viene utilizzato un dominio di autorizzazione separato, forse un database, per determinare se l'utente identificato può fare ciò che sta tentando nella richiesta HTTPS. Fin qui, tutto bene.

Nel caso dello strumento CLI, l'utente dovrà autenticarsi prima di eseguire qualsiasi comando, e quindi questo modello funziona perfettamente, poiché un singolo utente potrà sempre utilizzare la stessa istanza CLI in un dato momento.

Il problema si presenta quando proviamo ad integrare l'app web (UI) con il WS, perché molte persone potrebbero essere collegate all'app allo stesso tempo, tutte con autorizzazioni diverse che stabiliscono quali chiamate API di base sono autorizzate a fare .

Per quanto vedo io, sembra come se avessi solo 4 opzioni qui:

  • Credenziali memorizzate nella cache : dopo aver effettuato l'accesso all'app, le credenziali sono in qualche modo da qualche parte memorizzate nella cache (in modo che l'app possa accedervi) e l'app non impone alcun tipo della stessa politica di autorizzazione. Quando gli utenti tentano di fare cose che generano chiamate API sotto il cofano, le loro credenziali vengono ricercate dalla cache e inoltrate con le chiamate API. Se il WS determina che non sono autorizzati, restituisce un errore.
  • Account a livello di servizio : l'app e il WS utilizzano entrambi gli stessi reami di autenticazione / autorizzazione, ma l'interfaccia utente web ora impone l'autorizzazione su ciò che gli utenti possono effettivamente vedere e fare all'interno dell'app. Se gli è consentito fare qualcosa che genera una chiamata API sottostante, l'app invia le credenziali dell'account di servizio (ad esempio myapp-admin-user ) con ciascuna chiamata API per conto dell'utente.
  • OAuthv2 : non ho idea di cosa sia OAuth o se sia applicabile per questo scenario, ma ritengo che potrebbe essere una soluzione qui in qualche modo.
  • Server token : utilizza un server token come CAS o forse Kerberos per garantire gli utenti, in modo analogo a come si comporta l'opzione Account a livello di servizio. Qui, quando un utente accede correttamente all'app, il server dei token invia l'app a un UUID di sessione e registra anche quell'UUID con il WS. Ogni volta che l'app genera una chiamata API, applica l'UUID alla richiesta, che viene quindi convalidata sul lato WS.

L'opzione " Credenziali memorizzate nella cache " solo si sente come un'aberrazione di tutto ciò che è buono e sano in sicurezza-terra. È semplicemente sbagliato memorizzare le credenziali ovunque.

L'opzione " Token Server " sembra valida per un'impostazione di tipo SSO, ma non in questo caso particolare e si sente imbarazzante per me. Penso anche che non ci sia un buon modo per usare il concetto di UUID di sessione e BasicAuth / HTTPS allo stesso tempo.

Quindi questo lascia OAuthv2, di cui non so nulla, e " Account a livello di servizio (SLA) * " come le uniche opzioni rimanenti. L'opzione SLA sembra OK, ma presenta alcuni inconvenienti seguenti:

  • Richiede che l'account di servizio abbia fondamentalmente "privilegi di god" sul WS. In altre parole, una volta che l'app ritiene che un utente possa fare clic su un pulsante o fare qualcosa nell'interfaccia utente, ciò si traduce in una chiamata API incondizionata dall'account del servizio utilizzato dall'interfaccia utente. Mi fa male, mkay?
  • Mi viene in mente che il mantenimento di due serie di permessi (il set di autorizzazioni per ogni utente dell'app e il set di autorizzazioni per l'account di servizio utilizzato dall'app contro il WS) può comportare il superamento della sincronizzazione delle autorizzazioni l'uno con l'altro in qualche modo

Quindi sembra che io non abbia davvero delle buone opzioni qui. Sicuramente non posso essere il primo dev per imbattermi in questo, ma chiedere a Google Gods non mi ha aiutato molto qui. Qualche idea?

    
posta smeeb 22.07.2015 - 21:39
fonte

2 risposte

5

Esistono molti motivi per non utilizzare lo schema di autenticazione di base per proteggere i servizi API Web.

Per utilizzare il servizio, il client deve mantenere la password da qualche parte in chiaro per inviarlo insieme a ciascuna richiesta.

La verifica di una password dovrebbe essere molto lenta (per contrastare gli attacchi di forza bruta), il che ostacolerebbe la scalabilità del tuo servizio. D'altra parte, la convalida dei token di sicurezza può essere rapida (verifica della firma digitale).

OAuth2 offre soluzioni per ciascuno dei tuoi casi d'uso. La tua applicazione web può utilizzare la concessione del codice , che gli fornisce un token di accesso che può utilizzare per parlare alla tua API.

La tua applicazione web reindirizzerà il browser dell'utente al server delle autorizzazioni. Richiederà all'utente le credenziali (o smart card o codice di autenticazione a due fattori) e restituirà un codice al browser, che il client (la tua applicazione web) può utilizzare per ottenere un token di accesso dal server di autorizzazione.

La tua applicazione recupererà anche un token di aggiornamento con il quale potrà ottenere un nuovo token di accesso se il token corrente scade.

La tua applicazione CLI può utilizzare la concessione delle credenziali del proprietario delle risorse . Richiederai all'utente le credenziali e le invierai al server di autorizzazione per acquisire un token di accesso e aggiornamento. Una volta che l'applicazione CLI ha il token, è possibile eliminare la password dell'utente in memoria.

Entrambi i client (app web e client da riga di comando) devono essere registrati in anticipo con il server di autorizzazione.

Il tuo server di autorizzazione potrebbe parlare con un servizio di directory LDAP / (provider di identità o IdP) per eseguire l'autentica autenticazione.

Il tuo servizio API web deve solo verificare il token JWT in entrata e stabilire cosa l'utente è autorizzato a fare (autorizzazione).

Se sei vittima di un attacco man-in-the-middle e perdi il token di accesso, l'hacker ha solo un tempo limitato (durata del token) per usarlo. Una password è in genere valida per molto più tempo. I token di aggiornamento possono essere revocati in caso di perdita.

    
risposta data 28.07.2015 - 03:27
fonte
2

Sto lavorando ad un sistema un po 'simile in questo momento, in realtà; Mentirei se dicessi che conoscevo il modo "giusto" per fare questo lavoro da quando sto ancora sperimentando, ma forse andare oltre quello che ho trovato funziona potrebbe essere d'aiuto. L'installazione è strongmente ispirata da OAuth2 nonostante i suoi inconvenienti , alcuni dei quali discuterò.

DISCLAIMER: non sono un ragazzo della sicurezza per mestiere, e quello che ho creato l'ho creato con l'aiuto di Google e di tutti gli esempi che ho trovato.

Quando ho iniziato a cercare come avrei costruito l'API Web che supportava le applicazioni client, ho deciso che volevo provare a rendere l'API il più apribile possibile. Una parte di me è stata tentata di raggiungere l'autenticazione di base HTTP e fare in modo che gli utenti si autenticassero su ogni richiesta, ma sono emersi due problemi che hanno reso la soluzione non apparentemente praticabile:

  1. La ricerca di convalida delle credenziali è una spesa non banale, poiché comporterebbe almeno una chiamata al database con ogni richiesta
  2. Il sistema è un sistema multi-tenant; identificando quale dei tenant di cui l'utente apparteneva avrebbe richiesto un terzo parametro e che non è supportato dall'autenticazione di base HTTP (1)

Le complessità coinvolte nell'autenticazione mi hanno spinto a optare per un sistema token, in cui l'utente avrebbe fatto una richiesta di autenticazione a un endpoint che restituirebbe un token identificativo e poi lo memorizzerebbe da qualche parte sul server in modo da poterlo utilizzare per convalidare richieste e collegamenti con alcuni dati utente necessari. Non è perfettamente privo di stato e ho cercato i token web JSON come approccio alternativo, ma la ricerca di token può essere fatta molto velocemente. (2)

I client si aggrappano a quel token fino a quando il server non accetta più il token. Il client tenta quindi di eseguire nuovamente l'autenticazione con il server e recuperare un nuovo token per autenticare le richieste future con. Questo è ciò che il tuo post fa riferimento alla strategia delle credenziali memorizzate nella cache e abbiamo deciso di utilizzarlo perché ci consente di mantenere un maggiore controllo sull'accesso all'applicazione. A condizione che il client possa essere considerato attendibile per conservare le proprie informazioni di autorizzazione e si connetta solo tramite una connessione protetta (per questo motivo si impone l'accesso solo HTTPS), non è necessariamente un cattivo modo di gestire le cose, se non da una prospettiva UX. Per il servizio web manteniamo effettivamente il token nella memoria locale del browser; dal momento che si tratta solo di un'identificazione temporanea e non della combinazione effettiva di nome utente / password dell'utente, abbiamo ritenuto questo "abbastanza buono" se non addirittura valido.

I token vengono quindi inviati all'API Web come parte di un'intestazione Autorizzazione o come parametro GET per i client in cui le intestazioni HTTP personalizzate non sono disponibili. Questo è importante perché consente una maggiore flessibilità nel modo in cui accediamo all'API da una vasta gamma di potenziali applicazioni client, molto simile a come è necessario supportare una CLI e un'app Web. I token bearer sono una cosa abbastanza comune ma non sono esattamente perfetti . I problemi di sicurezza della nostra applicazione non sono abbastanza importanti da dedicare ulteriore tempo a migliorare questo aspetto.

Una volta che il token è stato convalidato, entra in gioco l'autorizzazione. Ciò che ciò comporta può variare notevolmente, ma a quel punto nell'applicazione l'identità dell'utente è nota e quindi è necessario fornire un servizio di autorizzazione di qualche tipo all'identità dell'utente e all'oggetto / azione da controllare.

Per lo meno, se vuoi usare questo tipo di strategia, ci sono molte librerie progettate per implementare OAuth e OAuth2; Se non sei come noi e hai dei requisiti molto limitati, ti consiglio vivamente di utilizzare una libreria di sicurezza di terze parti attendibile perché molto probabilmente non riuscirai a correggere le cose la prima volta che provi. Cerco ancora un'alternativa di terze parti per sostituire il nostro attuale sistema di autenticazione perché so che è pieno di buchi e casi limite che non riesco nemmeno a immaginare.

Note

  1. Ciò non sarebbe necessario se il nostro sistema fosse costruito diversamente, ad esempio utilizzando diversi punti di accesso per ciascun cliente; in alternativa ho anche preso in considerazione l'idea di diventare intelligente e il prefisso del nome utente con un identificativo del titolare
  2. Ho avuto alcune idee su come rendere la stringa token facile da validare solo a livello computazionale invece di dover eseguire una ricerca I / O come obiettivo a lungo termine per il miglioramento; almeno i token contengono un byte di versione che consentirà gli aggiornamenti lungo la linea se / quando il processo di decodifica per esso cambia
risposta data 28.07.2015 - 00:47
fonte

Leggi altre domande sui tag