http://www.phparch.com \ December 2018 \ 35
Adventures in Hashing
Security Corner
Assuming there are three possi-
ble CDN domains, I would calculate
1002118588 % 3 = 1 and use the CDN
domain with index 1 when routing traf-
fic for this image (perhaps https://cdn1.
phparch.com/owasp-cover-3d-768x960.
png).
A function to abstract this operation
would look something like Listing 1.
Userland Hashes
One of the better-known hashing
algorithms in use today is the djb2
hash^5 created by Dan Bernstein. It is not
a cryptographically secure algorithm,
but it is relatively simple and very fast.
A variant of this hash is used internally
by PHP when building hash table look-
ups (i.e., associative arrays).
When using hashes in a secure
environment, it’s essential to select
a cryptographically secure hashing
algorithm. A cryptographically
secure hash will produce output
that’s indistinguishable from
random noise and secure against
mathematical analysis. While there
are many available algorithms, not
all of them will be secure against
mathematical attacks as they aren’t
sufficiently random. Simple hashes
like crc32 and djb2 are useful for
checksums and simple tests but
never rely on them in a critical situ-
ation where security is important.
They’re too easy to reverse, break, or
otherwise extract information from.
Instead, use constructs like SHA to
ensure randomness in the output of
the hash.
If your application relies on build-
ing unique identifying values, a hash
function like djb2 can be both fast and
safe—safer than abusing crc32 check-
sums, at least. Note that this hash is
implemented in userland in Listing 2
and, while still relatively fast, will be
slower than native functions exposed
by the PHP runtime.
5 the djb2 hash:
http://www.cse.yorku.ca/~oz/hash.html
While userland hashes are often a
fun exercise, coding the algorithms in
PHP sacrifices speed for that readabil-
ity. Implementing cryptographically
secure operations on your own is also
dangerous as a simple typo might still
permit the algorithm to produce seem-
ingly random output while breaking
the fundamental primitives on which
it relies.
Said another way: never implement
cryptography yourself. Limit your
hand-coding of hashing algorithms
like djb2 above to indexing operations.
When your application needs a truly
secure hashing algorithm (i.e., for
password storage or protecting other
sensitive information), be sure to use
one of the strong native functions
exposed by PHP.
Strong Hashing In PHP
PHP’s native hash function^6 supports
a wide variety of secure hashing
algorithms, among them SHA256 and
SHA512. Further, the native hash_hmac^
6 native hash function:
http://php.net/function.hash
function^7 supports many of the same
algorithms in a keyed hashing mode^8
for further security.
These functions are implemented
natively and will outperform any user-
land implementation. Also, the source^9
of the algorithm implementations is
publicly available for audit and review.
Further, the latest versions of PHP
support several strong and generic
hashing mechanisms provided by libso-
dium^10. The sodium_crypto_shorthash()
function leverages the SipHash algo-
rithm to hash non-sensitive data
quickly. Use this function in place of
things like crc32 or djb2 to build hash
tables or cache lookup keys. The sodium_
crypto_generichash() function uses
the cryptographically secure BLAKE2
algorithm and is a solid, performant
stand-in for hash() or hash_hmac()
above. Use it whenever you need a
secure, collision-resistant, fast hash.
7 hash_hmac function:
http://php.net/function.hash-hmac
8 a keyed hashing mode:
https://en.wikipedia.org/wiki/HMAC
9 the source: https://phpa.me/php-src-hash
10 provided by libsodium:
https://phpa.me/paragonie-hashing-libsodium
Listing 1
- $domain_count = 3;
- function generate_cdn_url(string $path, int $domain_count) {
- $file = basename($path);
- $index = crc32($path) % $domain_count;
- return sprintf('https://cdn%d.phparch.com/%s', $index, $file);
- }
Listing 2
- <?php
- function hash_djb2(string $str) {
- $hash = 5381;
- for ($i = 0; $i < strlen($str); $i++) {
- $hash = (($hash << 5) + $hash) + ord($str[$i]);
- }
- return ($hash & 0xFFFFFFFF);
- }