Node.js - express bcrypt

Express bcrypt

Bcrypt is een externe module die geïnstalleerd kan worden in Node.js. De Bcrypt module maakt gebruik van de bcrypt hash functie die ontworpen is om een wachtwoord op een veilige manier op te slaan. Meer achtergrondinformatie over de bcrypt functie kun je hier vinden. Bcrypt

Wat is password hashing

Password hashing is een manier om een ingevuld wachtwoord door een gebruiker op een veilige manier op te slaan in bijvoorbeeld een database. De doel van hashing is dat het wachtwoord wat is ingevoerd niet meer terug te halen is naar de origineel ingevoerde string. Er bestaan verschillende soorten manieren van hashing en bcrypt is één van die manieren. De reden dat bcrypt veel gebruikt wordt is omdat het een bewezen veilige methode is om een wachtwoord te hashen. Er is geen bekende mogelijkheid de originele password string terug te halen uit de hash. Het is een zogenaamde eenwegsfunctie, er is geen weg terug.

Waarom password hashen?

De reden dat passwords worden gehashed is om te voorkomen dat wachtwoorden worden uitgelekt in het geval de database waarin de wachtwoorden zijn opgeslagen in verkeerde handen valt.

Gebruikers op websites gebruiken vaak hun wachtwoorden opnieuw dus het uitlekken van een wachtwoord kan grote gevolgen hebben. Als bedrijf of organisatie wil je je wapenen tegen dergelijke imago schade dus is het verstandig wachtwoorden NOOIT op te slaan in de originele tekst maar altijd te hashen.

Hoe ziet een bcrypt hash eruit?

Een voorbeeld van een bcrypt hash is $2b$10$VyjpC.enqW2Mgwashln8IeVol/0cPcspntRzV81OXkLDeayTdSEbq. Het originele wachtwoord is 123456. De opbouw van een bcrypt hash is als volgt:


$2b$10$VyjpC.enqW2Mgwashln8IeVol/0cPcspntRzV81OXkLDeayTdSEbq
\__/\/ \____________________/\_____________________________/
Alg Cost      Salt                        Hash
            

  • Alg: Het algoritme wat is gebruikt om te hashen, eigenlijk het versie nummer.
  • Cost: Een getal waarmee je opgeeft hoeveel keer er opnieuw wordt gehashed (hoe hoger het getal hoe moeilijker het is te hacken).
  • Salt: Een random string van 22 karakters die wordt gebruikt om het wachtwoord mee te hashen.
  • Hash: De resulterende hash. Dit is het resultaat van het hashen van de salt + origineel wachtwoord. Dus VyjpC.enqW2Mgwashln8Ie + 123456 resulteert in de hash Vol/0cPcspntRzV81OXkLDeayTdSEbq

Waarom een salt?

Het doel is om het zo moeilijk mogelijk te maken de originele string te herleiden uit de hash. Om te voorkomen dat er een lijst met wachtwoorden en bijbehorende hashes wordt gemaakt (een zogenoemde regenboogtabel), wordt er een willekeurige tekenreeks toegevoegd bij het maken van een hash. Dit wordt een salt genoemd.

Hoe controleer je dan of een wachtwoord correct is?

Het is dus niet mogelijk de hash terug om te zetten naar de originele string. Maar hoe controleer je dan het wachtwoord als de gebruiker inlogt?

Wanneer een gebruiker inlogt vult de gebruiker een wachtwoord in. Dit wachtwoord wordt ook gehashed en daarna vergeleken met de de opgeslagen hash. Zijn beide hashes gelijk, dan heeft de gebruiker het juiste wachtwoord ingevoerd. Zijn ze ongelijk? Dan heeft de gebruiker een foutief wachtwoord ingevoerd.

Hieronder de stappen hoe het inloggen werkt:

  • De gebruiker vult zijn gebruikersnaam (bijvoorbeeld email) en wachtwoord in.
  • De server ontvangt de gebruikersnaam en het wachtwoord.
  • De server maakt verbinding met de database en haalt de gebruiker op aan hand van de ingevoerde gebruikersnaam.
  • Bestaat de gebruiker niet in de database? Dan wordt er een bericht teruggegeven dat de ingevoerde gebruikersnaam/wachtwoord niet klopt.
  • Bestaat de gebruiker wel in de database? Dan wordt de salt van de opgeslagen hash gebruikt om het ingevoerde wachtwoord te hashen.
  • De hash van het ingevoerde wachtwoord wordt dan vergeleken met de opgeslagen hash.
  • Zijn ze niet gelijk? Dan wordt er een bericht teruggegeven dat de ingevoerde gebruikersnaam/wachtwoord niet klopt.
  • Zijn ze gelijk? Dan is de gebruiker ingelogd.

bcrypt gebruiken in NodeJS

Het hashen van de wachtwoorden hoeven we niet helemaal zelf te programmeren. Hier kunnen we de NodeJS bcrypt module voor gebruiken. Run hiervoor het commando npm install bcrypt

Een wachtwoord hashen met bcrypt

Het hashen van het wachtwoord doen we wanneer we een nieuwe gebruiker aanmaken (registeren) of wanneer een gebruiker zijn wachtwoord gegevens gaat aanpassen. Het onderstaande voorbeeld gaat uit van het aanmaken van een nieuwe gebruiker en het gebruik van MongoDB.

            
import bcrypt from 'bcrypt';

//een getal waarmee we opgeven hoeveel keer er geloopt wordt voor het maken van een salt (hoger getal is meer willekeurigheid)
const saltRounds = 10;

app.post('/register', (req, res) => {
    //email en wachtwoord worden uitgelezen uit de body
    const email = req.body.email;
    const password = req.body.password;

    //we inserten de user en geven een response terug
    insertUser(email, password).
        then(existingUser => sendRegisterResponse(res, existingUser));
});

//een functie om een registratie antwoord terug te geven
function sendRegisterResponse(res, existingUser) {
    if (existingUser) {
        //bestaat de gebruiker al geven we true terug, de client weet dan dat het niet gelukt is.
        res.send({ existingUser: true });
    } else {
        //bestond de gebruiker nog niet dan geven we false terug, de client weet dan dat het is gelukt.
        res.send({ existingUser: false });
    }
}

//een functie om een nieuwe gebruiker te maken met een email en password
async function insertUser(email, password) {
    try {
        // verbinden de client met de database server
        await client.connect();
        //verbinden met de database
        const database = client.db('');
        //de collectie ophalen uit de database
        const collection = database.collection('user');
        //we halen de bestaande gebruiker op aan hand van de email
        const existingUser = await collection.findOne({ email: email });

        //als de gebruiker al bestaat, dan geven we true terug en stoppen we
        if (existingUser) {
            return true;
        }
        //het wachtwoord wordt gehashed met bcrypt. De saltrounds bepaalt hoeveel keer er geloopt wordt voor een salt
        const hashedPassword = await bcrypt.hash(password, saltRounds);
        //we voegen de nieuwe gebruiker toe aan de collectie met het gehashte wachtwoord
        await collection.insertOne({ email: email, password: hashedPassword });

        //we geven false terug als de gebruiker nog niet bestaat (en dus succesvol is toegevoegd)
        return false;
    } finally {
        //we zorgen dat de connectie weer netjes wordt gesloten aan het einde
        await client.close();
    }
}
            
        

Een wachtwoord hash controleren met bcrypt (inloggen)

Wanneer we een gebruiker willen laten inloggen moeten we het ingevoerde wachtwoord controleren tegen de hash van de bestaande gebruiker. Het controleren van de hash doen we met een functie van bcrypt. De code hieronder gaat ervan uit dat je MongoDB gebruikt.

            

app.post('/login', (req, res) => {
    //email en wachtwoord worden uitgelezen uit de body
    const email = req.body.email;
    const password = req.body.password;

    //we loggen de user in en geven een response terug
    loginUser(email, password).
        then(loggedIn => sendLoginResponse(res, loggedIn));
});

//een functie om een login antwoord terug te geven
function sendLoginResponse(res, loggedIn) {
    if (loggedIn) {
        //is de gebruiker ingelogd dan sturen we loggedIn is true. De client weet dan dat het gelukt is.
        res.send({ loggedIn: true });
    } else {
        //is de gebruiker niet ingelogd dan sturen we loggedIn is false. De client weet dan dat het niet gelukt is.
        res.send({ loggedIn: false });
    }
}

async function loginUser(email, password) {
    try {
        // verbinden de client met de database server
        await client.connect();
        //verbinden met de database
        const database = client.db('');
        //de collectie ophalen uit de database
        const collection = database.collection('user');
        //we halen de bestaande gebruiker op aan hand van de email
        const existingUser = await collection.findOne({ email: email });

        //als de gebruiker niet bestaat, dan geven we false terug en stoppen we
        if (!existingUser) {
            return false;
        }
        //we controleren of het ingevoerde wachtwoord overeenkomt met het bestaande wachtwoord door bcrypt te gebruiken
        const passwordMatch = await bcrypt.compare(password, existingUser.password);
        //als er een match is geven we true terug
        if (passwordMatch) {
            return true;
        } else {
            //zo niet geven we false terug
            return false;
        }
    } finally {
        // we zorgen dat de client netjes gesloten wordt aan het einde
        await client.close();
    }
}