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 example
<?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 example
<?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 example
<?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 example
<?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 example
<?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 example
<?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 example
<?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.