La migliore soluzione per autorizzare che un utente sia autorizzato a modificare / agire con le proprie risorse in un'API REST

8

Sfondo:

Attualmente è in fase di creazione di un'API REST, utilizzando il nodo w / express e viene utilizzato da un'app mobile e infine da un sito Web (basato su browser moderni).

Sto cercando di identificare il modo migliore per autorizzare la richiesta di aggiornamento / azione di un utente in modo che possano modificare solo le proprie risorse. Le azioni si verificheranno a un tasso quasi elevato, quindi la preoccupazione.

Nota: la proprietà delle entità non può essere trasferita in questo caso d'uso.

Soluzioni potenziali:

Soluzione : Archivia e mantieni un elenco delle risorse di ciascun utente in una sessione del server supportato da qualcosa come reddis.

Concern (s) : La persistenza di una sessione lato server ha il proprio insieme di complessità che si adatta in modo specifico a più server. Ciò viola anche il REST. do-session-really-violate-restfulness per ulteriori informazioni.

Soluzione: Esegui una query di lettura prima della query di aggiornamento / azione dell'utente. IE mi danno gli articoli di questo utente, se poi nella sua lista procedere con l'aggiornamento.

Concern (s): Sovraccarico di una lettura aggiuntiva ogni volta che un utente agisce per conto di una risorsa.

Soluzione: Passa l'id utente fino al livello db e rendilo parte del condizionamento dell'aggiornamento o se vuoi avere fantasia usa qualcosa come la sicurezza a livello di riga di Postgres per quella risorsa a seconda del backend dei dati.

Concern (s): Questo sembra un po 'in ritardo nel ciclo di vita della richiesta per verificare se la risorsa è l'utente richiedente. L'errore dovrebbe essere sollevato dal backend dei dati. Sulla stessa nota sarebbe anche un po 'fuori luogo considerando che l'autenticazione e l'autorizzazione basata sui ruoli è spesso fatta all'inizio del ciclo di vita della richiesta. L'implementazione dipende anche dal backend dei dati. Spinge anche la logica aziendale nel backend dei dati.

Soluzione: Sessione firmata lato client di sorta. O con un JWT o un cookie crittografato / firmato. Essenzialmente mantenendo una sessione fidata contenente un elenco degli ID delle risorse dell'utente.

Concern (s): La dimensione della sessione lato client. Il fatto che sarebbe inviato con ogni singola richiesta anche quando non è necessario. La manutenzione diventa estremamente complessa quando si introduce la possibilità di più sessioni / client attivi. Come aggiorneresti lo stato lato client quando una risorsa è stata aggiunta su un altro client.

Soluzione: Passa un token di aggiornamento firmato (JWT) o url al client con la risorsa quando la risorsa viene recuperata. Aspettatevi quando una risorsa viene aggiornata / attivata. Il token firmato conterrebbe l'id utente e l'ID risorsa e potresti facilmente verificarlo.

Concern (s): Diventa complesso se la proprietà delle risorse è trasferibile, ma nel mio caso non è una preoccupazione. Introduce la complessità rispetto a una lettura prima dell'aggiornamento. È un po 'strano?

Considerazioni finali:

Mi sto appoggiando all'ultima soluzione, ma poiché non vedo accadere molto spesso mi chiedo se mi manca qualcosa? o forse fa parte di un modello di progettazione che non conosco.

    
posta Ashtonian 29.03.2016 - 20:23
fonte

3 risposte

1

Penso che ci siano due soluzioni nella tua lista che si adattino meglio al tuo caso d'uso; la query di lettura prima dell'aggiornamento (soluzione 2) e l'invio di un token di aggiornamento con la richiesta di lettura (soluzione 5).

Se sei preoccupato per le prestazioni, sceglierei l'una o l'altra in base al numero di letture rispetto agli aggiornamenti di una risorsa che ti aspetti. Se ti aspetti più aggiornamenti rispetto alle letture, ovviamente la soluzione 5 è migliore, perché non è necessario eseguire un recupero di database aggiuntivo per capire il proprietario della risorsa.

Tuttavia, non dovresti dimenticare le implicazioni sulla sicurezza di distribuire un token di aggiornamento. Con la soluzione 2, supponendo che l'autenticazione sia sicura, l'aggiornamento di una risorsa è probabilmente anche sicuro perché si determina il proprietario di una risorsa sul server.

Con la soluzione 5, non ricontrolla il reclamo fatto dal cliente, tranne che controlli la presenza di una firma valida. Se stai lasciando che l'aggiornamento si verifichi tramite un collegamento in chiaro (ad esempio, nessun SSL), codifica solo la risorsa e l'id utente nel token non è sicuro. Per esempio, ti stai aprendo per replicare gli attacchi, quindi dovresti includere un nonce che cresce ad ogni richiesta. Inoltre, se non si codifica una data / ora / data di scadenza nel token di aggiornamento, si concede fondamentalmente un accesso di aggiornamento indefinito a chiunque abbia quel token. Infine, se non si desidera il nonce, è necessario includere almeno un hmac della risorsa recuperata, in modo che il token di aggiornamento sia valido solo per lo stato della risorsa durante il recupero. Ciò rende più difficili gli attacchi di riproduzione e limita ulteriormente la conoscenza dei danni del token di aggiornamento, poiché il token di aggiornamento è valido solo quando la risorsa è nello stato dato.

Penso che anche se comunichi tramite un collegamento sicuro, aggiungere un nonce (o almeno un hmac dello stato della risorsa) e una data di scadenza per il token di aggiornamento sarebbe una cosa intelligente da fare. Non conosco il dominio dell'applicazione, ma potreste avere a che fare con utenti malintenzionati e potrebbero distruggere tutti i tipi di devastazione con il potere di accesso illimitato agli aggiornamenti alle proprie risorse.

L'aggiunta di un nonce significherà una colonna aggiuntiva per risorsa nel tuo database in cui memorizzi il valore del nonce che hai inviato con l'ultima richiesta di recupero. Puoi calcolare hmac sulla rappresentazione serializzata json della tua risorsa. Puoi quindi inviare il tuo token non come parte del corpo del messaggio ma come un'intestazione HTTP aggiuntiva.

Oh, e userei un token, non un URL; in REST, l'URL di aggiornamento per una determinata risorsa dovrebbe essere lo stesso URL da cui è stata prelevata la risorsa, giusto? Sposterei tutte le autenticazioni & roba relativa all'autorizzazione alle intestazioni HTTP, ad es. potresti fornire il token di aggiornamento nell'intestazione di autorizzazione della richiesta PUT.

Si noti che l'aggiunta di un nonce che cresce con ogni recupero di risorse richiederà anche un aggiornamento del database (il nuovo valore per il nonce) per ogni richiesta di recupero delle risorse (sebbene si possa ottenere solo l'aggiornamento del nonce quando lo stato della risorsa è in realtà cambiato, quindi sarebbe esente da un punto di vista delle prestazioni), a meno che non si voglia conservare tali informazioni nella memoria temporanea e semplicemente fare in modo che i client riprovino quando il server viene riavviato tra un recupero e una richiesta di aggiornamento.

Una nota a margine della soluzione 4 (sessioni lato client): in base al numero di risorse che gli utenti possono creare, la dimensione della sessione potrebbe non essere un problema. Il problema di aggiornamento della sessione lato client potrebbe essere abbastanza facile da risolvere: Se una richiesta di aggiornamento a una risorsa non riesce perché la risorsa non è elencata nei dati di sessione che hai ricevuto dal tuo client, ma l'utente è corretto, allora esegui un controllare nel back-end se i dati di sessione da quel client sono obsoleti. Se lo è, si consente l'aggiornamento e si restituisce un cookie aggiornato con la risposta alla richiesta di aggiornamento. Ciò causerà solo una ricerca nel database quando un utente tenta di aggiornare una risorsa da un client con dati di sessione locali obsoleti (o quando è malevolo e prova a ddos, ma limita la velocità al salvataggio!). Tuttavia, sono d'accordo che la soluzione 4 è più complicata delle altre e tu stai meglio con una delle tue altre idee. La soluzione 4 ha anche varie considerazioni sulla sicurezza da tenere in considerazione; la memorizzazione di questo stato di autorizzazione sul client ha davvero bisogno della sicurezza per essere impermeabile.

    
risposta data 01.04.2016 - 14:34
fonte
0

Un'altra soluzione che potresti prendere in considerazione:

Se una risorsa non può essere realmente trasferita a un altro utente una volta creata, è possibile pensare a codificare l'id utente nell'ID risorsa.

es. tutte le risorse appartenenti all'utente 'alice' sono denominate 'alice-1', 'alice-2' ecc.

È quindi banale verificare se 'eve' ha accesso a una risorsa chiamata 'alice-1'.

Se non vuoi che la proprietà della risorsa sia di dominio pubblico, puoi usare una funzione di hash crittografica in questo modo:

  1. Dato una password, solo il tuo server lo sa
  2. E un nome utente u (ad esempio "alice")
  3. E un nome di risorsa r (ad es. '1')

Genera hmac(password, u | '-' | r ) , dove | è la concatenazione. Utilizzare il risultato, insieme al nome della risorsa r, come ID della risorsa. Aggiungi facoltativamente un valore salt (come per la memorizzazione delle password).

Quando una richiesta di aggiornamento proviene dall'utente 'alice' per un determinato ID risorsa, estrai r dall'ID risorsa, calcola hmac (password, 'alice' | '-' | r) e confrontalo con la parte restante di l'ID della risorsa. Se corrisponde, alice è autorizzato ad aggiornare la risorsa, se non corrisponde, non è autorizzata. E senza la password, nessuno può capire chi possiede quale risorsa.

Penso che sia sicuro, ma potresti voler rispolverare le qualità di hmacs.

    
risposta data 01.04.2016 - 15:25
fonte
0

Sembra che ci siano due problemi separati da gestire. Sono (1) Come si sta autenticando l'utente che sta generando la richiesta? e (2) come fai a sapere quali oggetti back-end sono associati a quell'utente. Ho anche un'osservazione che potrebbe essere adatta al tuo caso d'uso (3)

(1) L'autenticazione dell'utente in modo RESTful non è mai carina. Preferisco utilizzare le intestazioni HTTP Authentication / Authorization e le risposte HTTP 401 per attivare l'autenticazione, il che significa che si sta potenzialmente autenticando l'utente a ogni richiesta. Poiché si tratta di un'applicazione mobile, hai la possibilità di creare il tuo schema di autenticazione personalizzato, che ti consente di fornire un token di sessione anziché i dettagli dell'autenticazione completa nelle richieste future.

(2) L'utente associato a un insieme di oggetti è sicuramente un dato chiave che è necessario archiviare insieme agli oggetti reali nell'archivio dati. Come parte della tua logica aziendale, è necessario verificare chi è il proprietario dell'oggetto e confrontarlo con l'identità che accede all'oggetto. Lo farei esplicitamente in codice, come parte del livello aziendale che convalida la richiesta. Se gli hit del database sono un problema abbastanza grande, avrebbe senso memorizzare queste informazioni in un archivio di valori chiave, come memcache o (per volumi molto elevati, siti altamente disponibili, riak) e colpire il database solo se la cache è fredda per questa combinazione utente / oggetto.

(3) Se si dispone di un numero sufficiente di utenti e oggetti, è possibile combinare l'ID utente e oggetto nello stesso campo e utilizzare questo campo come chiave primaria del database per la tabella degli oggetti. per esempio. Il numero a 64 bit offre 24 bit di spazio per l'ID utente e 40 bit di spazio per gli ID oggetto. In questo modo, quando esegui una query per l'oggetto, puoi essere sicuro al 100% per il tuo utente.

    
risposta data 01.04.2016 - 22:57
fonte

Leggi altre domande sui tag