(PHP 5 >= 5.1.2, PECL hash >= 1.1)
hash — Berechnet den Hash einer Nachricht
Name des gewählten Hash-Algorithmus (z.B. "md5", "sha256", "haval160,4", usw...)
Nachricht, die gehasht werden soll.
Ist dieser Parameter TRUE, werden direkt Binärdaten zurückgegeben, andernfalls werden klein geschriebene Hexadezimalziffern zurückgegeben.
Gibt den berechneten Hash als Hexadezimalzahl zurück, außer raw_output ist wahr, in diesem Fall wird die binäre Darstellung des Hashes zurückgegeben.
Beispiel #1 hash()-Beispiel
<?php
echo hash('ripemd160', 'Franz jagt im komplett verwahrlosten Taxi quer durch Bayern.');
?>
Das oben gezeigte Beispiel erzeugt folgende Ausgabe:
1e3d00e6a15336c66e46881d1efe8afc7813f66e
If the given algo parameter contains an algorithm that is not supported, the hash function will return bool(false) and issue a warning.
I've recently had the need to look at various hash algorithms and their values of the same string. To make my life a little easier I wrote this little script. It takes a string and runs it through all of the available hash algorithms on your server, outputting them in a clean little HTML table.
-----
<?php
/* Get the posted value of the form if there is one */
$p = empty($_POST['p']) ? null : $_POST['p'];
?>
<html>
<head><title>Hash testing</title></head>
<style type="text/css">
table {border-collapse: collapse;}
table, td, th {border: solid 1px #ccc;}
th {background: #e1e1e1;border-color: #999;}
td, th {padding: 0.25em;}
td.algo {font-weight: bold;}
tr.on td {background: #f0f0f0;}
</style>
<body>
<h1>String hashing</h1>
<form method="post" action="<?php echo basename(__FILE__) ?>">
<p><label for="p">Enter a string to hash:</label><br /><input id="p" type="text" name="p" value="<?php echo $p ?>" /></p>
<p><input type="submit" name="submit" value="Hash It" /></p>
</form>
<?php /* If there is a posted value use it */ ?>
<?php if ($p): ?>
<hr />
<h2>Table of hash values for <em><?php echo $p ?></em> based on algorithm</h2>
<table>
<tr>
<th>Algorithm</th>
<th>Hashed value of <em><?php echo $p ?></em></th>
</tr>
<?php /* Loop through each hash algorithm, colorizing every other row */ ?>
<?php $on = false; foreach (hash_algos() as $algo): ?>
<tr<?php if ($on): ?> class="on"<?php endif; ?>>
<td class="algo"><?php echo $algo ?></td>
<td class="hash"><?php echo hash($algo, $p) ?></td>
</tr>
<?php $on = !$on; endforeach; ?>
</table>
<?php endif; ?>
</body>
</html>
The well known hash functions MD5 and SHA1 should be avoided in new applications. Collission attacks against MD5 are well documented in the cryptographics literature and have already been demonstrated in practice. Therefore, MD5 is no longer secure for certain applications.
Collission attacks against SHA1 have also been published, though they still require computing power, which is somewhat out of scope. As computing power increases with time and the attacks are likely to get better, too, attacks against systems relying on SHA1 for security are likely to become feasible within the next few years.
There is no lack of potential alternative hash algorithms, as the many choices for the "algo" argument of PHPs hash() function already suggests. Unfortunately, there is lack of analysis, as to how secure these alternative algorithms are. It is rather safe to assume, though, that the SHA2 family with its most prominent members SHA-256 und SHA-512, is better than SHA1.
When storing password hashes, it is a good idea to prefix a salt to the password before hashing, to avoid the same passwords to hash to the same values and to avoid the use of rainbow tables for password recovery. Unlike suggested in other articles, there is no security advantage in putting the salt in the middle, or even at both the beginning and the end, of the combined salt-password-string.
Rather, there are two other factors, that determine the strength of the salt: Its length and its variability. For example, using the same salt for all passwords is easy to implement, but gives only very little additional security. In particular, if users type the same passwords, they will still hash to the same value!
Therefore, the salt should be random string with at least as many variable bits, as there are bits in the hash result. In the user database, store username, the randomly generated salt for that user, and the result of hashing the salt-password-string. Access authentication is then done by looking up the entry for the user, calculating the hash of the salt found in the database and the password provided by the user, and comparing the result with the one stored in the database.
Performance test results on my laptop:
Results are here shorten to fit php web notes ...
This was tested with 1024000 bytes (1000 KB) of random data, md4 always gets the first place, and md2 always get the last place :)
Results: (in microseconds)
1. md4 5307.912
2. md5 6890.058
3. crc32b 7298.946
4. crc32 7561.922
5. sha1 8886.098
6. tiger128,3 11054.992
7. haval192,3 11132.955
8. haval224,3 11160.135
9. tiger160,3 11162.996
10. haval160,3 11242.151
11. haval256,3 11327.981
12. tiger192,3 11630.058
13. haval128,3 11880.874
14. tiger192,4 14776.945
15. tiger128,4 14871.12
16. tiger160,4 14946.937
17. haval160,4 15661.954
18. haval192,4 15717.029
19. haval256,4 15759.944
20. adler32 15796.184
21. haval128,4 15887.022
22. haval224,4 16047.954
23. ripemd256 16245.126
24. haval160,5 17818.927
25. haval128,5 17887.115
26. haval224,5 18085.002
27. haval192,5 18135.07
28. haval256,5 18678.903
29. sha256 19020.08
30. ripemd128 20671.844
31. ripemd160 21853.923
32. ripemd320 22425.889
33. sha384 45102.119
34. sha512 45655.965
35. gost 57237.148
36. whirlpool 64682.96
37. snefru 80352.783
38. md2 705397.844
Code for generating this:
(compatible both with browser and cli mode)
<pre>
<?php
echo 'Building random data ...' . PHP_EOL;
@ob_flush();flush();
$data = '';
for ($i = 0; $i < 64000; $i++)
$data .= hash('md5', rand(), true);
echo strlen($data) . ' bytes of random data built !' . PHP_EOL . PHP_EOL . 'Testing hash algorithms ...' . PHP_EOL;
@ob_flush();flush();
$results = array();
foreach (hash_algos() as $v) {
echo $v . PHP_EOL;
@ob_flush();flush();
$time = microtime(true);
hash($v, $data, false);
$time = microtime(true) - $time;
$results[$time * 1000000000][] = "$v (hex)";
$time = microtime(true);
hash($v, $data, true);
$time = microtime(true) - $time;
$results[$time * 1000000000][] = "$v (raw)";
}
ksort($results);
echo PHP_EOL . PHP_EOL . 'Results: ' . PHP_EOL;
$i = 1;
foreach ($results as $k => $v)
foreach ($v as $k1 => $v1)
echo ' ' . str_pad($i++ . '.', 4, ' ', STR_PAD_LEFT) . ' ' . str_pad($v1, 30, ' ') . ($k / 1000) . ' microseconds' . PHP_EOL;
?>
</pre>
When using hash() for storing passwords, I've found that the best practice is not to use just one salt, but two. This example uses my preferred algorithm for passwords, whirlpool.
This way, even if someone can produce a rainbow table for your algorithm, and has the salt to do it with, they will never know where in the hash parameter the second salt was placed, because they don't know the length of the actual password.
<?php
function doubleSalt($toHash,$username){
$password = str_split($toHash,(strlen($toHash)/2)+1);
var_dump($password);
$hash = hash('md5', $username.$password[0].'centerSalt'.$password[1]);
return $hash;
}
?>
The str_split operation needs a +1 appended, otherwise the last character is cut off when the password is an odd number of characters, so that pass1 and pass2 will return the same value.
[NOTE BY danbrown AT php DOT net: Bug spotted by (Sunil) on 13-Mar-2009 and fix applied by OP.]
Just a quick note about these benchmarks and how you should apply them.
If you are hashing passwords etc for security, speed is not your friend. You should use the slowest method.
Slow to hash means slow to crack and will hopefully make generating things like rainbow tables more trouble than it's worth.
Note that some of the hash algorithms provided are considered weak (e.g., md5, sha1 and haval128,3). If you have a choice in the matter, use ripemd160, whirlpool or tiger192,4.
Another comprehensive benchmark script that orders results from best to worst and includes the crc32(), md5() and sha1() standalone functions:
<?php
define('HASH_TIMES', 1000);
define('HASH_DATA', 'The quick brown fox jumped over!'); // 32 bytes
header('Content-Type: text/plain');
echo 'Testing ' . strlen(HASH_DATA) . ' bytes of data over ' . HASH_TIMES . " iterations:\n";
foreach (hash_algos() as $algo) {
$time = microtime(1);
for ($i = 0; $i < HASH_TIMES; $i++) hash($algo, HASH_DATA);
$results[$algo] = microtime(1) - $time;
}
$time = microtime(1); for ($i = 0; $i < HASH_TIMES; $i++) crc32(HASH_DATA); $results['crc32()'] = microtime(1) - $time;
$time = microtime(1); for ($i = 0; $i < HASH_TIMES; $i++) md5(HASH_DATA); $results['md5()'] = microtime(1) - $time;
$time = microtime(1); for ($i = 0; $i < HASH_TIMES; $i++) sha1(HASH_DATA); $results['sha1()'] = microtime(1) - $time;
asort($results, SORT_NUMERIC);
foreach ($results as $algo => $time) echo "\n$time\t$algo";
?>
A upgrade of (dani88elx AT gmail DOT com)'s performance test [from 26-Jun-2007], this time grading them by speed (fastest first) and comparing times between hex and raw data.
<?php
function testAlgos() {
$algos = hash_algos();
$word="This will be crypted by all different algoritms";
$results = array();
foreach($algos as $algo)
{
$time=microtime(1);
$data = hash($algo, $word, false);
$results["".(microtime(1)-$time)][] = "$algo (hex)";
}
foreach($algos as $algo)
{
$time=microtime(1);
$data = hash($algo, $word, true);
$results["".(microtime(1)-$time)][] = "$algo (raw)";
}
ksort($results);
foreach($results as $time => &$algos) {
echo $time."\n";
sort($algos);
foreach($algos as $algo)
echo "\t".$algo."\n";
}
}
testAlgos();
?>
<?php
$time=microtime(1);
for ($i=0;$i<100000;$i++)
hash('md5', 'string');
echo microtime(1)-$time,': hash/md5<br>';
$time=microtime(1);
for ($i=0;$i<100000;$i++)
md5('string');
echo microtime(1)-$time,': md5<br>';
$time=microtime(1);
for ($i=0;$i<100000;$i++)
hash('sha1', 'string');
echo microtime(1)-$time,': hash/sha1<br>';
$time=microtime(1);
for ($i=0;$i<100000;$i++)
sha1('string');
echo microtime(1)-$time,': sha1<br>';
?>------------------------<br><?php
$time=microtime(1);
for ($i=0;$i<100000;$i++)
hash('md5', $i);
echo microtime(1)-$time,': hash/md5<br>';
$time=microtime(1);
for ($i=0;$i<100000;$i++)
md5($i);
echo microtime(1)-$time,': md5<br>';
$time=microtime(1);
for ($i=0;$i<100000;$i++)
hash('sha1', $i);
echo microtime(1)-$time,': hash/sha1<br>';
$time=microtime(1);
for ($i=0;$i<100000;$i++)
sha1($i);
echo microtime(1)-$time,': sha1<br>';
?>
Gives:
0.33311605453491: hash/md5
1.0671429634094: md5
0.383131980896: hash/sha1
1.3252220153809: sha1
------------------------
0.37684988975525: hash/md5
1.1258299350739: md5
0.43960785865784: hash/sha1
1.3876020908356: sha1