feat(whatsapp): validar firma de Meta en el webhook y restaurar el menú del módulo
- POST /webhooks exige X-Hub-Signature-256 (HMAC-SHA256 del cuerpo crudo); falla cerrado si app_secret no está configurado - nueva clave app_secret en whatsapp_system_config, editable desde Configuración Bot - menú lateral de WhatsApp visible de nuevo (Correo/IMAP sigue en Chat/Bot) Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
5e0c0367d9
commit
e3ad0a423a
@@ -45,6 +45,12 @@ class WhatsappWebhookController extends Controller
|
||||
|
||||
public function handle(Request $request)
|
||||
{
|
||||
// Solo Meta puede publicar aquí: firma HMAC-SHA256 del cuerpo crudo con el App Secret.
|
||||
// Falla cerrado — sin secreto configurado no se procesa nada.
|
||||
if (! $this->signatureValid($request)) {
|
||||
return response()->json(['status' => 'forbidden'], 403);
|
||||
}
|
||||
|
||||
$payload = $request->all();
|
||||
|
||||
// Loguear el payload entrante antes de procesar
|
||||
@@ -66,6 +72,36 @@ class WhatsappWebhookController extends Controller
|
||||
return response()->json(['status' => 'ok']);
|
||||
}
|
||||
|
||||
// ──────────────────────────────────────────────────────────
|
||||
// Verificación de la firma X-Hub-Signature-256 de Meta
|
||||
// ──────────────────────────────────────────────────────────
|
||||
|
||||
private function signatureValid(Request $request): bool
|
||||
{
|
||||
$secret = WhatsappSystemConfig::get('app_secret');
|
||||
|
||||
if ($secret === '') {
|
||||
Log::error('[WhatsApp Webhook] app_secret sin configurar; payload rechazado.');
|
||||
return false;
|
||||
}
|
||||
|
||||
$header = $request->header('X-Hub-Signature-256', '');
|
||||
|
||||
if (! str_starts_with($header, 'sha256=')) {
|
||||
Log::warning('[WhatsApp Webhook] Falta cabecera X-Hub-Signature-256.');
|
||||
return false;
|
||||
}
|
||||
|
||||
$esperada = hash_hmac('sha256', $request->getContent(), $secret);
|
||||
|
||||
if (! hash_equals($esperada, substr($header, 7))) {
|
||||
Log::warning('[WhatsApp Webhook] Firma inválida; payload rechazado.');
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// ──────────────────────────────────────────────────────────
|
||||
// Extracción y enrutamiento del payload
|
||||
// ──────────────────────────────────────────────────────────
|
||||
|
||||
@@ -14,6 +14,7 @@ class ShowConfiguracionBot extends Component
|
||||
public string $phone_number_id = '';
|
||||
public string $whatsapp_api_url = '';
|
||||
public string $webhook_verify_token = '';
|
||||
public string $app_secret = '';
|
||||
public string $bot_enabled = '1';
|
||||
public string $welcome_message = '';
|
||||
public string $default_no_match = '';
|
||||
@@ -27,6 +28,7 @@ class ShowConfiguracionBot extends Component
|
||||
'phone_number_id' => 'required|string',
|
||||
'whatsapp_api_url' => 'required|url',
|
||||
'webhook_verify_token' => 'required|string',
|
||||
'app_secret' => 'required|string',
|
||||
'welcome_message' => 'required|string|max:1000',
|
||||
'default_no_match' => 'required|string|max:500',
|
||||
'advisor_message' => 'required|string|max:500',
|
||||
@@ -36,7 +38,7 @@ class ShowConfiguracionBot extends Component
|
||||
{
|
||||
$keys = [
|
||||
'whatsapp_token', 'phone_number_id', 'whatsapp_api_url',
|
||||
'webhook_verify_token', 'bot_enabled', 'welcome_message',
|
||||
'webhook_verify_token', 'app_secret', 'bot_enabled', 'welcome_message',
|
||||
'default_no_match', 'advisor_message', 'business_hours_enabled',
|
||||
'business_hours_start', 'business_hours_end',
|
||||
];
|
||||
@@ -52,7 +54,7 @@ class ShowConfiguracionBot extends Component
|
||||
|
||||
$keys = [
|
||||
'whatsapp_token', 'phone_number_id', 'whatsapp_api_url',
|
||||
'webhook_verify_token', 'bot_enabled', 'welcome_message',
|
||||
'webhook_verify_token', 'app_secret', 'bot_enabled', 'welcome_message',
|
||||
'default_no_match', 'advisor_message', 'business_hours_enabled',
|
||||
'business_hours_start', 'business_hours_end',
|
||||
];
|
||||
|
||||
Reference in New Issue
Block a user