Inserimento di una password in una chiamata API REST


31

Supponiamo che io abbia un'API REST utilizzata anche per impostare / ripristinare le password. Supponiamo anche che funzioni su connessioni HTTPS. C'è qualche buon motivo per non inserire quella password nel percorso della chiamata, diciamo anche che lo codificherò in BASE64?

Un esempio potrebbe essere quello di ripristinare una password come questa:

http://www.example.com/user/joe/resetpassword/OLDPASSWD/NEWPASSWD

Capisco che BASE64 non è una crittografia, ma in questo caso voglio solo proteggere la password per navigare in spalla.


61
Stai suggerendo effetti collaterali su GET? Questa è una violazione del protocollo proprio lì.
Esben Skov Pedersen,

27
Questo non è davvero REST perché resetpassword/OLDPASSWD/NEWPASSWDnon è una risorsa. È un'invocazione di un processo. Non è necessario inserire tutto in un URL.
usr

5
@Esben: chi ha detto che è un OTTENERE? L'OP non lo ha mai detto.
Dagnelies

3
È vero, non ha fatto nella domanda. Ma il suo commento alla risposta di Netch dice "Immagino che dovrò usare POST dopo tutto", quindi possiamo presumere che originariamente intendesse / chiedesse GET. Che, come sottolinea Esben, è una brutta cosa. OTTIENI solo lettura.
Mawg,

Risposte:


76

Un buon server registra tutte le richieste che gli vengono inviate, inclusi URL (spesso, senza parte variabile dopo '?'), IP di origine, tempo di esecuzione ... Vuoi davvero che questo registro (potenzialmente letto da un ampio gruppo di amministratori) contenga informazioni criticamente sicure come password? Base64 non è un tappo contro di loro.


42
Questo non è il motivo principale per l'utilizzo di POST. È un motivo di sicurezza. Ma come Esben già nota dinnanzi ai commenti, cambiare stato con un GET è una violazione di tale servizio di riposo
Pinoniq,

2
@BartFriederichs: e la cronologia del browser ricorderebbe l'URL. E provare un sacco di password in forma anonima creando una pagina Web che contiene un collegamento per tutte le password che si desidera provare e lasciando che Googlebot
esegua le

2
"Ma come Esben già nota dinnanzi ai commenti, cambiare stato con un GET è una violazione di tale servizio di Resto" Ho notato anche quel commento, ma non vedo dove qualcuno stia dicendo che questa era una richiesta GET. Dopotutto, puoi incorporare le informazioni in un URI e comunque postarle. Non è davvero RESTful , tuttavia, poiché l'URI non sta effettivamente nominando una risorsa.
Joshua Taylor,

Ciao, buona risposta, ma non sono d'accordo con "senza parte variabile dopo '?'" ... ce ne sono molti che memorizzano l'URL completo !!!
Dagnelies,

2
"cambiare stato con un GET è una violazione di tale servizio di riposo" - Non restiamo troppo presi dal REST. Cambiare stato con un GET è una violazione di HTTP .
Dan Ellis,

69

Quello che stai proponendo non è né sicuro né RESTful.

@Netch ha già menzionato il problema con i log, ma c'è anche un altro problema nel fatto che stai mostrando le password inviate da HTTP, rendendo banale catturare le password con qualsiasi tipo di sniffer di filo o attacco man-in-the-middle.

Quando si effettua una richiesta GET utilizzando REST, i diversi elementi nell'URL rappresentano elementi a grana più fine. L'URL indica che stai restituendo una parte NEWPASSWD di una OLDPASSWD che fa parte di una password di ripristino. Ciò non ha alcun senso di senso semantico. I GET non devono essere utilizzati per salvare i dati.

Dovresti fare qualcosa del genere:

POST https://www.example.com/user/joe/resetpassword/
{oldpasswd:[data], newpasswd:[data]}

POST perché stai scrivendo dati e https perché non vuoi che vengano annusati.

(Questa è davvero la sicurezza di basso livello. Il minimo assoluto che dovresti fare.)


2
Questo non significherebbe eseguire l'hashing delle password sul lato client? È raccomandato?
Rowan Freeman,

1
Nota: ciò non consentirà l'applicazione di requisiti di password (lunghezza, ecc.). Questo potrebbe non essere un problema nel tuo caso, anche se è una pratica di sicurezza frequente e talvolta richiesta da alcune entità.
Paul Draper,

8
Non stai creando un nuovo record ma stai aggiornando un record esistente (di solito), quindi dovrebbe essere PUT anziché POST.

4
Questo non è molto RESTful. resetpassword non è una risorsa e tanto meno una sotto-risorsa. Tuttavia, /user/joe/passwordè un po 'meglio ma non ottimale.
Whirlwin,

12
@CamilStaps No, non puoi usarlo PUT, perché PUTè idempotente. Ma quando la password è stata cambiata da secretcon supersecretsuccesso, la stessa richiesta fallirà la seconda volta, quindi POSTqui è corretta. Naturalmente, come ha detto @whirlwin, questa risorsa non è ben nominata.
Residuum

60

Lo schema proposto presenta problemi in diversi settori.

Sicurezza

I percorsi URL vengono frequentemente registrati; mettere le password non cancellate nel percorso è una cattiva pratica.

HTTP

Le informazioni di autenticazione / autorizzazione devono essere visualizzate nell'intestazione dell'autorizzazione. O potenzialmente, per roba basata su browser, l'intestazione Cookie.

RIPOSO

Verbi come resetpasswordnel tuo URL sono generalmente un chiaro segno di un paradigma di trasferimento di stato non rappresentativo. Un URL dovrebbe rappresentare una risorsa. Cosa significa OTTENERE resetpassword? O ELIMINA?

API

Questo schema richiede di conoscere sempre la password precedente. Probabilmente vorrai consentire più casi; ad es. la password viene persa.


È possibile utilizzare l' autenticazione di base o digest , ovvero schemi ben compresi.

PUT /user/joe/password HTTP/1.0
Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==
Content-Type: text/plain
Host: www.example.com

NEWPASSWD

Non inserisce informazioni ultra sensibili nel percorso e segue le convenzioni HTTP e REST.

Se è necessario consentire un'altra modalità di autorizzazione (ad esempio un token inviato tramite un canale verificato per reimpostare la password), è possibile utilizzare semplicemente un'intestazione di autorizzazione diversa senza dover modificare nient'altro.


4

A parte la sicurezza, il problema è che non è un approccio molto RESTful.

OLDPASSWDe NEWPASSWDnon rappresentare nulla nella gerarchia delle risorse e, peggio ancora, l'operazione non è idempotente.

Quindi puoi usare solo POSTcome verbo e non dovresti includere le due password nel tuo percorso delle risorse.


1
Non stai creando un nuovo record ma stai aggiornando un record esistente (di solito), quindi dovrebbe essere PUT anziché POST.

2
@CamilStaps Se fosse solo l'impostazione della password, potrebbe essere. Ma poiché presumibilmente anche la vecchia password deve essere verificata, rende l'operazione non idempotente, e quindi PUTviene squalificata come verbo. Potrebbe essere rigettato con cui lavorare, PUTma nella sua forma attuale non funziona.
biziclop,

OLDPASSWD è informazioni di autenticazione e non dovrebbe essere nell'URL.

Non necessariamente, è pratica comune richiedere esplicitamente la vecchia password oltre all'autenticazione.
biziclop,

3

Il problema è evitare le password in testo semplice nelle tue richieste. Esistono due opzioni per soddisfare i requisiti di servizio web riposanti.

1. Hashing lato client

  • Immagino che stai memorizzando le tue password come ad es. Hash (password + salt)
  • È possibile eseguire l'hashing della nuova password con un salt sul lato client
  • Ciò significa: creare un nuovo salt sul lato client, creare un hash, ad esempio hash (newPassword + newSalt)
  • Invia il nuovo hash creato più il sale al tuo servizio web riposante
  • Invia la vecchia password anche come hash (oldPassword + oldSalt)

2. Crittografia

  • Crea una risorsa "one time key" (otk) per un utente come / otk / john
  • Questa risorsa restituisce una chiave unica unica casuale sicura, ad esempio kbDlJbmNmQ0Y0SmRHZC9GaWtRMW0ycVJpYzhMcVNZTWlMUXN6ZWxLdTZESFRs e un ID univoco ad esempio 95648915125
  • Il tuo servizio web riposante deve memorizzare questo otk casuale per la prossima comunicazione sicura con l'ID 95648915125
  • Crittografa la tua nuova e vecchia password con l'otk ad es. AES (per motivi di sicurezza dovresti usare due otk separati per la vecchia e la nuova password)
  • Invia le password crittografate alla tua risorsa di modifica password con l'ID 95648915125
  • Una combinazione otk e ID può funzionare una sola volta, quindi è necessario eliminare quella combinazione dopo aver modificato la password
  • Possibile opzione migliore: invia la password attuale / precedente tramite Basic-Auth.

Nota: HTTPS è richiesto per entrambe le opzioni!


1
Perché dovrei crittografare due volte (il tuo schema di crittografia con OTK e HTTPS) lo scambio di password? Qual è il vettore di attacco qui che non è coperto da HTTPS?
Bart Friederichs,

1
L'unico scopo è evitare possibili log del server. È anche possibile registrare un corpo di richiesta HTTP (ad esempio con una nuova password in chiaro) anche se viene utilizzato HTTPS. Un altro possibile attacco è l'uso di un certificato autofirmato che viene utilizzato in particolare per scopi interni.
maz258

2

Quali sono le caratteristiche di un'operazione di reimpostazione della password?

  1. Cambia qualcosa.
  2. C'è un valore su cui è impostato.
  3. Solo alcune persone sono autorizzate a farlo (l'utente, un amministratore o entrambi, forse con regole diverse su come entrambi possono farlo).

Il punto 1 indica che non è possibile utilizzare GET, è necessario POST qualcosa che rappresenta l'operazione di modifica della password in un URI che rappresenta una risorsa che gestisce le modifiche della password oppure PUT qualcosa che rappresenta la nuova password in un URI che rappresenta la password o rappresenta qualcosa (ad es. utente) di cui quella password è una caratteristica.

Generalmente POST, non ultimo perché può essere scomodo MESSA IN OPERA qualcosa che non riusciamo a ottenere in seguito e, naturalmente, non possiamo ottenere la password.

Il punto 2 saranno quindi i dati che rappresentano la nuova password, in ciò che è POST.

Il punto 3 significa che avremo bisogno di autorizzare la richiesta, il che significa che se l'utente è l'utente corrente avremo bisogno della password corrente per dimostrarci (anche se non necessariamente ricevere la password corrente, se ad esempio una sfida basata sull'hash è stato usato per dimostrarne la conoscenza senza inviarlo).

L'URI dovrebbe quindi essere qualcosa di simile <http://example.net/changeCurrentUserPassword>o <http://example.net/users/joe/changePassword>.

Potremmo decidere di voler ricevere la password corrente nei dati POST e nel meccanismo generale di autorizzazione utilizzato.

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.