Makrosites

Suas ideias em realidade digital!

Webhooks em PHP: validação com assinatura HMAC (exemplo completo e seguro)

Webhooks em PHP: validação com assinatura HMAC (exemplo completo e seguro)

O que é um Webhook?

Webhook é uma requisição HTTP enviada automaticamente por outro sistema quando um evento acontece.

Exemplos:

Mas existe um problema grave: qualquer pessoa pode tentar enviar requisição para seu endpoint.

Por isso você precisa validar a assinatura (HMAC).


1️⃣ Estrutura básica do endpoint webhook.php

<?php
header('Content-Type: application/json; charset=UTF-8');

$secret = "SEU_SEGREDO_SUPER_SECRETO";

$payload = file_get_contents("php://input");
$signature = $_SERVER["HTTP_X_SIGNATURE"] ?? "";

if (!$payload || !$signature) {
  http_response_code(400);
  echo json_encode(["error" => "Payload ou assinatura ausente"]);
  exit;
}
---

2️⃣ Validando assinatura HMAC

A maioria dos gateways usa:

hash_hmac('sha256', payload, secret)
<?php
$computed = hash_hmac("sha256", $payload, $secret);

if (!hash_equals($computed, $signature)) {
  http_response_code(401);
  echo json_encode(["error" => "Assinatura inválida"]);
  exit;
}

⚠️ Sempre use hash_equals() para evitar ataques de timing.

---

3️⃣ Validando timestamp (proteção contra replay attack)

Alguns provedores enviam timestamp no header. Você pode bloquear requisições antigas:

<?php
$timestamp = $_SERVER["HTTP_X_TIMESTAMP"] ?? 0;

if (abs(time() - (int)$timestamp) > 300) {
  http_response_code(403);
  echo json_encode(["error" => "Requisição expirada"]);
  exit;
}
---

4️⃣ Processando evento

<?php
$data = json_decode($payload, true);

if (!$data) {
  http_response_code(400);
  echo json_encode(["error" => "JSON inválido"]);
  exit;
}

switch ($data["event"] ?? "") {
  case "payment.approved":
    // atualiza pedido
    break;

  case "subscription.cancelled":
    // cancela plano
    break;

  default:
    // loga evento desconhecido
}
---

5️⃣ Respondendo corretamente

Webhooks devem responder rápido (200 OK).

<?php
http_response_code(200);
echo json_encode(["status" => "ok"]);
---

Boas práticas de produção


Exemplo completo final (resumido)

<?php
$secret = "SEU_SEGREDO";
$payload = file_get_contents("php://input");
$signature = $_SERVER["HTTP_X_SIGNATURE"] ?? "";

if (!hash_equals(hash_hmac("sha256", $payload, $secret), $signature)) {
  http_response_code(401);
  exit;
}

$data = json_decode($payload, true);

http_response_code(200);
echo json_encode(["status" => "ok"]);
---