Servizio web RESTful: come autenticare le richieste da altri servizi?


117

Sto progettando un servizio Web RESTful a cui gli utenti devono accedere, ma anche altri servizi Web e applicazioni. Tutte le richieste in arrivo devono essere autenticate. Tutte le comunicazioni avvengono tramite HTTPS. L'autenticazione dell'utente funzionerà in base a un token di autenticazione, acquisito tramite POST del nome utente e della password (su una connessione SSL) a una risorsa / session fornita dal servizio.

Nel caso dei client del servizio Web, non vi è alcun utente finale dietro il servizio client. Le richieste vengono avviate da attività pianificate, eventi o altre operazioni del computer. L'elenco dei servizi di connessione è noto in anticipo (ovviamente, immagino). Come devo autenticare queste richieste provenienti da altri servizi (web)? Voglio che il processo di autenticazione sia il più semplice possibile da implementare per questi servizi, ma non a scapito della sicurezza. Quali sarebbero lo standard e le migliori pratiche per uno scenario come questo?

Opzioni a cui posso pensare (o che mi sono state suggerite):

  1. Chiedi ai servizi client di ricorrere a un nome utente e una password "falsi" e di autenticarli allo stesso modo degli utenti. Non mi piace questa opzione, semplicemente non mi sembra giusta.

  2. Assegna un ID applicazione permanente per il servizio client, possibilmente anche una chiave dell'applicazione. Per quanto ho capito, è come avere nome utente + password. Con questo ID e questa chiave, posso autenticare ogni richiesta o creare un token di autenticazione per autenticare ulteriori richieste. Ad ogni modo, questa opzione non mi piace, perché chiunque possa ottenere una copia dell'ID e della chiave dell'applicazione può impersonare il client.

  3. Potrei aggiungere un controllo dell'indirizzo IP all'opzione precedente. Ciò renderebbe più difficile eseguire richieste false.

  4. Certificati client. Imposta la mia autorità di certificazione, crea il certificato radice e crea certificati client per i servizi client. Mi vengono in mente un paio di problemi: a) come posso ancora consentire agli utenti di autenticarsi senza certificati eb) quanto è complicato questo scenario da implementare dal punto di vista del servizio client?

  5. Qualcos'altro: ci devono essere altre soluzioni là fuori?

Il mio servizio sarebbe in esecuzione su Java, ma ho deliberatamente tralasciato le informazioni su quale framework specifico sarebbe stato costruito, perché sono più interessato ai principi di base e non tanto ai dettagli di implementazione - presumo che la soluzione migliore per questo sarà essere possibile implementare indipendentemente dal framework sottostante. Tuttavia, sono un po 'inesperto con questo argomento, quindi saranno molto apprezzati anche suggerimenti ed esempi concreti sull'attuale implementazione (come utili librerie di terze parti, articoli, ecc.).


Se posso suggerire, familiarizza con i servizi del sito Web di grandi dimensioni e scegli quello che ti piace. I tuoi utenti troverebbero anche somiglianze con le migliori pratiche di altri servizi RESTful.
Yzmir Ramirez

Trovata un'altra domanda (di quasi due anni) che tocca un argomento simile: stackoverflow.com/questions/1138831/…
Tommi

Su quale sistema operativo sono ospitati i servizi (sia il web che gli altri)? Sono in esecuzione su server che fanno parte della stessa infrastruttura?
Anders Abel

Il sistema operativo può variare: Win, * nix, ecc. E i servizi client possono o meno essere nella stessa infrastruttura del mio servizio.
Tommi

Risposte:


34

Qualsiasi soluzione a questo problema si riduce a un segreto condiviso. Inoltre non mi piace l'opzione nome utente e password hardcoded, ma ha il vantaggio di essere abbastanza semplice. Anche il certificato client è buono, ma è davvero molto diverso? C'è un certificato sul server e uno sul client. Il vantaggio principale è che è più difficile usare la forza bruta. Si spera che tu abbia altre protezioni per proteggerti da questo.

Non credo che il tuo punto A per la soluzione del certificato client sia difficile da risolvere. Devi solo usare un ramo. if (client side certificat) { check it } else { http basic auth }Non sono un esperto di Java e non ho mai lavorato con esso per creare certificati lato client. Tuttavia, un rapido Google ci porta a questo tutorial che sembra proprio il tuo vicolo.

Nonostante tutta questa discussione su "cosa è meglio", lasciatemi sottolineare che esiste un'altra filosofia che dice: "meno codice, meno intelligenza è meglio". (Personalmente sostengo questa filosofia). La soluzione del certificato client suona come un sacco di codice.

So che hai espresso domande su OAuth, ma la proposta OAuth2 include una soluzione al tuo problema chiamata " bearer token " che deve essere utilizzata insieme a SSL. Penso, per semplicità, sceglierei l'utente / pass hard-coded (uno per app in modo che possano essere revocati individualmente) o i token di portatore molto simili.


27
I certificati client NON sono un segreto condiviso. Ecco perché esistono. Il client ha una chiave privata e il server ha una chiave pubblica. Il client non condivide mai il suo segreto e la chiave pubblica non è un segreto.
Tim

5
Il collegamento al tutorial non porta a un articolo tutorial ma a qualche pagina di indice Java sul sito Oracle ...
Marjan Venema

2
@MarjanVenema Beh, è ​​perché stai provando il link +2 anni dopo che newz2000 ha risposto, ma puoi sempre provare WayBack Machine: web.archive.org/web/20110826004236/http://java.sun.com/…
Fábio Duque Silva

1
@MarjanVenema: Mi dispiace, ma ti aspetti che newz2000 venga qui e aggiorni il collegamento dopo che è morto? Come hai detto, è link rot, quindi prima o poi succede. O provi ad accedere all'archivio per vedere cosa stava vedendo l'autore in quel momento, oppure trovi il nuovo link e dai un contributo positivo. Non vedo come il tuo commento abbia aiutato nessuno. Ma qui, segui questo link: oracle.com/technetwork/articles/javase/… (attenzione che alla fine marcirà anche)
Fábio Duque Silva

2
@ FábioSilva: No. Non mi aspetto che lo faccia. Ho letto l'archivio ma non ho avuto il tempo di andare a caccia di un nuovo collegamento, quindi ho fatto la cosa migliore da fare: inserire un commento che è morto in modo che qualcun altro della comunità possa trovare la nuova posizione e aggiornare il inviare. Dato che ovviamente hai avuto il tempo di trovare il nuovo link, perché non hai aggiornato il link nel post invece di metterlo in un commento che mi attanagliava?
Marjan Venema

36

Dopo aver letto la tua domanda, direi, genera un token speciale per fare richiesta richiesta. Questo token vivrà in un tempo specifico (diciamo in un giorno).

Ecco un esempio da per generare il token di autenticazione:

(day * 10) + (month * 100) + (year (last 2 digits) * 1000)

ad esempio: 3 giugno 2011

(3 * 10) + (6 * 100) + (11 * 1000) = 
30 + 600 + 11000 = 11630

quindi concatenare con la password dell'utente, ad esempio "my4wesomeP4ssword!"

11630my4wesomeP4ssword!

Quindi fai MD5 di quella stringa:

05a9d022d621b64096160683f3afe804

Quando chiami una richiesta, usa sempre questo token,

https://mywebservice.com/?token=05a9d022d621b64096160683f3afe804&op=getdata

Questo token è sempre unico ogni giorno, quindi immagino che questo tipo di protezione sia più che sufficiente per proteggere sempre il tuo servizio.

La speranza aiuta

:)


1
Mi piace molto il modo in cui hai aggiunto il token di sicurezza a ogni richiesta, ma cosa succede quando il programmatore ha già creato centinaia di pagine jsp e dopo aver implementato la sicurezza in 100 pagine precedentemente create, nonché le pagine che verranno create. In tal caso, aggiungere un gettone in ogni richiesta non è una scelta corretta. :)
Ankur Verma

4
Cosa succede se gli orologi non sono sincronizzati? Il client non genererà il token sbagliato in questa istanza? Anche se entrambi generassero il datetime in UTC, i loro orologi potrebbero comunque essere diversi risultando in una finestra temporale ogni giorno, quando il token non funzionerebbe?
NickG

@ NickG, ho avuto questo problema prima, l'unico modo per proteggerlo richiedendo l'ora del server. Questo eliminerà il 99% il problema dell'UTC. Ovviamente lo svantaggio è una chiamata extra al server.
kororo

ma posso ancora utilizzare il tuo servizio web utilizzando questo token per un giorno, giusto? Non vedo come questo aiuterà
Mina Gabriel

@MinaGabriel puoi aggiungere più tempo nella generazione del token. (minuto * 10) + (ora * 100) + (giorno * 1000) + (mese * 10000) + (anno (ultime 2 cifre) * 100000)
kororo

11

Esistono diversi approcci che puoi adottare.

  1. I puristi RESTful vorranno che tu utilizzi l'autenticazione BASIC e invii le credenziali a ogni richiesta. La loro logica è che nessuno memorizza nessuno stato.

  2. Il servizio client potrebbe memorizzare un cookie, che mantiene un ID di sessione. Personalmente non lo trovo offensivo come alcuni dei puristi da cui ho sentito parlare: può essere costoso autenticarsi più e più volte. Tuttavia, sembra che questa idea non ti piaccia troppo.

  3. Dalla tua descrizione, sembra davvero che potresti essere interessato a OAuth2 La mia esperienza finora, da quello che ho visto, è che è un po 'confuso e un po' sanguinante. Ci sono implementazioni là fuori, ma sono poche e lontane tra loro. In Java, ho capito che è stato integrato nei moduli di sicurezza di Spring3 . (Il loro tutorial è scritto bene.) Stavo aspettando di vedere se ci sarà un'estensione in Restlet , ma finora, sebbene sia stato proposto e potrebbe essere nell'incubatore, non è stato ancora completamente incorporato.


Non ho nulla contro l'opzione 2 - Penso che sia una buona soluzione in un'app RESTful - ma da dove il servizio client ottiene il token in primo luogo? Come si autenticano la prima volta? Forse lo sto pensando male, ma sembra strano che il servizio client abbia bisogno di avere il proprio nome utente e password per questo.
Tommi

Se l'utente finale è un tuo utente, il servizio intermediario può passarti le sue credenziali alla prima richiesta e puoi restituire un cookie o un altro token.
jwismar

Allo stesso modo, nello scenario OAuth, l'utente finale delega al servizio intermediario il proprio accesso al servizio web.
jwismar

Sembra che ci sia un malinteso: non c'è alcun utente finale dietro il servizio clienti . Ho aggiornato la mia domanda per spiegare meglio la situazione.
Tommi

1
Vorrei solo aggiungere che l'opzione n. 1 sopra elencata dovrebbe essere eseguita solo su HTTPS.
mr-sk

3

Credo che l'approccio:

  1. Prima richiesta, il client invia ID / passcode
  2. Scambia ID / passaggio per token univoco
  3. Convalida il token su ogni richiesta successiva fino alla scadenza

è piuttosto standard, indipendentemente da come si implementa e da altri dettagli tecnici specifici.

Se vuoi davvero spingere la busta, forse potresti considerare la chiave https del client in uno stato temporaneamente non valido fino a quando le credenziali non vengono convalidate, limitare le informazioni se non lo sono mai e concedere l'accesso quando vengono convalidate, in base alla scadenza.

Spero che questo ti aiuti


3

Per quanto riguarda l'approccio del certificato client, non sarebbe molto difficile da implementare pur consentendo agli utenti senza certificati client di entrare.

Se in effetti creassi la tua Autorità di certificazione autofirmata e avessi emesso certificati client a ciascun servizio client, avresti un modo semplice per autenticare quei servizi.

A seconda del server web in uso, dovrebbe esserci un metodo per specificare l'autenticazione del client che accetterà un certificato del client, ma non ne richiede uno. Ad esempio, in Tomcat, quando si specifica il connettore https, è possibile impostare "clientAuth = want" invece di "true" o "false". Dovresti quindi assicurarti di aggiungere il tuo certificato CA autofirmato al tuo truststore (per impostazione predefinita il file cacerts nel JRE che stai utilizzando, a meno che tu non abbia specificato un altro file nella configurazione del tuo server web), in modo che gli unici certificati affidabili sarebbero quelli emessi da la tua CA autofirmata.

Sul lato server, consenti l'accesso ai servizi che desideri proteggere solo se sei in grado di recuperare un certificato client dalla richiesta (non nullo) e supera i controlli DN se preferisci una sicurezza aggiuntiva. Per gli utenti senza certificati client, potrebbero comunque accedere ai tuoi servizi, ma semplicemente non avranno certificati presenti nella richiesta.

Secondo me questo è il modo più "sicuro", ma sicuramente ha la sua curva di apprendimento e il suo overhead, quindi potrebbe non essere necessariamente la soluzione migliore per le tue esigenze.


3

5. Qualcos'altro: devono esserci altre soluzioni là fuori?

Hai ragione, c'è! E si chiama JWT (JSON Web Tokens).

JSON Web Token (JWT) è uno standard aperto (RFC 7519) che definisce un modo compatto e autonomo per trasmettere in modo sicuro le informazioni tra le parti come oggetto JSON. Queste informazioni possono essere verificate e attendibili perché sono firmate digitalmente. I JWT possono essere firmati utilizzando un segreto (con l'algoritmo HMAC) o una coppia di chiavi pubblica / privata utilizzando RSA.

Consiglio vivamente di esaminare i JWT. Sono una soluzione molto più semplice al problema rispetto a soluzioni alternative.

https://jwt.io/introduction/


1

È possibile creare sessioni sul server e condividere sessionIdtra client e server con ogni chiamata REST.

  1. Prima richiesta REST autenticazione: /authenticate. Restituisce la risposta (secondo il formato del cliente) con sessionId: ABCDXXXXXXXXXXXXXX;

  2. Conservare questo sessionIdin Mapcon la sessione attuale. Map.put(sessionid, session)o utilizzare SessionListenerper creare e distruggere chiavi per te;

    public void sessionCreated(HttpSessionEvent arg0) {
      // add session to a static Map 
    }
    
    public void sessionDestroyed(HttpSessionEvent arg0) {
      // Remove session from static map
    }
    
  3. Ottieni sessionid con ogni chiamata REST, come URL?jsessionid=ABCDXXXXXXXXXXXXXX(o in altro modo);

  4. Recupera HttpSessiondalla mappa utilizzando sessionId;
  5. Convalida la richiesta per quella sessione se la sessione è attiva;
  6. Invia risposta o messaggio di errore.

0

Vorrei che un'applicazione reindirizzasse un utente al tuo sito con un parametro id applicazione, una volta che l'utente approva la richiesta, genera un token univoco che viene utilizzato dall'altra app per l'autenticazione. In questo modo le altre applicazioni non gestiscono le credenziali utente e altre applicazioni possono essere aggiunte, rimosse e gestite dagli utenti. Foursquare e pochi altri siti si autenticano in questo modo ed è molto facile da implementare come l'altra applicazione.


Hmm, non sono sicuro di essere stato in grado di seguire la spiegazione. Di quale utente stiamo parlando? Sto parlando di un'applicazione che comunica con un'altra applicazione. Suppongo che tu l'abbia capito, ma ancora non riesco a capire del tutto. Cosa succede quando questo "token" scade, ad esempio?
Tommi

Ebbene, il token che generi e rispedisci all'altra applicazione è un token persistente, è legato all'utente e all'applicazione. Ecco un collegamento a foursquares docs developer.foursquare.com/docs/oauth.html ed è fondamentalmente oauth2, quindi esaminalo per una buona soluzione di autenticazione.
Devin M

Sembra che ci sia un malinteso: non c'è alcun utente finale dietro il servizio clienti . La documentazione di foursquare a cui ti sei collegato menziona brevemente l'accesso senza utenti, quindi è stata almeno in qualche modo utile - grazie! Ma non sono ancora in grado di formare un quadro completo di come funzionerebbe nella realtà.
Tommi

Basta generare le chiavi per le applicazioni quindi, se tutto ciò che stai facendo è consentire l'accesso alle applicazioni, allora un semplice application_id e application_key dovrebbero funzionare per l'autenticazione. Se vuoi che si autenticino con un token, controlla l'utilizzo delle opzioni di autenticazione del token di devise poiché sarebbe solo un parametro passato con la richiesta dell'URL alla tua applicazione.
Devin M

Ma allora non è esattamente lo stesso dello scenario nome utente + password = token di autenticazione della sessione? Application_id e application_key non sono solo sinonimi di nome utente e password? :) Va benissimo se questa è davvero la pratica standard per una situazione come questa - come ho detto, non sono esperto in questo - ma ho solo pensato che potessero esserci altre opzioni ...
Tommi

-3

Oltre all'autenticazione, ti suggerisco di pensare al quadro generale. Considera l'idea di creare il tuo servizio RESTful di backend senza alcuna autenticazione; quindi inserire un servizio di livello intermedio richiesto per l'autenticazione molto semplice tra l'utente finale e il servizio di backend.


Tra l'utente finale e il servizio di backend? Ciò non lascerebbe i servizi client completamente non autenticati? Non è quello che voglio. Ovviamente posso posizionare quel livello intermedio tra il mio servizio web ei servizi client, ma questo lascia ancora aperta la domanda: quale sarebbe il modello di autenticazione effettivo?
Tommi

Il livello intermedio può essere un server web come Nginx, qui puoi eseguire l'autenticazione. Il modello di autenticazione può essere basato sulla sessione.
Dagang

Come ho cercato di spiegare nella mia domanda, non desidero uno schema di autenticazione basato sulla sessione per questi servizi client. (Si prega di vedere i miei aggiornamenti alla domanda.)
Tommi

Ti suggerisco di utilizzare la lista degli ip bianchi o l'intervallo di ip invece di nome e password. Di solito, l'ip del servizio client è stabile.
Dagang
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.