Blog | G5 Cyber Security

Browser Password Hashing

TL;DR

You can calculate password hashes directly in a user’s browser using JavaScript and Web Crypto API. This improves security by preventing passwords from being sent to your server in plain text, even if compromised during transmission. However, it’s crucial to understand the limitations – browser-based hashing isn’t a replacement for robust server-side security practices.

How to Calculate Password Hashes in the Browser

  1. Choose a Hashing Algorithm: Select a secure hashing algorithm. SubtleCrypto supports algorithms like SHA-256, SHA-384, and SHA-512. SHA-256 is generally a good starting point.
    const algorithm = { name: 'SHA-256' };
  2. Fetch the Crypto API: Access the Web Crypto API using window.crypto.subtle.
    const crypto = window.crypto.subtle;
  3. Convert Password to ArrayBuffer: Passwords are strings, but hashing algorithms require an ArrayBuffer input. Use TextEncoder to convert the password string into a UTF-8 encoded Uint8Array and then create an ArrayBuffer.
    async function hashPassword(password) {
      const encoder = new TextEncoder();
      const data = encoder.encode(password);
      const buffer = await crypto.digest('SHA-256', data);
      return Array.from(new Uint8Array(buffer));
    }
    
  4. Generate a Salt (Important!): Salts are random values added to passwords before hashing, making rainbow table attacks much harder. Generate a unique salt for each password.
    async function generateSalt() {
      const array = new Uint8Array(16);
      window.crypto.getRandomValues(array);
      return Array.from(array);
    }
    
  5. Combine Password and Salt: Concatenate the salt with the password before hashing.
    async function hashWithSalt(password, salt) {
      const encoder = new TextEncoder();
      const combinedData = encoder.encode(salt.join('') + password);
      const buffer = await crypto.digest('SHA-256', combinedData);
      return Array.from(new Uint8Array(buffer));
    }
    
  6. Hash the Combined Data: Use crypto.digest() to hash the combined password and salt.
    const hashed = await crypto.digest('SHA-256', combinedData);
    
  7. Convert Hash to Hex String: The resulting hash is an ArrayBuffer. Convert it into a more readable hexadecimal string for storage.
    function arrayBufferToHexString(buffer) {
      return Array.from(new Uint8Array(buffer))
        .map(x => ('00' + x.toString(16)).slice(-2))
        .join('');
    }
    
  8. Store Salt and Hash: Store both the salt *and* the hash in your database. Never store passwords directly.
  9. Verification Process: When a user logs in, retrieve the stored salt for that user, combine it with the entered password, hash the combination using the same algorithm, and compare the resulting hash to the stored hash.

Important Considerations

Exit mobile version