Czy są jakieś implementacje javascript SHA-256, które są ogólnie uważane za godne zaufania?

99

Piszę login do forum i muszę zaszyfrować hasło po stronie klienta w javascript przed wysłaniem go na serwer. Mam problem ze znalezieniem implementacji SHA-256, której mogę zaufać. Spodziewałem się, że będzie jakiś autorytatywny skrypt, z którego wszyscy korzystali, ale znajduję mnóstwo różnych projektów, z których każdy ma własne implementacje.

Zdaję sobie sprawę, że używanie kryptowalut innych ludzi jest zawsze przejawem wiary, chyba że masz kwalifikacje do samodzielnego przejrzenia i że nie ma uniwersalnej definicji „godnego zaufania”, ale wydaje się to czymś powszechnym i na tyle ważnym, że powinno istnieć konsensusu co do tego, czego użyć. Czy jestem po prostu naiwny?

Edytuj, ponieważ pojawia się dużo w komentarzach: Tak, ponownie wykonujemy bardziej rygorystyczny hash po stronie serwera. Haszowanie po stronie klienta nie jest ostatecznym wynikiem, który zapisujemy w bazie danych. Haszowanie po stronie klienta jest spowodowane żądaniem klienta. Nie podali konkretnego powodu, prawdopodobnie po prostu lubią przesadę.

jono
źródło
9
Nie po to, żeby zejść z tematu, ale dlaczego haszujesz hasło po stronie klienta?
Steve
5
@ddyer Nawet nie blisko. „Nie rzucaj własnym” odnosi się do wymyślania własnego algorytmu, pisania własnej implementacji algorytmu, rozwijania własnego protokołu na podstawie algorytmów kryptograficznych lub prawie wszystkiego powyżej, używając tak wysokiego poziomu abstrakcji, jaki jest dostępny. Jeśli myślisz, że będziesz bezpiecznie trzymać się bezpiecznego rdzenia i pisać tylko kod kleju, będziesz się źle bawić.
Stephen Touset
26
jeśli używasz zaszyfrowanego hasła bez protokołu wezwania / odpowiedzi, to zaszyfrowane hasło JEST hasłem i tak naprawdę jest tym samym, co przesyłanie hasła w postaci zwykłego tekstu.
ddyer
26
@ddyer Ochrona hasła użytkownika w postaci zwykłego tekstu we wszystkich innych witrynach, w których mogą go używać, ma pewną wartość, jeśli nie w szczególności w naszej witrynie. Jest to łatwa poprawka, która może nam nie pomoże, ale może potencjalnie pomóc użytkownikowi, jeśli gdzieś schrzanimy. I jak powiedziałem, prośba klienta, nic nie mogę z tym zrobić, nawet gdybym chciał.
jono
5
@Anorov Jestem bardziej niż otwarty na zmianę zdania :) ale w tym przypadku nie bardzo rozumiem, jak odnosi się twój punkt widzenia. Dwa razy haszujemy hasło: raz po stronie klienta za pomocą prostego SHA-256, a raz po stronie serwera za pomocą czegoś bardziej wymagającego. Pierwszy do ochrony tekstu jawnego w przypadku MITM lub podobnego, a drugi do brutalnej ochrony. Nawet jeśli posiadasz bazę danych i hash administratora, nie możesz użyć tego bezpośrednio do sprawdzenia poprawności logowania.
jono

Odpowiedzi:

115

PRZESTARZAŁE: Wiele nowoczesnych przeglądarek ma teraz pierwszorzędną obsługę operacji kryptograficznych. Zobacz odpowiedź Witalija Zdanewicza poniżej.


Biblioteka Stanford JS Crypto zawiera implementację SHA-256. Chociaż kryptowaluty w JS nie są tak dobrze sprawdzone, jak inne platformy wdrożeniowe, ta jest przynajmniej częściowo opracowana i do pewnego stopnia sponsorowana przez Dana Boneha , który jest dobrze znaną i zaufaną marką w kryptografii , i oznacza, że ​​projekt jest nadzorowany przez kogoś, kto faktycznie wie, co robi. Projekt jest również wspierany przez NSF .

Warto jednak zauważyć ...
... że jeśli przed przesłaniem haszujesz hasło po stronie klienta, to hash jest hasłem , a oryginalne hasło staje się nieistotne. Osoba atakująca musi tylko przechwycić hash, aby podszyć się pod użytkownika, a jeśli ten skrót jest przechowywany na serwerze w postaci niezmienionej, serwer przechowuje prawdziwe hasło (skrót) w postaci zwykłego tekstu .

Twoje bezpieczeństwo jest teraz gorsze, ponieważ zdecydowałeś dodać własne ulepszenia do tego, co wcześniej było zaufanym schematem.

tylerl
źródło
32
Jeśli jednak ponownie haszujesz na serwerze, ta praktyka jest całkowicie uzasadniona.
Nick Brunt
13
@NickBrunt, jeśli haszujesz na serwerze, nie zapisujesz przesłanego hasła, ale nie dodałeś żadnych zabezpieczeń poza przesyłaniem oryginalnego hasła w przeciwieństwie do przesyłania skrótu hasła, ponieważ w obu przypadkach przesyłasz prawdziwe hasło.
tylerl
54
To prawda, ale nie jest to gorsze niż wysłanie hasła, które jest prawdopodobnie używane w innym miejscu w Internecie w postaci zwykłego tekstu.
Nick Brunt
15
Zgadzam się z @NickBrunt. Lepiej jest, jeśli osoba atakująca odczyta losowy ciąg znaków skrótu, który może pasować tylko do tej konkretnej aplikacji, zamiast oryginalnego hasła, które może być używane w kilku miejscach.
Aebsubis
13
Muszę używać funkcji mieszania po stronie klienta, ponieważ nie chcę, aby serwer kiedykolwiek widział hasło użytkownika w postaci zwykłego tekstu. Nie w celu zwiększenia bezpieczeństwa świadczonych przeze mnie usług, ale w stosunku do użytkownika. Nie tylko oszczędzam zasolony hash użytkownika ze stałą solą (stałą dla użytkownika, nie globalnie), ale także ponownie haszuję go z losową solą sesji przy każdym logowaniu, co zapewnia dodatkowe zabezpieczenie sieci przed podsłuchiwaniem. Jeśli serwer zostanie przejęty, gra się kończy, ale to prawda w przypadku wszystkiego, co nie jest naprawdę p2p.
Hatagashira,
57

Na https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/digest znalazłem ten fragment wykorzystujący wewnętrzny moduł js:

async function sha256(message) {
    // encode as UTF-8
    const msgBuffer = new TextEncoder().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;
}

Zauważ, że crypto.subtledostępne tylko w httpslub localhost- na przykład dla lokalnego rozwoju z python3 -m http.servermusisz dodać tę linię do /etc/hosts: 0.0.0.0 localhost

Uruchom ponownie - i możesz otworzyć localhost:8000podczas pracy crypto.subtle.

Witalij Zdaniewicz
źródło
2
To jest niesamowite. Dziękuję Ci. Czy porównywalny interfejs API jest dostępny w Node.js?
Con Antonakos
2
Wbudowana
tytho
18

Implementacja SHA-256 w Forge jest szybka i niezawodna.

Aby uruchomić testy na kilku implementacjach JavaScript SHA-256, przejdź do http://brillout.github.io/test-javascript-hash-implementations/ .

Wyniki na moim komputerze sugerują, że forge jest najszybszą implementacją, a także znacznie szybszą niż biblioteka Stanford Javascript Crypto Library (sjcl) wymieniona w zaakceptowanej odpowiedzi.

Forge ma rozmiar 256 KB, ale wyodrębnienie kodu związanego z SHA-256 zmniejsza rozmiar do 4,5 KB, zobacz https://github.com/brillout/forge-sha256

brillout
źródło
Udało mi się go zainstalować za pomocą npm install node-forge, ale teraz, jak użyć biblioteki, aby utworzyć hash z ciągu foo?
użytkownik
17

Dla zainteresowanych jest to kod do tworzenia skrótu SHA-256 przy użyciu sjcl:

import sjcl from 'sjcl'

const myString = 'Hello'
const myBitArray = sjcl.hash.sha256.hash(myString)
const myHash = sjcl.codec.hex.fromBits(myBitArray)
Danny Sullivan
źródło
5
Należy to uwzględnić w przyjętej odpowiedzi dla tych, którzy chcą tam skorzystać z rozwiązania. (Heck, nawet ich własne dokumenty nie mają takiego fragmentu)
MagicLegend
To najlepsza odpowiedź. Próbowałem rozwiązania kryptograficznego, ale nie zadziałało.
Augusto Gonzalez
12

Nie, nie ma sposobu, aby poprawić bezpieczeństwo hasła za pomocą JavaScript przeglądarki. Gorąco polecam przeczytanie tego artykułu . W twoim przypadku największym problemem jest problem jaja kurzego:

Na czym polega „problem z jajkiem kurzego” w dostarczaniu kryptografii JavaScript?

Jeśli nie ufasz sieci w zakresie dostarczania hasła lub, co gorsza, nie ufasz serwerowi, że nie zachowuje tajemnic użytkownika, nie możesz mu ufać, że dostarczy kod bezpieczeństwa. Ten sam napastnik, który węszył hasła lub czytał pamiętniki przed wprowadzeniem kryptografii, po prostu porywa kod kryptograficzny po tym, jak to zrobisz.

[…]

Dlaczego nie mogę użyć TLS / SSL do dostarczenia kodu kryptograficznego Javascript?

Możesz. To trudniejsze niż się wydaje, ale bezpiecznie przesyłasz krypto Javascript do przeglądarki używającej SSL. Problem w tym, że mając bezpieczny kanał z SSL, nie potrzebujesz już kryptografii Javascript; masz „prawdziwą” kryptografię.

Co prowadzi do tego:

Problem z uruchomieniem kodu kryptograficznego w Javascript polega na tym, że praktycznie każda funkcja, od której zależy krypto, może zostać po cichu przesłonięta przez dowolną treść użytą do zbudowania strony hostingowej. Zabezpieczenia kryptograficzne można cofnąć na wczesnym etapie procesu (generując fałszywe liczby losowe lub manipulując stałymi i parametrami używanymi przez algorytmy) lub później (przekazując kluczowy materiał z powrotem do atakującego) lub - w najbardziej prawdopodobnym scenariuszu --- pomijając całkowicie kryptowalutę.

Nie ma niezawodnego sposobu, aby jakikolwiek fragment kodu JavaScript mógł zweryfikować środowisko wykonawcze. Kod kryptograficzny Javascript nie może zapytać: „Czy naprawdę mam do czynienia z generatorem liczb losowych, czy z jakimś faksymileem dostarczonym przez atakującego?” I z pewnością nie można stwierdzić, że „nikomu nie wolno robić nic z tym krypto sekretem, chyba że w sposób, który ja, autor, zgadzam się”. Są to dwie właściwości, które często są udostępniane w innych środowiskach korzystających z kryptowalut i nie są dostępne w JavaScript.

Zasadniczo problem jest następujący:

  • Twoi klienci nie ufają Twoim serwerom, więc chcą dodać dodatkowy kod bezpieczeństwa.
  • Ten kod bezpieczeństwa jest dostarczany przez Twoje serwery (te, którym nie ufają).

Lub alternatywnie

  • Twoi klienci nie ufają SSL, więc chcą, abyś użył dodatkowego kodu bezpieczeństwa.
  • Ten kod bezpieczeństwa jest dostarczany przez SSL.

Uwaga: SHA-256 nie jest do tego odpowiedni, ponieważ tak łatwo jest brutalnie wymusić niesolone, nie iteracyjne hasła . Jeśli zdecydujesz się to zrobić tak, poszukaj realizacji bcrypt , scrypt lub PBKDF2 .

Brendan Long
źródło
1
To nie jest poprawne. Bezpieczeństwo haseł można poprawić, mieszając je po stronie klienta. Błędem jest również to, że SHA-256 nie nadaje się, dopóki po stronie serwera jest używane coś takiego jak PBKDF2. Po trzecie, chociaż artykuł matasano zawiera przydatne informacje, puenta jest błędna, ponieważ mamy teraz aplikacje przeglądarkowe, które zasadniczo zmieniają problem z jajkiem i kura.
user239558
2
Masz rację, że PBKDF2 powinien być używany na kliencie. Wściekłość na kryptowaluty javascript po stronie klienta zakłada, że ​​strona początkowa i kolejne żądania mają te same właściwości bezpieczeństwa. Oczywiście nie dotyczy to aplikacji przeglądarek, w których strona początkowa jest instalowana osobno i jest statyczna. Nie dotyczy to również żadnej strony z semantyką cache-forever. W takich przypadkach jest to TOFU, który jest dokładnie taki sam, jak podczas instalowania programu po stronie klienta. Może to również mieć miejsce w bardziej złożonych konfiguracjach, w których serwowanie stron statycznych i dynamicznych jest rozdzielone na serwerze.
user239558
3
Uzasadnione jest unikanie dostępu administratorów systemu do haseł użytkowników, więc haszowanie po stronie klienta uniemożliwia administratorom baz danych lub innym specjalistom IT wyświetlanie haseł użytkowników i próby ponownego wykorzystania ich w celu uzyskania dostępu do innych usług / systemów, ponieważ wielu użytkowników powtarza swoje hasła .
Yamada
1
@Xenland Jedyne, co robisz, to tworzenie nowego hasła, które jest „token + hasło”. Wszystkie pierwotne problemy nadal występują. Na przykład osoba atakująca może zastąpić JavaScript kodem, aby wysłać im token + hasło.
Brendan Long
2
@Xenland Problem z kurczakiem i jajkiem nie ma nic wspólnego z wysyłanymi przez Ciebie żądaniami. Problem polega na tym, że Twój klient używa kodu JavaScript, który został pobrany przez niezabezpieczone połączenie, aby zapewnić bezpieczeństwo. Jedynym sposobem bezpiecznego wysłania JavaScript do przeglądarki internetowej jest udostępnienie go przez TLS, a kiedy to zrobisz, nie musisz robić nic więcej, ponieważ twoje połączenie jest już bezpieczne. Nie ma znaczenia, jaki jest twój protokół, jeśli używasz kodu JavaScript, który atakujący może po prostu zastąpić.
Brendan Long,
10

Uważam, że ta implementacja jest bardzo łatwa w użyciu. Posiada również hojną licencję w stylu BSD:

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

Potrzebowałem szybkiego sposobu na uzyskanie reprezentacji ciągu szesnastkowego skrótu SHA-256. Zajęło tylko 3 linie:

var sha256 = new jsSHA('SHA-256', 'TEXT');
sha256.update(some_string_variable_to_hash);
var hash = sha256.getHash("HEX");
cobbzilla
źródło
1

Oprócz biblioteki Stanforda, o której wspomniał Tylerl. Uważam, że jsrsasign jest bardzo przydatny (repozytorium Github tutaj: https://github.com/kjur/jsrsasign ). Nie wiem, na ile jest to godne zaufania, ale użyłem jego API SHA256, Base64, RSA, x509 itp. I działa całkiem nieźle. W rzeczywistości obejmuje również bibliotekę Stanford.

Jeśli wszystko, co chcesz zrobić, to SHA256, jsrsasign może być przesadą . Ale jeśli masz inne potrzeby w pokrewnym obszarze, uważam, że to dobrze pasuje.

Daleko
źródło
2
Bezpieczniejsze i szybsze jest korzystanie z developer.mozilla.org/en-US/docs/Web/API/Web_Crypto_API
Vitaly Zdanevich
2
Zgadzam się, ale żeby być uczciwym, odpowiedziałem na to pytanie w 2015 roku, podczas gdy specyfikacja interfejsu API kryptografii internetowej Mozilli została opublikowana w styczniu 2017 r.
Faraway