Hashing een wachtwoord in Java

Java Top

Ik heb zojuist het nieuwe aangekondigd Leer de lente natuurlijk, gericht op de basisprincipes van Spring 5 en Spring Boot 2:

>> BEKIJK DE CURSUS

1. Overzicht

In deze tutorial bespreken we het belang van wachtwoordhashing.

We zullen snel bekijken wat het is, waarom het belangrijk is en enkele veilige en onzekere manieren om het in Java te doen.

2. Wat is hash?

Hashing is het proces waarbij een string wordt gegenereerd, of hash, van een gegeven bericht met behulp van een wiskundige functie die bekend staat als een cryptografische hash-functie.

Hoewel er verschillende hash-functies zijn, moeten degenen die zijn afgestemd op het hashen van wachtwoorden vier hoofdeigenschappen hebben om veilig te zijn:

  1. Het zou moeten zijn deterministisch: hetzelfde bericht verwerkt door dezelfde hash-functie zou moeten altijd hetzelfde produceren hash
  2. Het is niet omkeerbaar: het is onpraktisch om een bericht van zijn hash
  3. Het heeft een hoge entropie: een kleine wijziging in een bericht zou een heel ander moeten produceren hash
  4. En het verzet zich botsingen: twee verschillende berichten zou niet hetzelfde moeten produceren hash

Een hash-functie die alle vier de eigenschappen heeft, is een sterke kandidaat voor wachtwoord-hashing, omdat ze samen de moeilijkheid om het wachtwoord van de hash reverse-engineering te maken drastisch vergroten.

Maar ook wachtwoord-hashing-functies moeten traag zijn. Een snel algoritme zou daarbij helpen brute kracht aanvallen waarbij een hacker probeert een wachtwoord te raden door miljarden (of triljoenen) potentiële wachtwoorden per seconde te hashen en te vergelijken.

Enkele geweldige hash-functies die aan al deze criteria voldoen, zijnPBKDF2, BCrypt, en SCrypt. Maar laten we eerst eens kijken naar enkele oudere algoritmen en waarom ze niet langer worden aanbevolen

3. Niet aanbevolen: MD5

Onze eerste hash-functie is het MD5 message-digest-algoritme, ontwikkeld in 1992.

Java's MessageDigest maakt dit eenvoudig te berekenen en kan ook in andere omstandigheden nuttig zijn.

In de afgelopen jaren Er is ontdekt dat MD5 de vierde eigenschap voor het hashen van wachtwoorden mislukt doordat het rekenkundig eenvoudig werd om botsingen te genereren. Als klap op de vuurpijl is MD5 een snel algoritme en daarom onbruikbaar tegen brute force-aanvallen.

Daarom wordt MD5 niet aanbevolen.

4. Niet aanbevolen: SHA-512

Vervolgens kijken we naar SHA-512, dat deel uitmaakt van de Secure Hash Algorithm-familie, een familie die in 1993 met SHA-0 begon.

4.1. Waarom SHA-512?

Naarmate computers steeds krachtiger worden en we nieuwe kwetsbaarheden ontdekken, leiden onderzoekers nieuwe versies van SHA af. Nieuwere versies hebben een steeds langere lengte, of soms publiceren onderzoekers een nieuwe versie van het onderliggende algoritme.

SHA-512 vertegenwoordigt de langste sleutel in de derde generatie van het algoritme.

Terwijl er zijn nu veiligere versies van SHA, SHA-512 is de sterkste die in Java is geïmplementeerd.

4.2. Implementeren in Java

Laten we nu eens kijken naar de implementatie van het SHA-512-hash-algoritme in Java.

Ten eerste moeten we het concept van begrijpen zout. Simpel gezegd, dit is een willekeurige reeks die wordt gegenereerd voor elke nieuwe hash.

Door deze willekeur te introduceren, verhogen we de hasj entropie, en we beschermen onze database tegen vooraf samengestelde lijsten met hashes die bekend staan ​​als regenboog tafels.

Onze nieuwe hashfunctie wordt dan grofweg:

zout <- genereer-zout; hash <- salt + ':' + sha512 (salt + wachtwoord)

4.3. Zout genereren

Om zout te introduceren, gebruiken we de SecureRandom klas van java.security:

SecureRandom random = nieuwe SecureRandom (); byte [] salt = nieuwe byte [16]; random.nextBytes (zout);

Vervolgens gebruiken we de MessageDigest class om het SHA-512 hash-functie met ons zout:

MessageDigest md = MessageDigest.getInstance ("SHA-512"); md.update (zout);

En met dat toegevoegd, kunnen we nu de verteren methode om ons gehashte wachtwoord te genereren:

byte [] hashedPassword = md.digest (passwordToHash.getBytes (StandardCharsets.UTF_8));

4.4. Waarom wordt het niet aanbevolen?

Bij gebruik met zout is SHA-512 nog steeds een goede optie, maar er zijn sterkere en langzamere opties die er zijn.

De overige opties die we zullen behandelen, hebben ook een belangrijk kenmerk: configureerbare sterkte.

5. PBKDF2, BCrypt en SCrypt

PBKDF2, BCrypt en SCrypt zijn drie aanbevolen algoritmen.

5.1. Waarom worden deze aanbevolen?

Elk van deze is traag en elk heeft de briljante eigenschap dat ze een configureerbare sterkte hebben.

Dit betekent dat naarmate computers sterker worden, we kunnen het algoritme vertragen door de invoer te wijzigen.

5.2. Implementatie van PBKDF2 in Java

Nu, salts zijn een fundamenteel principe van wachtwoordhashing, en dus hebben we er ook een nodig voor PBKDF2:

SecureRandom random = nieuwe SecureRandom (); byte [] salt = nieuwe byte [16]; random.nextBytes (zout);

Vervolgens maken we een PBEKeySpec en een SecretKeyFactory die we instantiëren met behulp van de PBKDF2WithHmacSHA1 algoritme:

KeySpec spec = nieuwe PBEKeySpec (password.toCharArray (), salt, 65536, 128); SecretKeyFactory factory = SecretKeyFactory.getInstance ("PBKDF2WithHmacSHA1");

De derde parameter (65536) is in feite de sterkteparameter. Het geeft aan hoeveel iteraties dit algoritme uitvoert, waardoor de tijd die nodig is om de hash te produceren, toeneemt.

Eindelijk kunnen we onze SecretKeyFactory om de hash te genereren:

byte [] hash = factory.generateSecret (spec) .getEncoded ();

5.3. Implementatie van BCrypt en SCrypt in Java

Dus het blijkt dat Ondersteuning voor BCrypt en SCrypt wordt nog niet geleverd met Java, hoewel sommige Java-bibliotheken deze ondersteunen.

Een van die bibliotheken is Spring Security.

6. Wachtwoord-hashing met veerbeveiliging

Hoewel Java native zowel de PBKDF2- als de SHA-hash-algoritmen ondersteunt, ondersteunt het geen BCrypt- en SCrypt-algoritmen.

Gelukkig voor ons wordt Spring Security geleverd met ondersteuning voor al deze aanbevolen algoritmen via de Wachtwoord Encoder koppel:

  • MessageDigestPasswordEncoder geeft ons MD5 en SHA-512
  • Pbkdf2PasswordEncoder geeft ons PBKDF2
  • BCryptPasswordEncoder geeft ons BCrypt, en
  • SCryptPasswordEncoder geeft ons SCrypt

De wachtwoordcoders voor PBKDF2, BCrypt en SCrypt worden allemaal geleverd met ondersteuning voor het configureren van de gewenste sterkte van de wachtwoordhash.

We kunnen deze encoders direct gebruiken, zelfs zonder een Spring Security-gebaseerde applicatie. Of, als we onze site beschermen met Spring Security, dan kunnen we onze gewenste wachtwoord-encoder configureren via zijn DSL of via afhankelijkheidsinjectie.

En, in tegenstelling tot onze voorbeelden hierboven, deze versleutelingsalgoritmen zullen intern het zout voor ons genereren. Het algoritme slaat de salt op in de output-hash voor later gebruik bij het valideren van een wachtwoord.

7. Conclusie

Daarom hebben we een diepe duik genomen in het hashen van wachtwoorden; verkennen van het concept en het gebruik ervan.

En we hebben een aantal historische hash-functies bekeken, evenals enkele momenteel geïmplementeerde functies voordat we ze in Java coderen.

Ten slotte zagen we dat Spring Security wordt geleverd met zijn wachtwoordcoderingsklassen, die een reeks verschillende hash-functies implementeren.

Zoals altijd is de code beschikbaar op GitHub.

Java onderkant

Ik heb zojuist het nieuwe aangekondigd Leer de lente natuurlijk, gericht op de basisprincipes van Spring 5 en Spring Boot 2:

>> BEKIJK DE CURSUS