Esistono implementazioni javascript SHA-256 generalmente considerate affidabili?


95

Sto scrivendo un login per un forum e ho bisogno di hash della password lato client in javascript prima di inviarla al server. Ho problemi a capire di quale implementazione SHA-256 posso effettivamente fidarmi. Mi aspettavo che ci fosse una sorta di script autorevole che tutti usassero, ma sto trovando un sacco di progetti diversi tutti con le proprie implementazioni.

Mi rendo conto che usare la crittografia di altre persone è sempre un atto di fede a meno che tu non sia qualificato per rivederla da solo, e che non esiste una definizione universale di "affidabile", ma questo sembra qualcosa di abbastanza comune e importante che dovrebbe esserci un qualche tipo di consenso su cosa usare. Sono solo ingenuo?

Modifica poiché emerge molto nei commenti: Sì, facciamo di nuovo un hash più rigoroso sul lato server. L'hashing lato client non è il risultato finale che salviamo nel database. L'hashing lato client è perché il client umano lo richiede. Non hanno dato una ragione specifica per cui, probabilmente a loro piace l'eccessivo.


9
Non per andare fuori tema, ma perché stai eseguendo l'hashing della password sul lato client?
Steve

5
@ddyer Nemmeno vicino. "Don't roll your own" si applica all'invenzione del proprio algoritmo, alla scrittura della propria implementazione di un algoritmo, allo sviluppo del proprio protocollo su algoritmi crittografici o praticamente a qualsiasi cosa sopra utilizzando un'astrazione di alto livello disponibile. Se pensi di essere al sicuro attenendoti a un nucleo sicuro e scrivendo solo codice collante, ti divertirai un mondo.
Stephen Touset

26
se si utilizza una password con hash senza un protocollo challenge / response, la password hash È la password ed è in realtà lo stesso che trasmettere la password in testo non crittografato.
Ddyer

26
@ddyer C'è un certo valore nel proteggere la password in chiaro dell'utente per tutti gli altri siti su cui potrebbe usarla, se non per il nostro sito in particolare. È una soluzione semplice che forse non ci aiuterà ma può potenzialmente aiutare l'utente se sbagliamo da qualche parte. E come ho detto, richiesta del cliente, non posso farci niente anche se lo volessi.
jono

4
@Anorov Sono più che disponibile a far cambiare idea :) ma in questo caso, non capisco davvero come si applichi il tuo punto. Abbiamo hash la password due volte: una volta sul lato client con un semplice SHA-256 e una volta sul lato server con qualcosa di più impegnativo. Il primo per proteggere il testo in chiaro in caso di MITM o simili, e il secondo per la protezione bruta. Anche se si dispone del database e dell'hash dell'amministratore non è possibile utilizzarli direttamente per convalidare l'accesso.
jono

Risposte:


111

Il Stanford JS Crypto Library contiene un'implementazione di SHA-256. Sebbene la crittografia in JS non sia davvero così ben controllata come altre piattaforme di implementazione, questa è almeno parzialmente sviluppata e in una certa misura sponsorizzata da Dan Boneh , che è un nome consolidato e affidabile nella crittografia e significa che il progetto ha una supervisione da parte di qualcuno che sa davvero cosa sta facendo. Il progetto è supportato anche dalla NSF .

Vale la pena sottolineare, tuttavia ...
... che se si esegue l'hash della password lato client prima di inviarla, l' hash è la password e la password originale diventa irrilevante. Un utente malintenzionato deve solo intercettare l'hash per impersonare l'utente e, se tale hash è archiviato non modificato sul server, il server memorizza la vera password (l'hash) in testo normale .

Quindi la tua sicurezza ora è peggiore perché hai deciso di aggiungere i tuoi miglioramenti a quello che in precedenza era uno schema affidabile.


31
Se si esegue nuovamente l'hash sul server, tuttavia, questa pratica è perfettamente legittima.
Nick Brunt

13
@ NickBrunt se hai hash sul server, non stai salvando la password trasmessa, ma non hai aggiunto alcuna sicurezza oltre alla trasmissione della password originale invece di trasmettere l'hash della password, poiché in entrambi i casi ciò che stai trasmettendo è il vera password.
tylerl

54
Vero, ma non è peggio che inviare una password che potrebbe essere utilizzata altrove su Internet in chiaro.
Nick Brunt

15
Sono d'accordo con @NickBrunt. È meglio se l'aggressore legge una stringa hash casuale che potrebbe adattarsi solo a questa particolare app piuttosto che la password originale che può essere utilizzata in diversi punti.
Aebsubis

12
Devo usare l'hashing lato client perché non voglio che il server veda mai la password in chiaro dell'utente. Non per una maggiore sicurezza al servizio che sto fornendo, ma nei confronti dell'utente. Non sto solo salvando l'hash salato dell'utente con un sale costante (costante per utente, non a livello globale), ma lo ripeto anche con un sale di sessione casuale ogni accesso che fornisce un po 'di sicurezza in più sulla rete contro lo sniffing. Se il server viene compromesso, il gioco finisce, ma questo è vero per tutto ciò che non è veramente p2p.
Hatagashira

49

Su https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/digest ho trovato questo frammento che utilizza il modulo js interno:

async function sha256(message) {
    // encode as UTF-8
    const msgBuffer = new TextEncoder('utf-8').encode(message);                    

    // hash the message
    const hashBuffer = await crypto.subtle.digest('SHA-256', msgBuffer);

    // convert ArrayBuffer to Array
    const hashArray = Array.from(new Uint8Array(hashBuffer));

    // convert bytes to hex string                  
    const hashHex = hashArray.map(b => ('00' + b.toString(16)).slice(-2)).join('');
    return hashHex;
}

Nota che è crypto.subtledisponibile solo suhttps o localhost- ad esempio per il tuo sviluppo locale con python3 -m http.serverdevi aggiungere questa riga al tuo /etc/hosts: 0.0.0.0 localhost

Riavvia e puoi aprirlo localhost:8000lavorando crypto.subtle.


2
Questo e spettacolare. Grazie. È disponibile un'API comparabile in Node.js?
Con Antonakos

2
La libreria 'crypto' incorporata dovrebbe andare bene: nodejs.org/dist/latest-v11.x/docs/api/crypto.html
tytho

17

FucinaL'implementazione SHA-256 di è veloce e affidabile.

Per eseguire test su diverse implementazioni JavaScript SHA-256, vai a http://brillout.github.io/test-javascript-hash-implementations/ .

I risultati sulla mia macchina suggeriscono che la forgiatura sia l'implementazione più veloce e anche notevolmente più veloce della Stanford Javascript Crypto Library (sjcl) menzionata nella risposta accettata.

Forge è grande 256 KB, ma l'estrazione del codice relativo a SHA-256 riduce le dimensioni a 4,5 KB, vedere https://github.com/brillout/forge-sha256


Sono riuscito a installarlo con npm install node-forge, ma ora, come utilizzare la libreria creare un hash dalla stringa foo?
utente

15

Per chi è interessato, questo è il codice per la creazione di hash SHA-256 utilizzando sjcl:

import sjcl from 'sjcl'

const myString = 'Hello'
const myBitArray = sjcl.hash.sha256.hash(myString)
const myHash = sjcl.codec.hex.fromBits(myBitArray)

4
Questo dovrebbe essere incorporato nella risposta accettata per coloro che vogliono utilizzare la soluzione lì. (Diamine, anche i loro documenti non hanno uno snippet come questo)
MagicLegend

Questa è la migliore risposta. Stavo provando la soluzione crittografica ma non ha funzionato per me.
Augusto Gonzalez

11

No, non è possibile utilizzare JavaScript del browser per migliorare la sicurezza delle password. Ti consiglio vivamente di leggere questo articolo . Nel tuo caso, il problema più grande è il problema dell'uovo di gallina:

Qual è il "problema dell'uovo di gallina" con la distribuzione della crittografia Javascript?

Se non ti fidi della rete per fornire una password o, peggio, non ti fidi del server per non mantenere i segreti degli utenti, non puoi fidarti di loro per fornire il codice di sicurezza. Lo stesso malintenzionato che stava sniffando password o leggendo diari prima di introdurre la crittografia sta semplicemente dirottando il codice crittografico dopo che lo hai fatto.

[...]

Perché non posso utilizzare TLS / SSL per fornire il codice crittografico Javascript?

Puoi. È più difficile di quanto sembri, ma trasmetti in modo sicuro la crittografia Javascript a un browser utilizzando SSL. Il problema è che, avendo stabilito un canale sicuro con SSL, non hai più bisogno della crittografia Javascript; hai una crittografia "reale".

Il che porta a questo:

Il problema con l'esecuzione di codice crittografico in Javascript è che praticamente qualsiasi funzione da cui dipende la crittografia potrebbe essere sovrascritta silenziosamente da qualsiasi contenuto utilizzato per costruire la pagina di hosting. La sicurezza crittografica potrebbe essere annullata nelle prime fasi del processo (generando numeri casuali fasulli o manomettendo le costanti e i parametri utilizzati dagli algoritmi), o successivamente (inviando materiale chiave a un utente malintenzionato), o --- nello scenario più probabile --- bypassando completamente la crittografia.

Non esiste un modo affidabile per qualsiasi pezzo di codice Javascript per verificare il proprio ambiente di esecuzione. Il codice crittografico Javascript non può chiedere: "Ho davvero a che fare con un generatore di numeri casuali o con qualche facsimile fornito da un aggressore?" E certamente non può affermare "a nessuno è permesso fare nulla con questo segreto crittografico se non in modi che io, l'autore, approvo". Queste sono due proprietà che spesso vengono fornite in altri ambienti che utilizzano la crittografia e sono impossibili in Javascript.

Fondamentalmente il problema è questo:

  • I tuoi clienti non si fidano dei tuoi server, quindi vogliono aggiungere un codice di sicurezza extra.
  • Quel codice di sicurezza viene fornito dai tuoi server (quelli di cui non si fidano).

O in alternativa,

  • I tuoi clienti non si fidano di SSL, quindi vogliono che tu usi un codice di sicurezza aggiuntivo.
  • Quel codice di sicurezza viene fornito tramite SSL.

Nota: Inoltre, SHA-256 non è adatto a questo, dal momento che è così facile forzare le password non salate e non iterate . Se decidi di farlo comunque, cerca un'implementazione di bcrypt , scrypt o PBKDF2 .


1
Questo non è corretto. La sicurezza della password può essere migliorata mediante l'hashing sul lato client. È anche sbagliato che SHA-256 non sia adatto fintanto che qualcosa come PBKDF2 viene utilizzato sul lato server. In terzo luogo, mentre l'articolo di Matasano contiene informazioni utili, la battuta finale è sbagliata poiché ora abbiamo app browser che cambiano radicalmente il problema della gallina e dell'uovo.
user239558

2
Hai ragione che PBKDF2 dovrebbe essere utilizzato sul client. La rabbia contro la crittografia javascript lato client presuppone che la pagina iniziale e le richieste successive abbiano le stesse proprietà di sicurezza. Questo ovviamente non è vero per le app del browser in cui la pagina iniziale è installata separatamente ed è statica. Inoltre, non è vero per qualsiasi pagina con semantica cache-forever. In questi casi, è TOFU che è esattamente lo stesso di quando si installa un programma lato client. Ciò può essere vero anche in configurazioni più complesse in cui la pubblicazione di pagine statiche e dinamiche è separata sul server.
user239558

3
È legittimo evitare che gli amministratori di sistema abbiano accesso alle password degli utenti, quindi l'hashing sul lato client impedisce agli amministratori di database o ad altri professionisti IT di vedere le password degli utenti e provare a riutilizzarle per accedere ad altri servizi / sistemi, poiché molti utenti ripetono le proprie password .
Yamada

1
@Xenland Tutto quello che stai facendo è creare una nuova password che è "token + password". Tutti i problemi originali si applicano ancora. Ad esempio, un utente malintenzionato può sostituire JavaScript con il codice per inviare loro il token e la password.
Brendan Long

2
@Xenland Il problema della gallina e delle uova non ha nulla a che fare con le richieste che invii. Il problema è che il tuo client utilizza il codice JavaScript scaricato tramite una connessione non sicura per svolgere il lavoro di sicurezza. L'unico modo per inviare JavaScript a un browser Web in modo sicuro è servirlo tramite TLS e, una volta fatto, non è necessario fare nient'altro perché la connessione è già protetta. Non importa quale sia il tuo protocollo se utilizzi codice JavaScript che un utente malintenzionato può semplicemente sostituire.
Brendan Long,

10

Ho trovato questa implementazione molto facile da usare. Ha anche una generosa licenza in stile BSD:

jsSHA: https://github.com/Caligatio/jsSHA

Avevo bisogno di un modo rapido per ottenere la rappresentazione in stringa esadecimale di un hash SHA-256. Ci sono volute solo 3 righe:

var sha256 = new jsSHA('SHA-256', 'TEXT');
sha256.update(some_string_variable_to_hash);
var hash = sha256.getHash("HEX");

1

Oltre al lib di Stanford menzionato da Tylerl. Ho trovato jsrsasign molto utile (repository Github qui: https://github.com/kjur/jsrsasign ). Non so esattamente quanto sia affidabile, ma ho usato la sua API di SHA256, Base64, RSA, x509 ecc. E funziona abbastanza bene. In effetti, include anche la libreria Stanford.

Se tutto ciò che vuoi fare è SHA256, jsrsasign potrebbe essere eccessivo. Ma se hai altre esigenze nell'area correlata, penso che sia una buona soluzione.



2
D'accordo, ma per essere onesti, ho risposto a questa domanda nel 2015 mentre la specifica API di crittografia web di mozilla era stata pubblicata gennaio 2017
Faraway
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.