Le sessioni violano davvero RESTfulness?


491

L'utilizzo delle sessioni in un'API RESTful sta davvero violando RESTfulness? Ho visto molte opinioni andare in entrambe le direzioni, ma non sono convinto che le sessioni siano RESTless . Dal mio punto di vista:

  • l'autenticazione non è vietata per RESTfulness (altrimenti ci sarebbe poco uso nei servizi RESTful)
  • l'autenticazione viene eseguita inviando un token di autenticazione nella richiesta, generalmente l'intestazione
  • questo token di autenticazione deve essere ottenuto in qualche modo e può essere revocato, nel qual caso deve essere rinnovato
  • il token di autenticazione deve essere convalidato dal server (altrimenti non sarebbe autenticazione)

Quindi, in che modo le sessioni violano questo?

  • lato client, le sessioni sono realizzate utilizzando i cookie
  • i cookie sono semplicemente un'intestazione HTTP aggiuntiva
  • un cookie di sessione può essere ottenuto e revocato in qualsiasi momento
  • i cookie di sessione possono avere una durata infinita se necessario
  • l'id sessione (token di autenticazione) è validato lato server

In quanto tale, per il client, un cookie di sessione è esattamente uguale a qualsiasi altro meccanismo di autenticazione basato su intestazione HTTP, tranne per il fatto che utilizza l' Cookieintestazione invece Authorizationdell'altra intestazione proprietaria. Se non vi era alcuna sessione allegata al valore del cookie lato server, perché ciò farebbe la differenza? L'implementazione sul lato server non deve riguardare il client purché il server si comporti RESTful. Pertanto, i cookie da soli non dovrebbero rendere un'API RESTless e le sessioni sono semplicemente cookie per il client.

I miei presupposti sono sbagliati? Cosa rende RESTless i cookie di sessione ?



5
Per aggiungere ciò, se si utilizza la sessione solo per l'autenticazione, perché non utilizzare le intestazioni fornite? In caso contrario, e stai utilizzando la sessione per altri stati della conversazione, ciò viola il vincolo Stateless di REST.
Will Hartung,

2
@Will Thanks. Sembra che tu stia parlando di sessioni per l'archiviazione temporanea dei dati inviati dagli utenti, mentre nel mio caso ne sto solo parlando come un dettaglio di implementazione per l'autenticazione. Potrebbe essere questa la causa del disaccordo?
Inganno

3
@deceze Il mio unico punto è che se intendi utilizzare un'intestazione per rappresentare un token di autenticazione, HTTP ne fornisce uno oltre a un cookie generico. Quindi, perché non usarlo e mantenere la semantica libera che ottieni con esso (chiunque veda il payload può vedere che c'è un token di autenticazione assegnato ad esso).
Will Hartung,

7
Certo, ma allora perché non creare le tue intestazioni o dirottare qualche altra intestazione per il token di autenticazione. Usa l'intestazione X-XYZZY. È solo sintassi, giusto? Le intestazioni trasmettono informazioni. L'intestazione dell'autorizzazione è più "autocompattante" rispetto al tuo cookie, perché "tutti" sanno a cosa serve l'intestazione Auth. Se vedono solo JSESSIONID (o qualsiasi altra cosa), non possono fare ipotesi, o peggio, fare ipotesi sbagliate (cos'altro sta memorizzando nella sessione, cos'altro viene usato per questo, ecc.). Nomini le tue variabili nel tuo codice Aq12hsg? No certo che no. La stessa cosa vale qui.
Will Hartung,

Risposte:


299

Innanzitutto, definiamo alcuni termini:

  • Riposante:

    Si possono caratterizzare le applicazioni conformi ai vincoli REST descritti in questa sezione come "RESTful". [15] Se un servizio viola uno dei vincoli richiesti, non può essere considerato RESTful.

    secondo Wikipedia .

  • vincolo stateless:

    Successivamente aggiungere un vincolo all'interazione client-server: la comunicazione deve essere di natura stateless, come nello stile client-stateless-server (CSS) della Sezione 3.4.3 (Figura 5-3), in modo tale che ogni richiesta da client a il server deve contenere tutte le informazioni necessarie per comprendere la richiesta e non può sfruttare alcun contesto memorizzato sul server. Lo stato della sessione viene quindi mantenuto interamente sul client.

    secondo la tesi di Fielding .

Quindi le sessioni lato server violano il vincolo stateless di REST, e quindi anche RESTfulness.

Pertanto, per il cliente, un cookie di sessione è esattamente uguale a qualsiasi altro meccanismo di autenticazione basato su intestazione HTTP, tranne per il fatto che utilizza l'intestazione del cookie anziché l'autorizzazione o qualche altra intestazione proprietaria.

Tramite i cookie di sessione memorizzi lo stato del client sul server e quindi la tua richiesta ha un contesto. Proviamo ad aggiungere un bilanciamento del carico e un'altra istanza di servizio al tuo sistema. In questo caso, è necessario condividere le sessioni tra le istanze del servizio. È difficile mantenere ed estendere un tale sistema, quindi si ridimensiona male ...

Secondo me non c'è niente di sbagliato nei cookie. La tecnologia dei cookie è un meccanismo di memorizzazione lato client in cui i dati memorizzati vengono collegati automaticamente alle intestazioni dei cookie per ogni richiesta. Non conosco un vincolo REST che ha problemi con quel tipo di tecnologia. Quindi non c'è nessun problema con la tecnologia stessa, il problema è con il suo utilizzo. Fielding ha scritto una sottosezione sul perché ritiene che i cookie HTTP siano dannosi.

Dal mio punto di vista:

  • l'autenticazione non è vietata per RESTfulness (altrimenti ci sarebbe poco uso nei servizi RESTful)
  • l'autenticazione viene eseguita inviando un token di autenticazione nella richiesta, generalmente l'intestazione
  • questo token di autenticazione deve essere ottenuto in qualche modo e può essere revocato, nel qual caso deve essere rinnovato
  • il token di autenticazione deve essere convalidato dal server (altrimenti non sarebbe autenticazione)

Il tuo punto di vista è stato piuttosto solido. L'unico problema riguardava il concetto di creazione di token di autenticazione sul server. Non hai bisogno di quella parte. Ciò di cui hai bisogno è la memorizzazione di nome utente e password sul client e inviarlo con ogni richiesta. Non è necessario altro per eseguire questa operazione oltre all'autenticazione di base HTTP e una connessione crittografata:

Figura 1. - Autenticazione senza stato da parte di client attendibili

  • Figura 1. - Autenticazione senza stato da parte di client attendibili

Probabilmente avrai bisogno di una cache di autenticazione in memoria sul lato server per rendere le cose più veloci, poiché devi autenticare ogni richiesta.

Ora funziona abbastanza bene da clienti fidati scritti da te, ma per quanto riguarda i client di terze parti? Non possono avere nome utente, password e tutte le autorizzazioni degli utenti. Quindi devi memorizzare separatamente quali autorizzazioni può avere un client di terze parti da un utente specifico. Pertanto, gli sviluppatori client possono registrare i propri client di terze parti e ottenere una chiave API univoca e gli utenti possono consentire ai client di terze parti di accedere a una parte delle loro autorizzazioni. Come leggere il nome e l'indirizzo e-mail o elencare i loro amici, ecc ... Dopo aver autorizzato un client di terze parti, il server genererà un token di accesso. Questi token di accesso possono essere utilizzati dal client di terze parti per accedere alle autorizzazioni concesse dall'utente, in questo modo:

Figura 2. - Autenticazione senza stato da parte di client di terze parti

  • Figura 2. - Autenticazione senza stato da parte di client di terze parti

Pertanto, il client di terze parti può ottenere il token di accesso da un client attendibile (o direttamente dall'utente). Successivamente può inviare una richiesta valida con la chiave API e il token di accesso. Questo è il meccanismo di autenticazione di base di terze parti. Puoi leggere ulteriori informazioni sui dettagli di implementazione nella documentazione di ogni sistema di autenticazione di terze parti, ad esempio OAuth. Naturalmente questo può essere più complesso e più sicuro, ad esempio puoi firmare i dettagli di ogni singola richiesta sul lato server e inviare la firma insieme alla richiesta, e così via ... La soluzione effettiva dipende dalle esigenze della tua applicazione.


5
Sì, hai perfettamente ragione. Da quando ho pubblicato questa domanda, sono tornato a vederlo. I cookie di sessione non sono niente di speciale se guardati nei dettagli tecnici, ma manca la foresta per gli alberi. Ha accettato la tua risposta a causa dei bei grafici. ;)
ingannare

1
Ok, ho ripensato, la risposta del servizio REST non dovrebbe dipendere dall'autorizzazione, quindi penso che le prime 2 soluzioni siano a posto al 100% e le altre siano a posto se il servizio utilizza le informazioni solo per decidere se consentire la richiesta o non. Quindi penso che le autorizzazioni dell'utente debbano influire sulla rappresentazione della risorsa corrente.
inf3rno,

1
Creerò una domanda sulla dipendenza delle autorizzazioni dalle rappresentazioni. Estenderò questa risposta non appena avrò la soluzione corretta.
inf3rno,

3
@ inf3rno, è vero che un servizio completamente RESTful non può dipendere dai cookie di sessione per l'autenticazione nel modo in cui viene tradizionalmente implementato. Tuttavia, è possibile utilizzare i cookie per eseguire l'autenticazione se il cookie contiene tutte le informazioni sullo stato necessarie in seguito al server. Puoi anche proteggere il cookie dalla manomissione firmandolo con una coppia di chiavi pubblica / privata. Vedi i miei commenti qui sotto.
jcoffland,

3
Non capisco perché tutti sembrano accettare il commento che dovresti archiviare le password sul lato client e inviarle ad ogni richiesta. Questa è una brutta pratica e mette in pericolo i dati sensibili dei tuoi clienti. Una password non cancellata (che dovrebbe essere per inviarla più volte) non dovrebbe mai essere archiviata da nessuna parte. Se lo accettiamo, allora stai usando i token come fanno la maggior parte dei sistemi di autenticazione, nel qual caso qualunque meccanismo che usiamo per ridimensionare il repository di token avrà per lo più problemi di scalabilità uguali a quelli di qualsiasi scalabilità di sessione.
lvoelk,

334

Prima di tutto, REST non è una religione e non dovrebbe essere affrontato come tale. Mentre ci sono vantaggi per i servizi RESTful, dovresti seguire i principi di REST solo nella misura in cui hanno senso per la tua applicazione.

Detto questo, l'autenticazione e lo stato lato client non violano i principi REST. Mentre REST richiede che le transizioni di stato siano apolidi, ciò si riferisce al server stesso. Al centro, tutto il REST riguarda i documenti. L'idea alla base dell'apolidia è che il SERVER è apolide, non i client. Qualsiasi client che emette una richiesta identica (stesse intestazioni, cookie, URI, ecc.) Deve essere portato nello stesso posto nell'applicazione. Se il sito Web memorizzasse la posizione corrente dell'utente e gestisse la navigazione aggiornando questa variabile di navigazione sul lato server, REST verrebbe violato. Un altro client con informazioni di richiesta identiche verrebbe portato in una posizione diversa a seconda dello stato sul lato server.

I servizi web di Google sono un fantastico esempio di un sistema RESTful. Richiedono un'intestazione di autenticazione con la chiave di autenticazione dell'utente da trasmettere ad ogni richiesta. Ciò viola leggermente i principi REST, poiché il server sta monitorando lo stato della chiave di autenticazione. Lo stato di questa chiave deve essere mantenuto e presenta una sorta di data / ora di scadenza dopo le quali non concede più l'accesso. Tuttavia, come ho menzionato all'inizio del mio post, è necessario fare sacrifici per consentire a un'applicazione di funzionare davvero. Detto questo, i token di autenticazione devono essere archiviati in modo da consentire a tutti i possibili client di continuare a concedere l'accesso durante i loro periodi di validità. Se un server sta gestendo lo stato della chiave di autenticazione al punto che un altro server con bilanciamento del carico non può subentrare alle richieste che soddisfano basate su quella chiave, hai iniziato a violare davvero i principi di REST. I servizi di Google assicurano che, in qualsiasi momento, puoi prendere un token di autenticazione che stavi utilizzando sul tuo telefono contro il server A del bilanciamento del carico e colpire il server B del bilanciamento del carico dal desktop e avere comunque accesso al sistema ed essere indirizzato alle stesse risorse se le richieste erano identiche.

Ciò che si riduce a tutto è che è necessario assicurarsi che i token di autenticazione siano convalidati rispetto a un archivio di backup di qualche tipo (database, cache, qualunque cosa) per assicurarsi di conservare il maggior numero possibile di proprietà REST.

Spero che tutto ciò abbia senso. Dovresti anche consultare la sezione Vincoli dell'articolo di Wikipedia sul trasferimento dello stato rappresentativo se non l'hai già fatto. È particolarmente illuminante per ciò che i principi di REST stanno effettivamente sostenendo e perché.


6
Vorrei riformulare la tua dichiarazione iniziale. Utilizzare REST solo se i vincoli di REST hanno senso per l'applicazione. Sei libero di applicare un sottoinsieme di tali vincoli e otterrai un sottoinsieme dei vantaggi. Tuttavia, a quel punto hai creato il tuo stile architettonico. Non è una brutta cosa, in realtà è proprio quello che riguardano i primi quattro capitoli della tesi di Roy, il design di principio. REST era solo un esempio.
Darrel Miller,

4
@Darrel Un punto abbastanza giusto. Onestamente non sono sicuro di come lo faccia Google, ma il tempo di scadenza potrebbe essere codificato nel token di autenticazione. Credo che il mio punto più grande sia ancora valido. Esistono alcuni tipi di stato che devono essere semplicemente mantenuti e fintanto che capisci perché REST richiede apolidia, puoi violarlo in un modo che ha senso senza molte ripercussioni sul resto del sistema e sui vantaggi di un'architettura RESTful .
Jared Harding,

7
Dato che finora non sono state avanzate altre argomentazioni, sto accettando questa risposta ben scritta. Penso che la parte importante sia che server senza stato non significa server senza stato , qualcosa che penso sia spesso frainteso o applicato male. Il server può (e di solito deve ) avere lo stato desiderato, purché si comporti idempotente .
Inganno

10
Ho ascoltato così tante prediche che le sessioni non sono riposanti. L'autenticazione di base HTTP è un vero passo indietro se stai provando a creare un'app Web.
Ben Thurley,

1
@Micah Henning, stai assumendo il falso presupposto che il server richiede informazioni sullo stato per convalidare il token di autenticazione. Possiamo ragionevolmente presumere che non sia possibile falsificare un token firmato da una coppia di chiavi pubblica / privata se non si conosce la chiave privata. Per verificare che il token sia valido, è sufficiente disporre della chiave pubblica. Ritengo ancora che sia possibile un'autenticazione completamente RESTful.
jcoffland,

12

I cookie non sono per l'autenticazione. Perché reinventare una ruota? HTTP ha meccanismi di autenticazione ben progettati. Se usiamo i cookies, cadiamo nella utilizzando HTTP come solo un protocollo di trasporto, quindi abbiamo bisogno di creare il nostro proprio sistema di segnalazione, ad esempio, per dire agli utenti che hanno fornito l'autenticazione sbagliato (usando HTTP 401 sarebbe corretto in quanto probabilmente non avremmo fornire Www-Authenticatea un client, come richiedono le specifiche HTTP :)). Va anche notato che Set-Cookieè solo una raccomandazione per il cliente. Il suo contenuto può essere salvato o meno (ad esempio, se i cookie sono disabilitati), mentreAuthorization intestazione viene inviata automaticamente ad ogni richiesta.

Un altro punto è che, per ottenere un cookie di autorizzazione, probabilmente vorrai fornire prima le tue credenziali da qualche parte? Se è così, allora non sarebbe RESTless? Esempio semplice:

  • Si prova GET /a senza cookie
  • In qualche modo ricevi una richiesta di autorizzazione
  • Vai e autorizzi in qualche modo POST /auth
  • Hai capito Set-Cookie
  • Prova GET /a con i cookie. Ma GET /ain questo caso si comporta in modo idempotente?

Per riassumere, credo che se accediamo ad alcune risorse e dobbiamo autenticarci, allora dobbiamo autenticarci su quella stessa risorsa , non altrove.


1
Nel frattempo mi sono avvicinato di più anche a questo punto di vista. Penso che tecnicamente faccia poca differenza, sono solo intestazioni HTTP. È vero, tuttavia, che il comportamento di autenticazione stesso non è RESTful, se è richiesto l'accesso tramite un indirizzo separato. Quindi i cookie sono solo un sintomo di un problema più grande con il sistema di autenticazione.
Inganno

Questo non tiene conto del fatto che i browser Web supportano solo Authorization: Basico Digest. Se vuoi fare qualcosa di più avanzato di base o digest (e dovresti) in un contesto di browser, allora avrai bisogno di qualcosa di diverso Authorizationdall'intestazione.
Oliver Charlesworth,

1
Assolutamente: se stai facendo JS puro, le cose sono sostanzialmente OK (tranne, ad esempio, Websocket). Ma il mio punto è che l'autenticazione basata su API non è necessariamente l'unica considerazione in uno scenario di browser.
Oliver Charlesworth,

5
GET /asenza un cookie e con un cookie sono chiaramente due richieste diverse ed è accettabile che si comportino diversamente.
TRiG

1
Per aggiungere a @TRiG, ​​seguendo questa logica, GET /aanche l'intestazione di autenticazione è la stessa GET /asenza l'intestazione di autenticazione, rendendola ugualmente inutilizzabile per REST. Se hai intenzione di trattare un'intestazione http in modo diverso da un altro, lo affronterai almeno.
Jasper,

7

In realtà, RESTfulness si applica solo alle RISORSE, come indicato da un identificatore di risorse universale. Quindi anche parlare di cose come intestazioni, cookie, ecc. Riguardo a REST non è proprio appropriato. REST può funzionare su qualsiasi protocollo, anche se accade regolarmente su HTTP.

Il determinante principale è questo: se si invia una chiamata REST, che è un URI, quindi una volta che la chiamata viene eseguita correttamente sul server, l'URI restituisce lo stesso contenuto, presupponendo che non siano state eseguite transizioni (PUT, POST, DELETE) ? Questo test escluderebbe la restituzione di errori o richieste di autenticazione, poiché in quel caso la richiesta non è stata ancora inviata al server, ovvero il servlet o l'applicazione che restituirà il documento corrispondente all'URI specificato.

Allo stesso modo, nel caso di un POST o PUT, puoi inviare un determinato URI / payload e, indipendentemente da quante volte invii il messaggio, aggiornerà sempre gli stessi dati, in modo che i successivi GET restituiscano un risultato coerente?

REST riguarda i dati dell'applicazione, non le informazioni di basso livello richieste per trasferire tali dati.

Nel seguente post sul blog, Roy Fielding ha fornito un bel riassunto dell'intera idea REST:

http://groups.yahoo.com/neo/groups/rest-discuss/conversations/topics/5841

"Un sistema RESTful passa da uno stato stazionario all'altro, e ciascuno di questi stati stazionari è sia un potenziale stato iniziale sia un potenziale stato finale. Vale a dire, un sistema RESTful è un numero sconosciuto di componenti che obbediscono a un semplice insieme di regole tali da essere sempre in REST o in transizione da uno stato RESTful ad un altro stato RESTful. Ciascuno stato può essere completamente compreso dalla rappresentazione o dalle rappresentazioni che contiene e dall'insieme delle transizioni che fornisce, con le transizioni limitate a un'uniforme insieme di azioni per essere comprensibili.Il sistema può essere un diagramma di stato complesso, ma ogni agente utente è in grado di vedere solo uno stato alla volta (l'attuale stato stazionario) e quindi ogni stato è semplice e può essere analizzato in modo indipendente. l'utente, OTOH, è in grado di creare le proprie transizioni in qualsiasi momento (ad esempio, inserire un URL, selezionare un segnalibro,aprire un editor, ecc.) "


Passando al problema dell'autenticazione, indipendentemente dal fatto che si realizzi tramite cookie o intestazioni, purché le informazioni non facciano parte dell'URI e del payload POST, non ha assolutamente nulla a che fare con REST. Quindi, per quanto riguarda l'apolidia, stiamo parlando solo dei dati dell'applicazione.

Ad esempio, quando l'utente immette i dati in una schermata della GUI, il client sta tenendo traccia di quali campi sono stati immessi, quali no, dei campi obbligatori mancanti ecc. Questo è tutto CONTESTO CLIENTE e non deve essere inviato o tracciato dal server. Ciò che viene inviato al server è l'insieme completo di campi che devono essere modificati nella risorsa IDENTIFICATA (dall'URI), in modo tale che si verifichi una transizione in quella risorsa da uno stato RESTful a un altro.

Pertanto, il client tiene traccia di ciò che l'utente sta facendo e invia al server solo transizioni di stato logicamente complete.


3
Non vedo come questo faccia luce sulla domanda posta.
jcoffland,

1

La transazione HTTP, l'autenticazione dell'accesso di base, non è adatta per RBAC, poiché l'autenticazione dell'accesso di base utilizza il nome utente crittografato: password ogni volta per identificare, mentre ciò che è necessario in RBAC è il ruolo che l'utente desidera utilizzare per una chiamata specifica. RBAC non convalida le autorizzazioni per il nome utente, ma per i ruoli.

Potresti provare a concatenare in questo modo: usernameRole: password, ma questa è una cattiva pratica ed è anche inefficiente perché quando un utente ha più ruoli, il motore di autenticazione dovrebbe testare tutti i ruoli in concatenazione, e che ogni chiamata di nuovo. Ciò distruggerebbe uno dei maggiori vantaggi tecnici di RBAC, vale a dire un test di autorizzazione molto rapido.

Quindi quel problema non può essere risolto usando l'autenticazione di accesso di base.

Per risolvere questo problema, è necessario mantenere la sessione e ciò sembra, secondo alcune risposte, in contraddizione con REST.

Questo è quello che mi piace della risposta che REST non dovrebbe essere trattato come una religione. In casi aziendali complessi, ad esempio nel settore sanitario, l'RBAC è assolutamente comune e necessario. E sarebbe un peccato se non gli fosse permesso di usare REST perché tutti i progettisti di strumenti REST considererebbero REST come una religione.

Per me non ci sono molti modi per mantenere una sessione su HTTP. Si possono usare i cookie, con un sessionId, o un'intestazione con un sessionId.

Se qualcuno ha un'altra idea, sarò felice di ascoltarla.



-4
  1. Le sessioni non sono RESTless
  2. Vuoi dire che il servizio REST è solo per uso http o ho sbagliato tutto? La sessione basata su cookie deve essere utilizzata solo per i propri servizi (!) Basati su http! (Potrebbe essere un problema lavorare con i cookie, ad esempio da Mobile / Console / Desktop / ecc.)
  3. se offri un servizio RESTful per gli sviluppatori di 3d party, non utilizzare mai sessioni basate su cookie, utilizza invece i token per evitare problemi di sicurezza.

3
il cookie non deve essere utilizzato per memorizzare una chiave di sessione per una sessione sul server che contiene il token di autenticazione. ma se il cookie contiene il token di autenticazione stesso, è una soluzione fattibile. (ovviamente il cookie dovrebbe essere httponly e protetto)
roberkules il
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.