The importance of randomness in authentication

Last Update: 2023-04-16

Authentication keys are machine-generated character sequences used for password resets, account deletions, API keys and other use cases. The generated random character sequence must use a secure random generator to create the key, otherwise anyone can use them.

Why?

If the attacker knows the algorithm used to generate the authentication key, which uses pseudo random numbers based on known metrics such as time and therefor not cryptographically secure, it can be more easily brute forced.

Take for example this time-based generating algorithm:

// Additional is a parameter for additional bytes to avoid collisions. Don't
// be confused by this, its just to add to the productionness of this function.
async function generateSecretKey(additional) {
  // Get current timestamp
  const now = new Date();
  const now_timestamp = now.getTime();

  return await hashSecretKey(now_timestamp, additional);
}

async function hashSecretKey(secret_key, additional) {
  if (!additional) {
    additional = "";
  }

  // Create a hexadecimal array from it
  const hexdec_string = Number(secret_key).toString(16) + additional;
  const hexdec_array = Int16Array.from(hexdec_string);

  // Obfuscate the data with a hashing algorithm
  const byteHash = Array.from(
    new Uint8Array(await crypto.subtle.digest("SHA-256", hexdec_array)),
  );
  const charHash = byteHash.map((b) => b.toString(16).padStart(2, "0")).join(
    "",
  );

  return charHash;
}

And the function to guess the hash:

// Guess the hash with a given estimated time when the hash was created
async function guessHash(target_hash, time_created) {
  const start_time = (new Date()).getTime();

  // Guess milliseconds between 2.5 min before up to 3.0 min after the estimated
  // creation time
  const start = time_created - 2500;
  const end = time_created + 3000;

  for (time = start; time <= end; ++time) {
    const hash = await hashSecretKey(time);

    // Imagine a http request here
    let found_hash = hash === target_hash;

    if (found_hash) {
      const end_time = (new Date()).getTime();

      return {
        duration: end_time - start_time,
        requests: time - start + 1,
      };
    }
  }

  return false;
}

Pseudo random generators are weak against attacks guessing the seed or the original data. In this example it just particulary easy to guess the API-Key, because the time - which is kinda known - is used.

Interactive example

This example generates a hash and records the time with ± 1 Minutes, when it was generated.