Proteger seus endpoints de webhook é crítico para garantir que as solicitações de webhook venham do Gu1 e não de atores maliciosos. Este guia cobre como verificar assinaturas de webhook, implementar melhores práticas de segurança e evitar erros de segurança comuns.
Gu1 assina todas as solicitações de webhook com uma assinatura HMAC SHA-256 usando seu secret de webhook. A assinatura é enviada no header X-Webhook-Signature, permitindo que você verifique se a solicitação é autêntica.
Gu1 gera uma assinatura: Ao enviar um webhook, Gu1 cria um hash HMAC SHA-256 do corpo raw da solicitação usando seu secret de webhook
Assinatura é enviada no header: A assinatura é incluída no header X-Webhook-Signature
Seu servidor recalcula: Seu endpoint recalcula a assinatura usando o mesmo secret e corpo raw
Comparar assinaturas: Se as assinaturas corresponderem, o webhook é autêntico
Sempre verifique assinaturas de webhook em produção. Sem verificação, qualquer pessoa pode enviar webhooks falsos para seu endpoint e potencialmente comprometer seu sistema.
Crítico: Você deve verificar a assinatura usando o corpo raw da solicitação antes de ser analisado como JSON. Se você verificar contra o corpo JSON analisado (por exemplo, JSON.stringify(req.body)), a assinatura não corresponderá porque a formatação JSON pode diferir.
from flask import Flask, request, jsonifyimport hmacimport hashlibimport osimport loggingapp = Flask(__name__)@app.route('/webhooks/gu1', methods=['POST'])def gu1_webhook(): try: # Obter assinatura do header signature = request.headers.get('X-Webhook-Signature') webhook_secret = os.getenv('GU1_WEBHOOK_SECRET') # Obter corpo raw para verificação de assinatura raw_body = request.get_data(as_text=True) # Verificar assinatura if not verify_signature(raw_body, signature, webhook_secret): logging.error('Invalid webhook signature') return jsonify({'error': 'Invalid signature'}), 401 # Analisar payload payload = request.json event = payload.get('event') data = payload.get('payload') logging.info(f'Received webhook: {event}') # Processar o webhook handle_webhook(event, data) return jsonify({ 'success': True, 'message': 'Webhook received' }), 200 except Exception as e: logging.error(f'Webhook error: {e}') return jsonify({ 'success': False, 'error': str(e) }), 500def verify_signature(raw_body: str, signature: str, secret: str) -> bool: """Verificar assinatura HMAC SHA-256""" expected_signature = hmac.new( secret.encode('utf-8'), raw_body.encode('utf-8'), hashlib.sha256 ).hexdigest() return hmac.compare_digest(signature, expected_signature)def handle_webhook(event: str, data: dict): # Processar webhook baseado no tipo de evento passif __name__ == '__main__': app.run(port=3000)
Use hmac.compare_digest() em vez de == para comparar assinaturas em Python. Esta função realiza uma comparação segura contra timing que previne ataques de timing.
Um erro comum é verificar a assinatura usando o objeto JSON analisado em vez do corpo raw da solicitação. Isso sempre falhará porque a formatação JSON pode diferir.
Copy
Ask AI
// ERRADO: Verificando corpo analisadoapp.post('/webhooks', express.json(), (req, res) => { const signature = req.headers['x-webhook-signature']; // Isso NÃO funcionará - JSON.stringify pode formatar diferentemente const body = JSON.stringify(req.body); const expectedSignature = crypto .createHmac('sha256', secret) .update(body) .digest('hex'); if (signature === expectedSignature) { // Sempre falhará }});
Webhooks podem ser entregues mais de uma vez devido a problemas de rede, timeouts ou tentativas. Implemente idempotência para garantir que você processe cada webhook apenas uma vez.
// NÃO FAÇA ISSO - Sem verificação de assinaturaapp.post('/webhooks', (req, res) => { // Processando webhook sem verificação handleWebhook(req.body); res.status(200).send('OK');});
Risco: Qualquer pessoa pode enviar webhooks falsos para seu endpoint.
// NÃO FAÇA ISSO - Processando duplicadosapp.post('/webhooks', (req, res) => { // Sem verificação de duplicados await createUser(req.body.entity); res.status(200).send('OK');});
// NÃO FAÇA ISSO - Processando eventos desconhecidosapp.post('/webhooks', (req, res) => { // Aceitando qualquer tipo de evento handleAnyEvent(req.body.event, req.body.payload);});
Risco: Atacantes podem enviar tipos de eventos arbitrários.