data types and standard library
Hashing
A hash turns input into a fixed-size fingerprint. In PHP projects, hashes are used for checksums, cache keys, webhook signatures, password storage, duplicate detection, and integrity checks.
Different hashing jobs need different tools. A checksum is not password storage. A plain hash is not encryption. A hash proves that the same input produced the same fingerprint; it does not let you recover the original input.
Create a checksum
Use hash() when you need a deterministic fingerprint for ordinary data.
<?php
declare(strict_types=1);
$message = 'download contents';
$checksum = hash('sha256', $message);
echo strlen($checksum) . PHP_EOL;
echo ctype_xdigit($checksum) ? 'hex checksum' : 'invalid checksum';
echo PHP_EOL;
// Prints:
// 64
// hex checksum
SHA-256 produces a 64-character hexadecimal string. This is useful for file integrity, cache keys, and detecting whether content changed.
Compare hashes safely
Use hash_equals() when comparing hashes, tokens, or signatures supplied from outside your code.
<?php
declare(strict_types=1);
$expected = hash('sha256', 'download contents');
$actual = hash('sha256', 'download contents');
if (!hash_equals($expected, $actual)) {
throw new RuntimeException('Checksum mismatch.');
}
echo 'Checksum matched' . PHP_EOL;
// Prints:
// Checksum matched
hash_equals() avoids timing differences that can leak information during sensitive comparisons.
Hash files without loading them fully
Use hash_file() for files.
<?php
declare(strict_types=1);
$path = tempnam(sys_get_temp_dir(), 'hash_');
file_put_contents($path, "example file\n");
$checksum = hash_file('sha256', $path);
echo strlen($checksum) . PHP_EOL;
unlink($path);
// Prints:
// 64
This is the shape you might use when verifying a downloaded archive or checking whether an uploaded file changed.
Passwords need password hashing
Do not store passwords with hash('sha256', $password). Use password_hash() and password_verify().
<?php
declare(strict_types=1);
$password = 'correct horse battery staple';
$hash = password_hash($password, PASSWORD_DEFAULT);
echo password_verify($password, $hash) ? 'password accepted' : 'password rejected';
echo PHP_EOL;
// Prints:
// password accepted
PASSWORD_DEFAULT chooses PHP's current recommended password algorithm. The output includes the algorithm and salt information needed for verification.
Rehash old passwords when needed
As PHP and your security policy change, existing password hashes may need upgrading after a successful login.
<?php
declare(strict_types=1);
$hash = password_hash('secret-password', PASSWORD_DEFAULT);
echo password_needs_rehash($hash, PASSWORD_DEFAULT) ? 'rehash needed' : 'hash is current';
echo PHP_EOL;
// Prints:
// hash is current
The usual flow is: verify the submitted password, check password_needs_rehash(), then save a new hash if needed.
Sign messages with HMAC
When you need to prove a message came from someone who knows a shared secret, use hash_hmac().
<?php
declare(strict_types=1);
$payload = '{"event":"order.paid","id":101}';
$secret = 'shared-webhook-secret';
$signature = hash_hmac('sha256', $payload, $secret);
$expected = hash_hmac('sha256', $payload, $secret);
echo hash_equals($expected, $signature) ? 'signature accepted' : 'signature rejected';
echo PHP_EOL;
// Prints:
// signature accepted
This pattern appears in webhook handling. The payload must be exactly the same bytes that were signed, so do not decode and re-encode JSON before verifying the signature.
What to remember
Use plain hashes for fingerprints and checksums, password_hash() for passwords, hash_hmac() for signed messages, and hash_equals() for sensitive comparisons. Hashing does not encrypt data, and choosing the wrong kind of hash is a real security bug.
Practice
Task: Verify a webhook signature
Write a small webhook signature verifier.
Requirements
- Use
declare(strict_types=1);. - Accept a raw payload string, a received signature, and a shared secret.
- Calculate the expected signature with
hash_hmac('sha256', ...). - Compare signatures with
hash_equals(). - Print the result for one accepted payload.
- Print the result for one rejected signature.
- Include the expected output as comments in the same PHP code block.
The verifier should use the raw payload string, not a decoded and re-encoded version of the JSON.
Show solution
<?php
declare(strict_types=1);
function isValidWebhookSignature(string $payload, string $receivedSignature, string $secret): bool
{
$expectedSignature = hash_hmac('sha256', $payload, $secret);
return hash_equals($expectedSignature, $receivedSignature);
}
$payload = '{"event":"order.paid","id":101}';
$secret = 'shared-webhook-secret';
$validSignature = hash_hmac('sha256', $payload, $secret);
echo isValidWebhookSignature($payload, $validSignature, $secret) ? 'accepted' : 'rejected';
echo PHP_EOL;
echo isValidWebhookSignature($payload, 'bad-signature', $secret) ? 'accepted' : 'rejected';
echo PHP_EOL;
// Prints:
// accepted
// rejected
The verifier signs the exact raw payload string and compares the expected signature with the received one using hash_equals(). In a real controller, this check should happen before trusting the webhook data.