Reimpostazione della password RESTful


107

Qual è il modo corretto per strutturare una risorsa RESTful per reimpostare una password?

Questa risorsa ha lo scopo di reimpostare la password per qualcuno che ha perso o dimenticato la propria password. Invalida la vecchia password e invia loro una password tramite posta elettronica.

Le due opzioni che ho sono:

POST /reset_password/{user_name}

o...

POST /reset_password
   -Username passed through request body

Sono abbastanza sicuro che la richiesta dovrebbe essere un POST. Sono meno sicuro di aver selezionato un nome appropriato. E non sono sicuro che user_name debba essere passato attraverso l'URL o il corpo della richiesta.

Risposte:


54

AGGIORNAMENTO: (oltre a commentare sotto)

Vorrei qualcosa del genere:

POST /users/:user_id/reset_password

Hai una raccolta di utenti, in cui il singolo utente è specificato dal {user_name}. Dovresti quindi specificare l'azione su cui operare, che in questo caso è reset_password. È come dire "Crea ( POST) una nuova reset_passwordazione per {user_name}".


Risposta precedente:

Vorrei qualcosa del genere:

PUT /users/:user_id/attributes/password
    -- The "current password" and the "new password" passed through the body

Avresti due raccolte, una raccolta di utenti e una raccolta di attributi per ogni utente. L'utente è specificato da :user_ide l'attributo è specificato da password. L' PUToperazione aggiorna il membro indirizzato della raccolta.


10
Sono d'accordo con la tua soluzione aggiornata (POST). Le richieste PUT dovrebbero essere idempotenti (cioè le richieste ripetute non dovrebbero influenzare il risultato). Questo non è il caso delle richieste POST.
Ross McFarlane

16
Vorrei cambiare reset_password in password_reset
Richard Knop

9
Aspetta ragazzi ... questo essenzialmente non permetterebbe a CHIUNQUE di reimpostare la password di qualcuno? Come, se questo è per qualcuno che dimentica la password corrente, l'utente interessato non può essere autenticato con la password corrente. Quindi, in sostanza, questo significa che questa API non può accettare alcuna password, consentendo così a chiunque di reimpostare la password di qualcuno e, se l'API la restituisce, persino ottenere la password di qualsiasi utente conosciuto ??? O mi sto perdendo qualcosa
transient_loop

39
Il problema con / user / {id} / password e simili è che potresti non conoscere l '"id" dell'utente. Dovresti conoscere il loro "nome utente" o "email" o "telefono", ma non l '"id".
coolaj86

17
Il difetto fondamentale di questo approccio è che si presume che tu conosca già l'ID utente. Ciò sarà vero in alcune circostanze, ma come si fa quando il nome utente o l'ID utente non è noto se l'utente deve solo specificare un'e-mail per il ripristino.
Alappin

94

Utenti non autenticati

Facciamo una PUTrichiesta su un api/v1/account/passwordendpoint e richiediamo un parametro con l'e-mail dell'account corrispondente per identificare l'account per il quale l'utente desidera reimpostare (aggiornare) la password:

PUT : /api/v1/account/password?email={email@example.com}

Nota: come @DougDomeny ha menzionato nel suo commento, passare l'e-mail come stringa di query nell'URL è un rischio per la sicurezza. I parametri GET non sono esposti durante l'uso https(e dovresti sempre usare una httpsconnessione appropriata per tali richieste) ma ci sono altri rischi per la sicurezza coinvolti. Puoi leggere di più su questo argomento in questo post del blog qui .

Il passaggio dell'email nel corpo della richiesta sarebbe un'alternativa più sicura al passarlo come parametro GET:

PUT : /api/v1/account/password

Corpo della richiesta:

{
    "email": "email@example.com"
}

La risposta ha un significato di risposta 202accettata :

La richiesta è stata accettata per l'elaborazione, ma l'elaborazione non è stata completata. La richiesta potrebbe o meno essere eseguita alla fine, in quanto potrebbe non essere consentita quando l'elaborazione ha effettivamente luogo. Non è possibile inviare nuovamente un codice di stato da un'operazione asincrona come questa.

L'utente riceverà un'e-mail all'indirizzo email@example.come l'elaborazione della richiesta di aggiornamento dipende dalle azioni intraprese con il collegamento dall'e-mail.

https://example.com/password-reset?token=1234567890

L'apertura del collegamento da questa e-mail indirizzerà a un modulo di reimpostazione della password nell'applicazione front-end che utilizza il token di reimpostazione della password dal collegamento come input per un campo di input nascosto (il token fa parte del collegamento come stringa di query). Un altro campo di input consente all'utente di impostare una nuova password. Un secondo input per confermare la nuova password verrà utilizzato per la convalida sul front-end (per evitare errori di battitura).

Nota: nell'email potremmo anche menzionare che nel caso in cui l'utente non abbia inizializzato alcuna reimpostazione della password può ignorare l'email e continuare a utilizzare l'applicazione normalmente con la sua password attuale

Quando il modulo viene inviato con la nuova password e il token come input, avrà luogo il processo di reimpostazione della password. I dati del modulo verranno inviati PUTnuovamente con una richiesta ma questa volta includendo il token e sostituiremo la password della risorsa con un nuovo valore:

PUT : /api/v1/account/password

Corpo della richiesta:

{
    "token":"1234567890",
    "new":"password"
}

La risposta sarà una risposta 204senza contenuto

Il server ha soddisfatto la richiesta ma non ha bisogno di restituire un corpo dell'entità e potrebbe voler restituire le metainformazioni aggiornate. La risposta PU includere metainformazioni nuove o aggiornate sotto forma di intestazioni di entità, che se presenti DOVREBBERO essere associate alla variante richiesta.

Utenti autenticati

Per gli utenti autenticati che vogliono cambiare la propria password la PUTrichiesta può essere eseguita immediatamente senza l'email (l'account di cui stiamo aggiornando la password è noto al server). In tal caso il modulo presenterà due campi:

PUT : /api/v1/account/password

Corpo della richiesta:

{
    "old":"password",
    "new":"password"
}

Nel tuo primo paragrafo dici PUT ma l'esempio sotto dice DELETE. Quale è accurato?
jpierson

Questo espone l'indirizzo e-mail sull'URL, che sarebbe un dato JSON più sicuro.
Doug Domeny

@DougDomeny Sì, inviare l'email come dati JSON sarebbe probabilmente meglio. L'ho aggiunto alla risposta come opzione alternativa più sicura, altrimenti la soluzione può essere la stessa.
Wilt

@Wilt: non sarebbe un'operazione PATCH? PUT richiede di inviare la risorsa completa
j10

1
@jitenshah Buon punto. Quando ho scritto questo ho pensato che PUT sarebbe stato meglio, ma non ricordo esattamente perché. Sono d'accordo con il tuo ragionamento che la patch potrebbe essere più adatta a questo caso.
Wilt

18

Diventiamo super-RESTful per un secondo. Perché non utilizzare l'azione DELETE per la password per attivare un ripristino? Ha senso, non è vero? Dopotutto, stai effettivamente scartando la password esistente a favore di un'altra.

Ciò significa che faresti:

DELETE /users/{user_name}/password

Ora, due grandi avvertimenti:

  1. HTTP DELETE dovrebbe essere idempotente (una parola di fantasia per dire "non è un grosso problema se lo fai più volte"). Se stai facendo le cose standard come l'invio di un'e-mail di "reimpostazione della password", allora incorrerai in problemi. È possibile aggirare questo problema contrassegnando l'utente / password con un flag booleano "Is Reset". Ad ogni cancellazione, controlli questo flag; se non è impostata , allora si può reimpostare la password e invia la tua email. (Nota che avere questo flag potrebbe avere anche altri usi.)

  2. Non puoi utilizzare HTTP DELETE tramite un modulo , quindi dovrai effettuare una chiamata AJAX e / o eseguire il tunneling DELETE attraverso il POST.


9
Idea interessante. Tuttavia non vedo che mi DELETEstia bene qui. Dovresti sostituire la password con una generata casualmente, immagino, quindi DELETEpotrebbe essere fuorviante. Preferisco il Create (POST) new reset_password action, dove il nome (risorsa) su cui agiresti è "azione reset_password". Questo si adatta bene anche per l'invio di e-mail, poiché l'azione incapsula tutti questi dettagli di livello inferiore. POSTnon è idempotente.
Daniel Vassallo

Mi piace la proposta. Il problema 1 potrebbe essere risolto utilizzando richieste condizionali, ovvero HEAD che invia ETag + DELETE e If-Match header. Se qualcuno tenta di eliminare una password non più attiva, otterrà un 412.
whiskeysierra

1
Eviterei DELETE. Stai aggiornando, poiché la stessa entità / concetto riceverà un nuovo valore. Ma in realtà, di solito non succede nemmeno ora, ma solo dopo aver inviato la nuova password in una successiva richiesta diversa (dopo una mail di reimpostazione della password) - Al giorno d'oggi nessuno invia una nuova password per posta, ma un token per reimpostarla in una nuova richiesta con un dato gettone, giusto?
antonio.fornie

11
Cosa succede se l'utente ricorda la sua password subito dopo aver effettuato una richiesta di ripristino? Che ne dici di un bot che tenta di reimpostare account casuali? L'utente dovrebbe essere autorizzato a ignorare l'e-mail di ripristino in tal caso (l'email dovrebbe dirlo), il che significa che il tuo sistema non dovrebbe eliminare o aggiornare le password da solo.
Maxime Laval,

3
@ MaximeLaval Questo è un ottimo punto. In realtà, stai "creando una richiesta di ripristino", che sarebbe un POST.
Craig Walker,

12

Spesso non si desidera eliminare o distruggere la password esistente dell'utente nella richiesta iniziale, poiché ciò potrebbe essere stato attivato (involontariamente o intenzionalmente) da un utente che non ha accesso all'email. Invece, aggiorna un token di reimpostazione della password sul record utente e invialo in un collegamento incluso in un'e-mail. Fare clic sul collegamento confermerebbe che l'utente ha ricevuto il token e desidera aggiornare la propria password. Idealmente, anche questo sarebbe sensibile al tempo.

L'azione RESTful in questo caso sarebbe un POST: l'attivazione dell'azione di creazione sul controller PasswordResets. L'azione stessa aggiorna il token e invia un'e-mail.


9

In realtà sto cercando una risposta, non intendo fornirne una, ma "reset_password" mi sembra sbagliato in un contesto REST perché è un verbo, non un sostantivo. Anche se dici che stai facendo un nome di "azione di ripristino" - usando questa giustificazione, tutti i verbi sono nomi.

Inoltre, a qualcuno che cerca la stessa risposta potrebbe non essere venuto in mente che potresti essere in grado di ottenere il nome utente attraverso il contesto di sicurezza e non doverlo inviare tramite l'URL o il corpo, il che mi rende nervoso.


4
Forse reset-passwordsuona come un verbo, ma puoi facilmente invertirlo ( password-reset) per renderlo un sostantivo. E se hai modellato la tua applicazione utilizzando Event Sourcing o anche se hai solo un qualsiasi tipo di controllo, è logico che tu abbia effettivamente un'entità reale con questo nome e potresti persino consentire a GET su di essa per utenti o amministratori di vedere history (ovviamente mascherando il testo della password). Non mi rende affatto nervoso. E per quanto riguarda la raccolta automatica del nome utente sul lato server, puoi, ma allora come gestisci cose come amministrazione / rappresentazione?
Aaronaught

1
Non c'è niente di sbagliato nell'usare il verbo in REST. Fintanto che viene utilizzato in luoghi appropriati. Penso che questo sia più un controller che una risorsa, e reset-passwordriesco a descrivere bene i suoi effetti.
Anders Östman

6

Penso che un'idea migliore sarebbe:

DELETE /api/v1/account/password    - To reset the current password (in case user forget the password)
POST   /api/v1/account/password    - To create new password (if user has reset the password)
PUT    /api/v1/account/{userId}/password    - To update the password (if user knows is old password and new password)

Per quanto riguarda la fornitura dei dati:

  • Per ripristinare la password corrente

    • l'email dovrebbe essere inserita nel corpo.
  • Per creare una nuova password (dopo il ripristino)

    • la nuova password, il codice di attivazione e l'ID e-mail devono essere inseriti nel corpo.
  • Per aggiornare la password (per utente connesso)

    • vecchia password, la nuova password dovrebbe essere menzionata nel corpo.
    • UserId in Params.
    • Token di autenticazione nelle intestazioni.

2
Come commentato in altre risposte, "DELETE / api / v1 / account / password" è una cattiva idea, poiché chiunque potrebbe reimpostare la password di chiunque.
Maximedupre

Abbiamo bisogno di un ID e-mail registrato per reimpostare la password. Le possibilità di conoscere l'ID e-mail di un utente sconosciuto sono molto scarse, a meno che non gestiamo un sito come Facebook e non abbiamo tonnellate di ID e-mail raccolti con qualsiasi mezzo. Quindi le politiche di sicurezza verranno definite di conseguenza. Qual è il tuo suggerimento per reimpostare la password di qualcuno?
codesnooker

5

Ci sono alcune considerazioni da fare:

Le reimpostazioni della password non sono idempotenti

Una modifica della password influisce sui dati utilizzati come credenziali per eseguirla, che di conseguenza potrebbe invalidare i tentativi futuri se la richiesta viene semplicemente ripetuta alla lettera mentre le credenziali memorizzate sono cambiate. Ad esempio, se un token di ripristino temporaneo viene utilizzato per consentire la modifica, come è consuetudine in una situazione di password dimenticata, tale token dovrebbe scadere in caso di modifica della password riuscita, il che annulla nuovamente ulteriori tentativi di replica della richiesta. Quindi un approccio RESTful alla modifica della password sembra essere un lavoro più adatto POSTdiPUT .

L'ID o l'e-mail nel caricamento dei dati è probabilmente ridondante

Sebbene non sia contro REST e possa avere uno scopo speciale, spesso non è necessario specificare un ID o un indirizzo e-mail per reimpostare la password. Pensaci, perché dovresti fornire l'indirizzo email come parte dei dati a una richiesta che dovrebbe passare attraverso l'autenticazione in un modo o nell'altro? Se l'utente sta semplicemente cambiando la propria password, deve autenticarsi per farlo (tramite nome utente: password, email: password o token di accesso fornito tramite intestazioni). Quindi, abbiamo accesso al loro account da quel passaggio. Se avessero dimenticato la password, gli sarebbe stato fornito un token di ripristino temporaneo (tramite e-mail) che possono utilizzare specificamente come credenziali per eseguire la modifica. E in questo caso l'autenticazione tramite token dovrebbe essere sufficiente per identificare il proprio account.

Prendendo in considerazione tutto quanto sopra, ecco quello che credo sia lo schema corretto per una modifica della password RESTful:

Method: POST
url: /v1/account/password
Access Token (via headers): pwd_rst_token_b3xSw4hR8nKWE1d4iE2s7JawT8bCMsT1EvUQ94aI
data load: {"password": "This 1s My very New Passw0rd"}

Un'affermazione secondo cui un segnaposto richiede informazioni fuori banda non è completamente vera. Un tipo di supporto speciale può descrivere la sintassi e la semantica di determinati elementi di una richiesta o risposta. È quindi possibile per un tipo di media definire che un URI contenuto in un determinato campo può definire un segnaposto per determinati dati e la semantica definisce ulteriormente che un'e-mail utente codificata o cosa non dovrebbe essere inclusa al posto del segnaposto. I client e i server che rispettano quel tipo di supporto saranno comunque conformi ai principi dell'architettura RESTful.
Roman Vottner

1
Per quanto riguarda la POSTvs. PUT RFC 7231 specifica che un aggiornamento parziale può essere ottenuto attraverso la sovrapposizione di dati di due risorse, ma è discutibile se qualcosa di simile /v1/account/passwordeffettivamente compensa una buona risorsa. Come POSTlo Swiss-Army-Kniff del Web che può essere utilizzato se nessuno degli altri metodi è fattibile, considerare PATCHpotrebbe anche essere una scelta per impostare la nuova password.
Roman Vottner

che dire dell'URL per richiedere la reimpostazione della password quando non conoscono la propria password?
dan carter

2

Non avrei qualcosa che modifichi la password e ne invii una nuova se decidi di utilizzare il metodo / users / {id} / password e ti attieni alla tua idea che la richiesta è una risorsa a sé stante. cioè / user-password-request / è la risorsa, ed è use PUT, le informazioni utente dovrebbero essere nel corpo. Tuttavia, non cambierei la password, mando un'e-mail all'utente che contiene un collegamento a una pagina che contiene un request_guid, che potrebbe essere passato insieme a una richiesta a POST / user / {id} / password /? Request_guid = xxxxx

Ciò cambierebbe la password e non consente a qualcuno di ingannare un utente richiedendo una modifica della password.

Inoltre, il PUT iniziale potrebbe non riuscire se c'è una richiesta in sospeso.


0

Aggiorniamo la password dell'utente registrato PUT / v1 / users / password: identifica l'ID utente utilizzando AccessToken.

Non è sicuro scambiare l'ID utente. L'API Restful deve identificare l'utente utilizzando AccessToken ricevuto nell'intestazione HTTP.

Esempio in spring-boot

@putMapping(value="/v1/users/password")
public ResponseEntity<String> updatePassword(@RequestHeader(value="Authorization") String token){
/* find User Using token */
/* Update Password*?
/* Return 200 */
}
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.