Como proteger URLs do webhook Twilio em Node.js

November 26, 2019
Escrito por

Como proteger URLs do webhook Twilio em Node.js

As APIs da Twilio permitem que os desenvolvedores reinventem as comunicações com recursos como chamadas telefônicas, SMS ou chatbots inteligentes programáveis. Os desenvolvedores podem criar aplicativos para interagir com seus usuários e reagir às respostas deles. Para responder a eventos como mensagens recebidas, você pode definir um URL de webhook. Um webhook é uma solicitação HTTP que a Twilio executa para descobrir qual deve ser a reação a um evento da Twilio, como um SMS recebido. Seus endpoints HTTP definidos precisam responder com uma linguagem de configuração Twilio compreensível chamada TwiML (Twilio Markup Language).

Esses endpoints podem ser hospedados em qualquer lugar, desde que estejam disponíveis publicamente e acessíveis pela infraestrutura da Twilio.

Três formas de proteger os URLs do seu webhook

Vamos supor que você tenha criado um aplicativo de classificação que permita aos usuários classificarem um evento no qual estão enviando um SMS para um determinado número. Cada SMS recebido para o seu número aciona um webhook para a sua infraestrutura que pode gravar os dados incluídos no disco.

Infelizmente, um hacker mal intencionado encontrou seu endpoint HTTP do webhook e reconhece a resposta TwiML como configuração da Twilio. O hacker agora pode fazer solicitações ao seu aplicativo, o que atrapalha o resultado da classificação do seu evento e a torna inútil.

É sempre recomendável proteger os URLs do seu webhook. Mas, como descrito nos documentos da Twilio, é crucial que você exponha dados confidenciais ou transforme conjuntos de dados existentes, como em nosso exemplo de classificação. Como você pode ter certeza de que apenas a Twilio está interagindo com seus endpoints?

Primeiro, verifique se você está usando HTTPSOs ataques man-in-the-middlesão um risco comum e, se você não estiver atendendo à configuração da Twilio em uma conexão segura, nunca terá certeza de que um terceiro não a alterou. O que você poderia fazer além disso é proteger seus endpoints com autenticação HTTP. Desta forma, os URLs do seu webhook não podem ser acessados sem uma combinação de nome de usuário/senha.

Uma terceira opção é validar se as solicitações recebidas foram enviadas pela Twilio usando o cabeçalho X-Twilio-Signature que é o núcleo deste artigo. Vamos analisar com mais detalhes.

O cabeçalho X-Twilio-Signature

Quando a Twilio envia uma solicitação para o URL do seu webhook definido, ela incluirá o cabeçalho x-twilio-signature. Este cabeçalho é uma string codificada que representa o URL da solicitação e os parâmetros da solicitação classificada. A string resultante é então assinada usando HMAC-SHA1 usando seu AuthToken como a chave. O AuthToken é crucial aqui porque seu valor só pode ser acessado pela Twilio e por você. Um terceiro não pode gerar a mesma assinatura da Twilio com hash sem ter acesso a ela.

Para validar as solicitações recebidas, você pode executar o mesmo procedimento de assinatura e comparar se a assinatura gerada e a assinatura enviada são iguais. Se isso acontecer, você pode ter certeza de que a Twilio enviou a solicitação que você recebeu.

Caso esteja usando funções sem servidor pelo Twilio Runtime, você pode ter visto a configuração de controle de acesso, que permite a validação de assinatura da Twilio para cada solicitação recebida.

Captura de tela da interface do Twilio Functions destacando a seção de controle de acesso

Validação de X-Twilio-Signature usando a Biblioteca auxiliar Twilio Node.js

Para facilitar ao máximo, a Biblioteca auxiliar Twilio Node.js não só oferece a você a funcionalidade de criar respostas TwiML ou interagir com os endpoints da API, como também inclui o recurso para validar solicitações.

Para começar, certifique-se de ter instalado a biblioteca.

npm i twilio

Depois de instalá-la, você pode usar o método validateRequest exposto ao objeto Twilio exportado. O método aceita quatro argumentos:

  • Seu AuthToken Twilio
  • O cabeçalho x-twilio-signature incluído na solicitação de entrada
  • O URL em que seu HTTP está acessível
  • Os parâmetros de solicitação incluídos

No Node.js, você pode usar o método validateRequest no Express da seguinte forma:

const twilio = require('twilio');
const express = require('express');
const bodyParser = require('body-parser');

const app = express();
const port = 8080;

app.use(bodyParser.urlencoded({ extended: true }));

app.post('/sms', (req, res) => {
  const twilioSignature = req.headers['x-twilio-signature'];
  const params = req.body;
  const url = 'https://your-webhook-endpoint.io';

  const requestIsValid = twilio.validateRequest(
    process.env.TWILIO_AUTH_TOKEN,
    twilioSignature,
    url,
    params
  );

  if (!requestIsValid) {
    return res.status(401).send('Unauthorized');
  }

  // write to a database, call other services, ...
  // then respond to the message!
  res.set({
    'Content-Type': 'text/plain'
  });
  res.send("You're allowed!");
});


app.listen(
  port,
  () => console.log(`Example app listening on port ${port}!`)
);

validateRequest é a maneira recomendada de validar webhooks de entrada. Ele funciona com qualquer estrutura disponível. Espero que você comece a proteger seus URLs de endpoints hoje mesmo. Se você também estiver usando o Express, a biblioteca da Twilio também fornece um middleware para facilitar a validação de solicitações com essa estrutura popular.

O que acontece em segundo plano

Se estiver curioso para saber como gerar a assinatura, você pode dar uma olhada nos documentos que a explicam em detalhes ou conferir o seguinte código de exemplo:

/**
 * Get a valid Twilio signature for a request 
 *
 * @param {String} authToken your Twilio AuthToken
 * @param {String} URL your webhook URL
 * @param {Object} params the included request parameters
 */
function getSignature(authToken, url, params) {
  // get all request parameters
  var data = Object.keys(params)
     // sort them
    .sort()
     // concatenate them to a string
    .reduce((acc, key) => acc + key + params[key], url);

  return crypto
     // sign the string with sha1 using your AuthToken
    .createHmac('sha1', authToken)
    .update(Buffer.from(data, 'utf-8'))
     // base64 encode it
    .digest('base64');
}

Nem todas as solicitações vêm de origens conhecidas

Espero que este artigo esclareça a questão de como você pode se certificar de que a solicitação que atinge os URLs do seu webhook é proveniente da Twilio, e não de uma pessoa que está usando a sua infraestrutura. Se você quer saber como tornar seus URLs do webhook mais seguros usando o Express, veja este tutorial. Você também pode conferir como proteger seus webhooks em Ruby ou Python. E é isso, pessoal. Protejam-se! Vejo vocês na próxima!

Este artigo foi traduzido do original "How to secure Twilio webhook URLs in Node.js". Enquanto melhoramos nossos processos de tradução, adoraríamos receber seus comentários em help@twilio.com - contribuições valiosas podem render brindes da Twilio.