Glossary

HMAC

HMAC provides evidence of message authenticity and integrity between parties sharing a secret. It neither encrypts the content nor prevents replay attacks by itself.

Short definition

Signing a message with a shared secret.

The sender and recipient know the same secret. The sender calculates an HMAC over the data specified by the protocol and sends the result in a header. The recipient recalculates the expected value from the original data and compares it with the signature. A match shows that the data was created by a holder of the same secret and did not change in transit.

HMAC is a message authentication code, not an asymmetric digital signature. Every holder of the secret can create and verify a message, so HMAC does not provide independent proof to a third party. It does not protect payload confidentiality or, without a timestamp, nonce, or ID, prevent the replay of a previously valid message.

Use cases

When an application needs to verify the origin of a message

HMAC is suitable when two parties share a secret securely and need to verify a particular request.

  • a webhook from a payment gateway, marketplace, or carrier
  • a server-to-server HTTP request within an integration flow
  • signing a callback whose protocol defines the signed headers and body precisely
  • verifying the integrity of an internal request alongside HTTPS and authentication

Practical example

Verifying a webhook signature in PHP

In this simplified example, the provider signs the timestamp.rawBody string using HMAC-SHA-256 and sends the result as hexadecimal in a header. A real endpoint must use the exact headers, separator, encoding, and rules of the provider.

The secret comes from secure configuration. After verification, the event ID is stored with a unique constraint; HMAC itself does not prevent the same valid message from being delivered again.

$rawBody = $request->getContent();
$timestamp = (string) $request->headers->get('X-Webhook-Timestamp', '');
$providedSignature = (string) $request->headers->get('X-Webhook-Signature', '');

if (!preg_match('/^\d+\z/', $timestamp) || abs(time() - (int) $timestamp) > 300) {
    throw new AccessDeniedHttpException('Neplatný webhook.');
}

// Podoba vstupu musí přesně odpovídat dokumentaci poskytovatele.
$signingInput = $timestamp . '.' . $rawBody;
$expectedSignature = hash_hmac('sha256', $signingInput, $webhookSecret);

if (!hash_equals($expectedSignature, $providedSignature)) {
    throw new AccessDeniedHttpException('Neplatný webhook.');
}

How it works

From the raw request to accepting a webhook safely

The provider defines the message format and signature encoding; they cannot be inferred safely without its documentation.

  1. Building the input The protocol specifies the raw body, timestamp, event ID, or their combination. Both parties must use the same bytes and order.
  2. Calculating the signature The sender calculates the HMAC from that input and secret. It sends the signature, for example, as hex or Base64 in a designated header.
  3. Receiving without modification The recipient first reads the raw body and relevant headers. It must not re-serialise or normalize JSON before verification.
  4. Verification and freshness It calculates the expected HMAC, compares it with a constant-time function, and verifies a timestamp, nonce, or event ID according to the contract.
  5. Durable effect Only after verification does it store the event with duplicate protection and pass the work to a queue.

Key concepts

The secret, data, and protocol must agree.

Security depends not only on the algorithm, but also on the signed data and secret management.

Shared secret

The secret is the key for both calculating and verifying the HMAC. It is neither a user password nor a public API key; it belongs in secure configuration, not in a log or repository.

Raw payload and canonicalization

The signature applies to precisely defined bytes. Even identical JSON values can differ in whitespace, key order, or escaping. The protocol defines canonicalization.

Algorithm and encoding

HMAC-SHA-256 is a common choice, but the contract defines both the algorithm and signature format. The application does not accept an algorithm or key URL from a header.

Constant-time comparison

The result is not compared with ordinary == or ===. In PHP, hash_equals() limits leakage based on the position of the first mismatch; the expected value is the first argument.

Replay and rotation

A valid signature can be replayed. A time window, event ID, and durable deduplication solve a different problem from HMAC. During rotation, the old secret must have a limited lifetime.

Benefits and limitations

Simple verification at the cost of a shared secret.

Benefits

  • integrity and sender verification without a custom PKI
  • fast calculation over precisely defined input
  • well suited to webhooks and server-to-server integrations

Risks

  • a leaked shared secret allowing valid signatures to be created
  • HMAC neither encrypting the payload nor proving which particular holder of the secret sent it
  • a mismatch in raw data, encoding, or timestamp causing rejection
  • a valid message having repeated effects without replay protection

Scope

Use it when both parties can manage the secret securely.

HMAC suits an integration contract with a known provider and a precisely documented signature. The recipient must manage the secret and its rotation securely and make rejected requests traceable. The signature complements transport protection and authorization of subsequent steps.

It is unsuitable when the recipient must verify a signature without being able to create a message itself; an asymmetric signature with a public verification key is appropriate then. HMAC is also not a password-storage mechanism, for which intentionally slow functions are used.

What to consider

Verify the data first, then its business meaning.

A secure signature is part of the intake flow, not merely a condition in a controller.

  • the algorithm, signing input, and signature format exactly as documented by the provider
  • the raw body before JSON decoding and comparison with hash_equals()
  • the secret in secure configuration, rotation, and no logging of its value
  • a time window, event ID, and database deduplication to limit replays and retries
  • a generic error for an untrusted request and tests using a modified payload

Common questions

What HMAC actually guarantees

Does HMAC encrypt the payload?

No. HMAC verifies integrity and possession of the secret. HTTPS/TLS or encryption protects the content.

Is comparing the signature with === enough?

No. PHP provides hash_equals(), which is designed for comparison resistant to timing attacks.

Why must the endpoint read the raw body?

The signature is often calculated over the original bytes. Re-encoding JSON can change the resulting HMAC.

Does HMAC prevent a replay attack?

No. A valid request can be sent again. A timestamp, nonce, or durably stored event ID adds replay protection.

Is HMAC suitable for storing passwords?

No. Passwords use adaptive hashing functions; HMAC authenticates a message with a shared key.

How I approach API and integration boundaries

I design external event intake with verification.

For webhooks and API integrations, I address verification, retries, timeouts, and business rules.

Request a call

I will call you on the next working day between 9:00 and 17:00.

You can also call me directly.

+420 605 181 728

Leave your phone number and send a callback request.

By sending, you agree to processing your data in order to handle your request.