Simple Javascript Password Encryption & Decryption

Welcome to a tutorial on how to encrypt and decrypt passwords in Javascript. First, a piece of good news for you guys – Javascript has a native web crypto API that we can use to protect passwords, and there are plenty of free crypto libraries as well.

But now for the bad news – Password encryption only makes sense if you are working on server-side Javascript (such as NodeJS), it pretty much does nothing good on the client side. Just why is that so? Let us explore that in-depth by walking through a couple of examples on both server and client-side Javascript. Read on!

ⓘ I have included a zip file with all the example source code at the start of this tutorial, so you don’t have to copy-paste everything… Or if you just want to dive straight in.

 

TABLE OF CONTENTS

 

 

DOWNLOAD & NOTES

Firstly, here is the download link to the example code as promised.

 

QUICK NOTES

If you spot a bug, feel free to comment below. I try to answer short questions too, but it is one person versus the entire world… If you need answers urgently, please check out my list of websites to get help with programming.

 

EXAMPLE CODE DOWNLOAD

Click here to download the source code, I have released it under the MIT license, so feel free to build on top of it or use it in your own project.

 

CLIENT-SIDE PASSWORDS

Let us start with how to do password encryption/decryption on client-side Javascript (that is on a web page or web app). Also on why most web developers won’t bother doing this at all.

 

BASIC JAVASCRIPT CRYPTO

1-basic-password.html
<!-- (A) LOAD CRYPTO JS LIBRARY -->
<!-- https://cryptojs.gitbook.io/docs/ -->
<!-- https://cdnjs.com/libraries/crypto-js -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.1.1/crypto-js.min.js"></script>
<script>
// (B) ENCRYPT & DECRYPT FUNCTIONS
var crypt = {
  // (B1) THE SECRET KEY
  secret : "CIPHERKEY",
 
  // (B2) ENCRYPT
  encrypt : clear => {
    var cipher = CryptoJS.AES.encrypt(clear, crypt.secret);
    return cipher.toString();
  },
 
  // (B3) DECRYPT
  decrypt : cipher => {
    var decipher = CryptoJS.AES.decrypt(cipher, crypt.secret);
    return decipher.toString(CryptoJS.enc.Utf8);
  }
};
 
// (C) TEST
// (C1) ENCRYPT CLEAR TEXT
var cipher = crypt.encrypt("FOO BAR");
console.log(cipher);
 
// (C2) DECRYPT CIPHER TEXT
var decipher = crypt.decrypt(cipher);
console.log(decipher);
</script>

For the purpose of demonstrating that Javascript is capable of doing crypto stuff, here is an example that uses a good old library called Crypto-JS. Yep, all we do is just plug and play the library to do password encryption/decryption.

 

 

CLIENT-SIDE PASSWORD ENCRYPTION – IT’S BAD

THE SIGN-UP PAGE

2a-signup.html
<!-- (A) LOAD CRYPTO JS LIBRARY -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.1.1/crypto-js.min.js"></script>
<script>
// (B) ENCRYPT PASSWORD
function crypt () {
  // (B1) GET PASSWORD + ENCRYPT
  var password = document.getElementById("user-password"),
      cipher = CryptoJS.AES.encrypt(password.value, "CIPHERKEY");

  // (B2) SET ENCRYPTED PASSWORD TO HIDDEN FIELD
  document.getElementById("user-real-password").value = cipher..toString();

  // (B3) REMOVE CLEAR TEXT PASSWORD
  password.value = "";

  // (B4) SUBMIT FORM
  return true;
}
</script>
 
<!-- (C) REGISTRATION FORM -->
<form onsubmit="return crypt()">
  <input type="email" name="email" required value="jon@doe.com">
  <input type="password" id="user-password" required value="Hello World 12345">
  <input type="hidden" id="user-real-password" name="password">
  <input type="submit" value="Register">
</form>

With the baseline established, let us walk through why client-side password encryption is a waste of time and why you should never do it. Here is a common example of a registration page, where beginners encrypt the password before sending it to the server.

Yep, that’s pretty neat, right? The password is now encrypted and secured. We don’t have to worry about it being “hijacked” by other people when the form is being submitted… So, just what is wrong with this? You will find out in the next 5 minutes.

 

HOW ABOUT THE DECRYPTION DURING LOGIN?

2b-login.html
<!-- (A) LOAD CRYPTO JS LIBRARY -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.1.1/crypto-js.min.js"></script>
<script>
// (B) PROCESS LOGIN
function login () {
  // (B1) SET EMAIL
  var data = new FormData();
  data.append("email", document.getElementById("user-email").value);
 
  // (B2) AJAX REQUEST - GET + CHECK PASSWORD FROM SERVER
  fetch("get-user.txt")
  .then(res => res.json())
  .then(res => {
    // (B2-1) DECRYPT
    let decipher = CryptoJS.AES.decrypt(res.password, "CIPHERKEY").toString(CryptoJS.enc.Utf8);
 
    // (B2-2) MATCH!
    if (decipher == document.getElementById("user-password").value) {
      alert("OK!");
      // location.href = "members.html";
    }
 
    // (B2-3) WRONG
    else { alert("Invalid password"); }
  });
  return false;
}
</script>
 
<!-- (C) LOGIN FORM -->
<form onsubmit="return login()">
  <input type="email" id="user-email" required value="jon@doe.com">
  <input type="password" id="user-password" required value="Hello World 12345">
  <input type="submit" value="Sign in">
</form>

Next, let us assume that the user data is saved into the server’s database upon successful registration. Then comes the following question – How do we check and confirm the password? Some beginners will just pull the password straight from the server and verify it using Javascript.

 

WHY THIS IS A WASTE OF TIME

This seems to work at first, but there are a lot of loopholes. Importantly, remember that client-side Javascript runs on the user’s device. Meaning, the source code is downloaded and fully visible to all users. Your secret key and algorithm are just in plain sight… All your “security measures” are worthless. Anyone can reverse engineer and harvest all the passwords. No sweat.

Some of you smarty pants may be thinking “just obfuscate the Javascript then”. Nope. It can still be reverse-engineered. Also, sending the password to the client side for verification is another critical loophole. Encrypted or not. Period.

 

 

THE CORRECT WAY

The only correct way to properly protect the password is to encrypt/verify it on the server side. This is the only way to keep the password crypto hidden away from the users. Oh, and if you are worried about the passwords being “hijacked” when the registration form is being submitted – There is a very easy solution.

That is called “just enforce HTTPS on your server”. Yep, once it’s HTTPS, all data exchange between client and server is automatically encrypted. No need for all the manual Javascript encryption gimmicks.

P.S. If you are using PHP on the server side, I will leave links below to my other tutorials on how to create a proper registration and login page.

 

SERVER-SIDE PASSWORDS

Now let’s move on to examples of how we can do password encryption and decryption in NodeJS. Also on why 2-way encryption may not be the best.

 

GET CRYPTOJS

To get the CryptoJS library, simply navigate to your project folder in the command line and run npm i crypto-js.

 

 

ENCRYPT-DECRYPT

3-encrypt-decrypt.js
// (A) LOAD ENCRYPT LIBRARY
const CryptoJS = require("crypto-js");

// (B) SECRET KEY
var key = "ASECRET";

// (C) ENCRYPT
var cipher = CryptoJS.AES.encrypt("PASSWORD", key);
cipher = cipher.toString();
console.log(cipher);

// (D) DECRYPT
var decipher = CryptoJS.AES.decrypt(cipher, key);
decipher = decipher.toString(CryptoJS.enc.Utf8);
console.log(decipher);

Yep, as simple as that. We are still using the same Crypto JS library, and it works just the same as on the client-side.

 

WARNING: 2-WAY ENCRYPTION IS PRETTY BAD!

Before the hater troll things start to spew acid, I don’t really recommend using 2-way encryption for passwords. I.E. Encrypted passwords that can be decrypted. Consider this – If the secret key is compromised, the bad code ninja can pretty much decrypt and retrieve all the passwords in your system.

 

PASSWORD WITH SALT IN NODEJS

4-hash-salt.js
// Credits : https://ciphertrick.com/salt-hash-passwords-using-nodejs-crypto/
// (A) REQUIRE CRYPTO LIBRARY
var crypto = require("crypto");

// (B) CREATE PASSWORD HASH
var creepy = clear => {
  // (B1) GENERATE RANDOM SALT
  let length = 16;
  let salt =  crypto.randomBytes(Math.ceil(length / 2))
  .toString("hex")
  .slice(0, length); 

  // (B2) SHA512 HASH
  let hash = crypto.createHmac("sha512", salt);
  hash.update(clear);
  return {
    salt: salt,
    hash: hash.digest("hex")
  };
};

// (C) TEST ENCRYPT
// Save BOTH the password and salt into database or file
var clearpass = "He110Wor!d";
var creeped = creepy(clearpass);
console.log("===== HASHED PASSWORD + SALT =====");
console.log(creeped);

// (D) VALIDATE PASSWORD
var validate = (userpass, hashedpass, salt) => {
  let hash = crypto.createHmac("sha512", salt);
  hash.update(userpass);
  userpass = hash.digest("hex");
  return userpass == hashedpass;
};

// (E) TEST VALIDATE
// clearpass = "FOOBAR";
var validated = validate(clearpass, creeped.hash, creeped.salt);
console.log("===== VALIDATION =====");
console.log("Clear password: " + clearpass);
console.log("Validation status: " + validated);

So for you guys who are thinking of using 2-way encryption for the convenience of password recovery, you have been informed. The safer way is to do a one-way hash with salt instead. Credits to CipherTrick for this one done with simple SHA512.

 

 

EXTRA BITS & LINKS

That’s all for this guide, and here is a small section on some extras and links that may be useful to you.

 

LINKS & REFERENCES

Sorry folks, I am not a security expert. So if you are looking for ways to use the Web Crypto API, here are some good links that I found:

For the people who are on PHP and trying to secure the passwords:

 

OTHER CRYPTO LIBRARIES

Don’t like CryptoJS? Here are a few other options.

 

TUTORIAL VIDEO

 

INFOGRAPHIC CHEAT SHEET

Encrypt-Decrypt Password In JS (Click to enlarge)

 

THE END

Thank you for reading, and we have come to the end of this guide. I hope that it has helped you with your project, and if you want to share anything with this guide, please feel free to comment below. Good luck and happy coding!

Leave a Comment

Your email address will not be published. Required fields are marked *