PHP 8.4.24 Released!

Voting

: min(four, seven)?
(Example: nine)

The Note You're Voting On

Anonymous
16 years ago
Another solution to the salted hash with salt included directly in the hash, while keeping the same length of the result. If you want to generate a hash, call the function without the second argument. If you want to check a password against a hash, use the hash as the second argument. In this case, the function returns the hash itself on success, or boolean false on failure. You can also specify a hash algorithm as the third argument (otherwise SHA-1 will be used).

<?php
function __hash($password, $obscured = NULL, $algorithm = "sha1")
{
  // whether to use user specified algorithm
  $mode = in_array($algorithm, hash_algos());
  // generate random salt
  $salt = uniqid(mt_rand(), true);
  // hash it
  $salt = $mode ? hash($algorithm, $salt) : sha1($salt);
  // get the length
  $slen = strlen($salt);
  // compute the actual length of salt we will use
  // 1/8 to 1/4 of the hash, with shorter passwords producing longer salts
  $slen = max($slen >> 3, ($slen >> 2) - strlen($password));
  // if we are checking password against a hash, harvest the actual salt from it, otherwise just cut the salt we already have to the proper size
  $salt = $obscured ? __harvest($obscured, $slen, $password) : substr($salt, 0, $slen);
  // hash the password - this is maybe unnecessary
  $hash = $mode ? hash($algorithm, $password) : sha1($password);
  // place the salt in it
  $hash = __scramble($hash, $salt, $password);
  // and hash it again
  $hash = $mode ? hash($algorithm, $hash) : sha1($hash);
  // cut the result so we can add salt and maintain the same length
  $hash = substr($hash, $slen);
  // ... do that
  $hash = __scramble($hash, $salt, $password);
  // and return the result
  return $obscured && $obscured !== $hash ? false : $hash;
}
?>

It uses a random, variable length salt, depending on the length of the password. The functions __scramble() and __harvest() are used to place salt into the hash or pull it out respectively. You can write your own, and of course the strength of the result greatly depends on them. They can be relatively simple yet still quite secure:

<?php
function __scramble($hash, $salt, $password)
{
  return substr($hash, 0, strlen($password)) . $salt . substr($hash, strlen($password));
}

function __harvest($obscured, $slen, $password)
{
  return substr($obscured, min(strlen($password), strlen($obscured) - $slen), $slen);
}
?>

Or they can be ridiculously complicated (my favourite kind):

<?php
function __scramble($hash, $salt, $password)
{
  $k = strlen($password); $j = $k = $k > 0 ? $k : 1; $p = 0; $index = array(); $out = ""; $m = 0;
  for ($i = 0; $i < strlen($salt); $i++)
  {
    $c = substr($password, $p, 1);
    $j = pow($j + ($c !== false ? ord($c) : 0), 2) % (strlen($hash) + strlen($salt));
    while (array_key_exists($j, $index))
      $j = ++$j % (strlen($hash) + strlen($salt));
    $index[$j] = $i;
    $p = ++$p % $k;
  }
  for ($i = 0; $i < strlen($hash) + strlen($salt); $i++)
    $out .= array_key_exists($i, $index) ? $salt[$index[$i]] : $hash[$m++];
  return $out;
}

function __harvest($obscured, $slen, $password)
{
  $k = strlen($password); $j = $k = $k > 0 ? $k : 1; $p = 0; $index = array(); $out = "";
  for ($i = 0; $i < $slen; $i++)
  {
    $c = substr($password, $p, 1);
    $j = pow($j + ($c !== false ? ord($c) : 0), 2) % strlen($obscured);
    while (in_array($j, $index))
      $j = ++$j % strlen($obscured);
    $index[$i] = $j;
    $p = ++$p % $k;
  }
  for ($i = 0; $i < $slen; $i++)
    $out .= $obscured[$index[$i]];
  return $out;
}
?>

<< Back to user notes page

To Top