In cerca di feedback sulla progettazione dell'autenticazione dell'API in stile REST e dell'autenticazione a due fattori

3

Autenticazione

L'autenticazione sarà basata sulla firma. La firma verrà generata utilizzando:

HMAC_SHA256(SHA1(secret_key) + '#' + request_data + '#' + utc_timestamp)

Anche utc_timestamp verrà incluso nell'intestazione X-Timestamp o nell'URL utilizzando il parametro _timestamp . Il request_data conterrà tutti i parametri (URL e POST) e le intestazioni relative alle API. Tutti questi dati saranno in minuscolo, ordinati e uniti usando = e & .

Generalmente, l'API REST supporterà tre schemi di autenticazione:

  1. Chiavi API basate: la firma includerà la chiave segreta speciale associata alla particolare chiave API. Utilizzerà l'intestazione X-API-Key o il parametro _api_key URL e l'intestazione X-API-Signature o il parametro _signature URL.

  2. Credenziali utente in base: la firma utilizzerà la password come chiave segreta. Utilizzerà l'intestazione X-API-Signature o il parametro _signature URL e il parametro staff_name URL.

  3. Sessione in base: la firma utilizzerà la password dell'utente o un codice appositamente creato (ulteriori dettagli di seguito) ... utilizzerà l'intestazione X-Session-Signature oppure il parametro _session_signature URL e l'intestazione X-Session-ID o il parametro _session_id URL.

Tieni presente che la specifica consente di utilizzare Credenziali utente e Sessione contemporaneamente (ovvero, le intestazioni e i parametri non sono in conflitto).

Sessione

Disclaimer: Sì, lo so - le sessioni non sono RESTful, poiché sono stateful ... Tuttavia, il nostro prodotto richiede sessioni per alcune funzionalità e, per ragioni di semplicità, vogliamo mantenere una API / protocollo - quindi non sto cercando di entrare in un dibattito RESTful. Preferisco pensare che una sessione sia solo una risorsa, che devi mantenere mentre lavori con l'API.

La sessione sarà solo una risorsa: /api/v1/session .

Quindi, la procedura standard:

  1. Per creare una sessione un'applicazione client dovrà inviare una richiesta POST utilizzando l'autenticazione credenziali dello staff , che è stata descritta sopra: POST /api/v1/session&staff_name=s-andy .

  2. Il server risponderà con 201 Created e invierà l'ID di sessione nel corpo della risposta.

  3. Successivamente l'applicazione client utilizzerà questo ID di sessione e la password dello staff per accedere all'API.

Dopo aver ottenuto una richiesta con un particolare ID di sessione, il server aggiornerà l'ultima volta in cui è avvenuta l'accesso alla risorsa di sessione appropriata.

Ma i nostri utenti avranno anche un'opzione per usare l'autenticazione a due fattori. Nell'interfaccia utente dopo aver effettuato l'accesso a tali utenti verrà richiesto di inserire il codice di verifica , che arriverà ai propri dispositivi mobili. Quando ho progettato l'autenticazione ho pensato che sarebbe stato fantastico avere un "segreto" speciale invece della password dell'utente per la sessione. Poi mi sono reso conto: perché non utilizzare questo codice di verifica ?

Quindi il flusso per l'autenticazione a due fattori sarà:

  1. Un'applicazione client invia una richiesta POST utilizzando le credenziali dello staff .

  2. Il server avvia il codice di verifica generazione e consegna e restituisce 202 Accepted con l'id di sessione, ma la sessione non è ancora stata verificata.

  3. Entro 30 secondi l'applicazione client invia qualsiasi richiesta utilizzando l'id di sessione nell'intestazione X-Session-ID o nel parametro _session_id URL e il codice di verifica come segreto per generare la firma.

  4. Avendo ottenuto tale richiesta il server aggiorna la sessione facendola verificare e salva il codice di verifica (alias password unica) come chiave segreta per questa sessione.

  5. Successivamente l'applicazione client utilizzerà l' id di sessione e il codice di verifica (come chiave segreta) per accedere all'API.

  6. Quando la sessione scade e, pertanto, viene eliminata o quando l'utente elimina la sessione (cioè esegue la disconnessione), l'ID della sessione e la "password unica" diventano inutilizzabili.

Volevo utilizzare questa comunità di esperti come cassa di risonanza per questa idea di autenticazione a due fattori; riesci a vedere le insidie che non posso?

    
posta Andriy Lesyuk 14.03.2013 - 20:43
fonte

3 risposte

5

Il problema è che ciò che chiami codice di verifica è in realtà un token a due fattori. I token sono generalmente brevi (come 6-7 caratteri).

Quindi farai:

HMAC_SHA256(SHA1(TOKEN) + '#' + request_data + '#' + utc_timestamp)

E il token è troppo corto.

Inoltre, non capisco che cosa sia un session_id verificato rispetto a un% co_de non verificato.

Il problema più grande è che questo schema di autenticazione sembra inutilmente complesso. Complessità e sicurezza sono per lo più contrari.

Il modo in cui suggeriamo ai nostri clienti ( www.authy.com ) di eseguire l'autenticazione a due fattori è:

  1. L'utente invia una richiesta POST con le proprie credenziali.

  2. Il server convalida le credenziali. Se corretto, il server genera una stringa da 256 bit e la salva sul database e la restituisce al browser. Chiameremo questa stringa "otp".

  3. L'utente riceve una petizione sul suo token a due fattori.

  4. Nella richiesta POST del passaggio 3 ci saranno:

    hidden_field otp - > che torna dal server nel passaggio 2
       token a due fattori del campo di testo - > che la persona ottiene dalla sua app mobile

  5. A questo punto sul tuo server verifichi il token a due fattori e l'otp. Se sei corretto crea la sessione e resetta l'otp in modo che non possa essere riutilizzato.

risposta data 15.03.2013 - 00:37
fonte
1

Il tuo codice di verifica assomiglia molto alla verifica in due passaggi di google funziona. Se è abbastanza sicuro per gli account google, probabilmente funzionerà per te.

La grande domanda è come consegnare il codice di verifica all'utente? È il classico problema dei sistemi di crittografia a chiave simmetrica.

Inoltre, in che lingua stai implementando questo? Hai trovato soluzioni open source che fanno questo genere di cose? Ad esempio in c # ServiceStack Auth fa lo stesso genere di cose con credenziali e sessioni.

    
risposta data 15.03.2013 - 00:13
fonte
-1

È importante chiedersi se è davvero necessario utilizzare le sessioni? Cosa hai intenzione di memorizzare nella sessione? Disponi di dati contestuali del cliente (ad esempio carrello) che non rappresentano un oggetto dominio che viene mantenuto? Se è così, probabilmente un servizio RESTful non è la scelta migliore. In caso contrario, invece di memorizzare il contesto client sul server, è possibile memorizzarlo sul client?

Con questo in mente vorrei andare con l'opzione 2 (credenziali utente). Ci scusiamo per aver postato questa stessa risposta per più domande, ma un approccio di questo tipo è il seguente:

Supponiamo di avere un'app client mobile; per prima cosa iscrivere o registrare l'utente fornendo la propria email (nome utente) e la password in un modulo Web separato (non facente parte dell'app o del servizio REST). Quindi, una volta completata la registrazione, si risponde con una chiave utente (può essere una chiave segreta condivisa memorizzata nell'account utente sul server (database) per la crittografia simmetrica o una chiave pubblica per la crittografia asimmetrica in cui la chiave privata corrispondente è memorizzata nell'account utente sul server (database). Tutto questo viene fatto utilizzando un modulo Web su SSL.

Ora, quando l'utente apre l'app client, è necessario chiedere loro le credenziali che verranno inviate con ogni richiesta al servizio RESTful. Devono fornire il loro nome, password e chiave di crittografia che hanno ricevuto in precedenza. Questo deve essere fatto solo una volta. L'app fornisce quindi un header http con ogni richiesta che assomiglia a questo:

AUTENTICAZIONE > username: timestamp: crittografato {password: timestamp} / AUTHENTICATE >

Si noti che sia la password che il timestamp all'interno di {} vengono crittografati utilizzando la chiave dell'utente. Il timestamp viene aggiornato con ogni singola richiesta.

Implementa un filtro di autenticazione sul server che effettua le seguenti operazioni:

Prima controlla il timestamp e se scaduto (diciamo più vecchio di 1 secondo) invia un codice di risposta HTTP NON AUTORIZZATO. Se il timestamp è valido, cerca il nome utente nel tuo database degli account utente. Se non trovato, invia una risposta HTTP NON AUTORIZZATA. Se viene trovato il nome utente, recuperare la chiave di crittografia memorizzata per quell'utente (ricordare che questa può essere una chiave segreta condivisa o la chiave privata per la chiave pubblica dell'utente). Decifrare la {password: timestamp} crittografata. La password rifiutata deve corrispondere alla password dell'utente memorizzata nel database (la password stessa potrebbe anche essere inclusa nel database utilizzando un'altra chiave per maggiore sicurezza) e la data / ora decrittografata deve corrispondere anche al timestamp non crittografato inviato nell'intestazione AUTHENTICATE sopra. In caso contrario, inviare un codice di risposta HTTP NON AUTORIZZATO. In caso di esito positivo la richiesta è stata autenticata senza l'uso di cookie / sessioni.

Puoi anche memorizzare nella cache i dettagli dell'utente per evitare di effettuare una ricerca nel database ad ogni richiesta. Inoltre, è possibile utilizzare la stessa chiave per crittografare i dati sensibili che vengono inviati al client nella risposta e contrassegnarli in modo che il client sappia decrittografarli.

Ora, se qualcuno sta indagando e intercetta la richiesta, non sarà in grado di riutilizzarla per ottenere l'accesso perché la data / ora non sarà valida o, se aggiornerà la data / ora non crittografata per essere valida, non corrisponderà alla crittografia timestamp (dopo che il filtro di autenticazione lo ha decrittografato).

Un altro vantaggio di questo approccio rispetto all'utilizzo di una singola chiave dell'app è che ora hai il controllo completo su chi può accedere al tuo servizio inserendo una data di scadenza sull'account utente nel database (implementando in modo efficace un servizio basato su abbonamento). Questo è ottimo perché all'inizio potresti voler ottenere il maggior numero possibile di utenti con un abbonamento di prova (gratuito per esempio 1 anno) e poi bloccare l'accesso a quell'utente se non hanno pagato per estendere la data di scadenza dell'account:)

    
risposta data 10.05.2014 - 10:57
fonte

Leggi altre domande sui tag