Come posso assicurarmi che l'hashing della password sia sicuro sui computer senza rallentare eccessivamente sui dispositivi mobili?

15

Sono nuovo della crittografia e delle sue implementazioni. Sto progettando un'app per Android in cui un utente immette una password per recuperare alcuni dati crittografati. Dopo alcune ricerche sulle possibili soluzioni ho finito con bcrypt usato in questo modo:

User enter Password -> bcrypt(Password) -> key for encryption

E poi memorizzarlo nel database in questo modo:

SHA256(bcrypt(password)) -> user check (this is stored in DB)

Avevo intenzione di utilizzare l'implementazione di jBCrypt in Android, ma da quello che ho letto bcrypt è estremamente lento sui telefoni cellulari ( per minuti a seconda del modello del telefono / forza selezionata .)

So che rallentare la procedura di hash è il nucleo dell'algoritmo di bcrypt, ma non è accettabile che un utente richieda 2 minuti per accedere / decrittografare su un dispositivo mobile, mentre la stessa operazione su un normale PC richiede pochi secondi. Tuttavia, se si riduce il conteggio delle iterazioni sullo smartphone, un utente malintenzionato può eseguire la bruteforce su un PC più veloce. Come posso riconciliare questi due aspetti del problema?

Modifica

Alla fine ho deciso di provare jBCrypt su Android, e l'ho provato su diversi telefoni cellulari che ho con diverse versioni SO. L'ho anche testato sul mio PC di casa e ho compilato una tabella di risultati:

Sembra che un fattore di lavoro di 10-11 sia accettabile pur rimanendo ragionevolmente sicuro. Cercherò di implementare una soluzione NDK per vedere se posso migliorare i risultati. (Pubblicherò i risultati se finirò per fare i test.)

    
posta nsL 11.09.2013 - 15:06
fonte

4 risposte

10

jBcrypt è un'implementazione Java. Le app Android sono scritte in quasi-Java, per una macchina virtuale quasi-Java chiamata Dalvik .

Per le attività di elaborazione intensiva, in particolare algoritmi crittografici, Java comporta un tipico rallentamento 3x rispetto al codice C ottimizzato equivalente: una buona macchina virtuale Java esegue un Compilatore JIT , e le prestazioni del codice saranno limitate dai vincoli su cui una JVM deve operare (la conversione da bytecode a codice nativo deve essere veloce e incrementale, i metodi vengono tradotti in base alla chiamata) e il risultato deve essere ancora appropriato per il garbage collector ). Un ulteriore rallentamento può essere osservato in alcuni casi specifici in cui il codice nativo può trarre vantaggio da opcode aggiuntivi forniti dalla CPU, il caso tipico è rappresentato da calcoli su interi grandi (ad esempio, per RSA), poiché la CPU può offrire una moltiplicazione "estesa" (64x64- > 128) che il codice Java non può utilizzare, a causa della mancanza di un tipo intero a 128 bit in Java.

Per un esperimento piuttosto approfondito nel contesto delle funzioni hash (non "hashing password", solo "hashing"), vedi sphlib : una libreria che implementa molte funzioni hash, in C e in Java, con "sforzi simili alle ottimizzazioni" (lo stesso sviluppatore per l'intero lotto) e include misure su una varietà di tipi di piattaforma .

Ora, ci possono essere ulteriori rallentamenti per le app Android:

  • La CPU di uno smartphone non è veloce come quella di un PC. La CPU per smartphone può andare all'intervallo di gigahertz, ma fare comunque meno lavoro per ciclo rispetto alla CPU x86 - perché uno degli obiettivi principali della CPU di uno smartphone è quello di risparmiare la batteria. Uno smartphone deve essere in vita per un giorno intero, mentre si sta impegnando in attività radio, mentre un portatile può avere una batteria più voluminosa e ha solo bisogno di essere in piedi per alcune ore. Gli smartphone moderni sono efficienti, ma c'è ancora un gap rispetto al PC desktop. Diciamo un ulteriore fattore 3x.

  • Dalvik non è necessariamente il miglior compilatore JIT in circolazione. La relativa mancanza di muscoli grezzi significa che il JIT non può applicare le strategie di ottimizzazione più complesse, perché renderebbe la traduzione in JIT di un metodo troppo costoso.

  • Fino a Android 2.1 (incluso), Dalvik non ha JIT . Questo è il peggior rallentamento, a cui altre risposte alludono. In Android 2.1 (e prima), il bytecode Java è interpretato, non tradotto in codice nativo, e ciò comporta un costo extra pesante (diciamo da 10 a 20 volte più lento del codice JIT). Android 2.2 in poi ha JIT.

Passare a PBKDF2 non cambierà molto l'immagine. Se usi PBKDF2 , assicurati di usare SHA-256 (o SHA-1), non SHA-512, perché il la CPU dello smartphone è basata su ARM e sarà piuttosto scomoda con operazioni aritmetiche a 64 bit su cui si basa pesantemente l'SHA-512, rallentando così il codice - laddove l'hacker ha un PC, che mangia operazioni a 64 bit per la colazione, e non subire lo stesso rallentamento.

Riepilogo: bcrypt sarà molto lento su Android 2.1 (e versioni precedenti). Sarà piuttosto veloce su Android 2.2, anche se non così veloce come un PC (diciamo, tra 5 e 10 volte più lento). PBKDF2, scrypt ... non modificare questa immagine in modo significativo.

Tutto questo è detto sulle prestazioni di bcrypt. Non ho fatto commenti in alcun modo sull'uso corretto dei segreti derivati da bcrypt per la verifica della password di e di crittografia. In effetti, quando si memorizza "qualcosa" per convalidare una password, non si intende quel "qualcosa" troppo vicino alla chiave derivata dalla password che si utilizza per la crittografia.

Un "metodo crittograficamente ragionevole" consiste nel calcolare l'hash della password con bcrypt o PBKDF2 (con sali e iterazioni e tutto), quindi prendere l'output e espanderlo con un Key Derivation Function in abbastanza materiale chiave per le tue esigenze. Nel tuo contesto, prendi l'output bcrypt, quindi esegui l'hash con SHA-256. Questo produce 256 bit. Memorizza i primi 128 bit nel tuo database come "token di verifica password". Utilizza la altra metà come chiave di crittografia.

PBKDF2 rende questo processo un po 'più semplice dal momento che è un KDF, quindi puoi richiedere subito 256 bit di output derivato da password, senza bisogno di un hashing aggiuntivo; mentre la dimensione dell'output di bcrypt è fissata a 192 bit, il che potrebbe non essere sufficiente per le tue esigenze.

    
risposta data 11.09.2013 - 15:44
fonte
1

Sono stato nella stessa situazione. Su Android dovresti essere in grado di utilizzare PBKDF2 comodamente. Non ho provato lo scrypt su un dispositivo mobile, ma mi aspetto che le prestazioni non siano migliori di scrypt durante la generazione di un hash. Oltre a questo, invece di prendere un SHA-256 potresti facilmente utilizzare PBKDF2 con SHA-256 HMAC . Ricorda che PBKDF2 ti permette anche di impostare la lunghezza dell'output dell'hash (preferibilmente usi HKDF-Extract sull'output di PBKDF2 invece ). Quindi normalmente prendere SHA-256(pbkdf2(pass)) sarà troppo.

Consiglierei comunque di prendere un'altra PBKDF2(PKBDF2(pass)) e di memorizzarla anche nella tua applicazione. In questo modo puoi verificare che, quando l'utente immette una password e tu usi PBKDF2 per generare la chiave (inner PBKDF2 ) che la chiave sia effettivamente la chiave corretta prima di decodificare.

  • password = > usato per generare la chiave (segreto, non dovrebbe essere memorizzato)
  • PBKDF2(password) = > usato come chiave per decrittografare i tuoi dati crittografati (segreto, non dovrebbe essere archiviato)
  • PBKDF2(PBKDF2(password)) = > usato per verificare la chiave prima di tentare la decodifica (non segreta, può essere memorizzata all'interno dell'applicazione)
risposta data 11.09.2013 - 15:25
fonte
1

È lo stesso dilemma quando si configura un gestore di password come KeePass su più piattaforme - su quale piattaforma ti concentri?

Poiché bcrypt è una funzione di derivazione chiave utilizzata per estendere il processo altrimenti rapido di passaggio attraverso password umane prevedibili; se affronti il problema delle password umane, affronti il problema delle prestazioni.

Avere accesso ai dati governati da una chiave di sessione. Quando la chiave di sessione deve essere rigenerata (ad esempio, ogni 24 ore o dopo il riavvio del telefono) viene utilizzato bcrypt(password) o bcrypt(password + master key) . Durante una sessione, la chiave di sessione è protetta solo dalla crittografia della password non crittografata per la velocità.

In alternativa puoi lasciare il bcrypt() e il ritardo associato sul posto e migliorare semplicemente l'esperienza dell'interfaccia utente per il processo.

Se il processo di decrittografia viene eseguito come attività in background con CPU con una notifica in primo piano che contrassegna lo stato di decrittazione con la barra di avanzamento e la percentuale; la maggior parte degli utenti troverà ciò accettabile se lo scopo principale dell'applicazione è la sicurezza. In effetti, potrebbe persino far sì che l'applicazione sia più sicura rispetto a una decifratura superveloce.

Naturalmente, se lo scopo principale dell'applicazione non è legato alla sicurezza, potresti voler rivisitare se ha davvero bisogno del miglioramento della sicurezza di bcrypt() e del ritardo associato al dispositivo mobile.

    
risposta data 11.09.2013 - 15:38
fonte
0

Come riconosci tu, hai requisiti contraddittori. Desiderate che bcrypt sia il più costoso possibile per aiutare a difendersi dai cracker off-line, mentre allo stesso tempo volete che non sia eccessivamente costoso per l'utente legittimo su un dispositivo a bassa potenza.

Ci sono due tipi di approcci, entrambi i quali dovresti perseguire. Ottimizza il tuo fattore di costo (o le iterazioni) e trova un'implementazione di hashing che ti offre il miglior rapporto di attaccante / difensore (il più piccolo).

Riduci il rapporto Attaccante / Difensore

Il password cracker, Bitweisel, parla di quello che lui chiama "Rapporto Attaccante / Difensore" (ADR) . Quando il Defender è bloccato eseguendo bcrypt, scrypt o PBKDF2 in un modo che è, ad esempio, 1/10 della velocità di ciò che l'attaccante ha a disposizione, quindi l'ADR è 10 a 1. Nella tua situazione, il Defender non è solo su un dispositivo meno potente, potrebbe anche rimanere bloccato con i compilatori che non funzionano bene su questi sistemi.

Non so abbastanza bene su Android per indirizzarti nella giusta direzione, ma dovresti cercare di trovare qualcosa attentamente ottimizzato per il suo ambiente operativo. Non so se sarà bcrypt o PBKDF2. Di nuovo, non sei preoccupato (ancora) della velocità assoluta, ma del rapporto tra il tuo ambiente e quello dell'attaccante.

Ad esempio, il PBKDF2 che utilizza HMAC-SHA512 è estremamente lento per te, ma potrebbe valerne la pena se l'uso di SHA512 preclude la capacità dell'aggressore di utilizzare le GPU per il crack. Quindi, anche se lento, questo potrebbe effettivamente farti perdere l'ADR.

Installa una copia di ocl-hashcat + (il cracker a pagamento là fuori) per testare i tassi di cracking sul tuo computer desktop in modo che Avrai quel lato dell'equazione per giudicare l'ADR per diversi schemi di hashing.

Ottimizza il tuo fattore di costo

PBKDF2, bcrypt e scrypt all consentono di impostare i fattori di lavoro. L'istruzione "bcrypt dura due minuti" è risolvibile riducendo il parametro di costo di uno (per farlo richiede un minuto) o di due (per 30 secondi). Con PBKDF2 imposti l'iterazione, che scala linearmente.

Quindi, una volta trovato l'algoritmo e l'implementazione che ti danno il miglior ADR, allora decidi semplicemente a che ora gli utenti tollereranno. Se sono 2 secondi, quindi aggiusta il costo o le iterazioni per ottenere quei due minuti.

    
risposta data 12.09.2013 - 07:39
fonte

Leggi altre domande sui tag