Proteja a entrada do Twilio Voice com criptografia e supressão

Protect Twilio Voice Input with Encryption and Redaction
February 26, 2024
Escrito por
Revisado por
Paul Kamp
Twilion

Proteja a entrada do Twilio Voice com criptografia e supressão

Você está fazendo tudo o que pode para proteger as informações confidenciais que seus chamadores confiam a você?

À medida que as organizações aproveitam informações mais confidenciais, proteger esses dados é fundamental. A Twilio oferece inúmeras maneiras de proteger seus dados confidenciais, mas cabe a você implementar os recursos que a Twilio fornece de maneira responsável.

Neste artigo, aprenda a criptografar e editar dados coletado do Twilio Programmable Voice, usando <Gather> TwiML com Twilio Serverless Functions e Voice PCI Mode.

Itens necessários

Para seguir este tutorial, você precisará do seguinte:

O que você está construindo?

Você criará um aplicativo de voz interativo simples para lidar com a autenticação de chamadas. Uma função será usada para solicitar ao chamador as seguintes informações confidenciais via <Gather> TwiML.

  1. "Insira o seu PIN de quatro dígitos"

  2. "Insira os últimos quatro dígitos do número do seu cartão de pagamento"

Assim que esta informação for recebida do chamador, ela será criptografada. A partir desse momento, os dados permanecerão criptografados até que cheguem ao seu destino.

Em uma implementação do mundo real, o destino provavelmente seria o seu serviço de back-end para processamento. Mas aqui, outra Função atuará como uma "API fictícia" para demonstrar como a descriptografia seria realizada.

Você também ativará o Modo PCI do Voice para editar as informações coletadas nos registros de chamadas do Voice.

O antes

Antes de entrar na solução, dê uma olhada em como seus logs seriam sem criptografia ou supressão.

O Twilio Functions irá registrar qualquer erro gerado a partir de uma função no seu Twilio Debugger. Neste cenário de exemplo, você registrará um erro se determinados dígitos específicos não forem inseridos. Você pode ver os parâmetros de solicitação de texto simples no erro recebido pelo Debugger.

O Programmable Voice também registrará os dígitos coletados em texto simples no registro de chamada do Voice:

Você pode encontrar essas informações se tiver acesso aos registros de chamadas ou ao Debugger.

O depois

Os dados visíveis após a implementação desta solução são menos vulneráveis. No final, seu registro de funções mostrará valores criptografados mais seguros:

E seu registro de chamadas mostrará *EDITADO*:

Os desenvolvedores avançados devem considerar o uso do Serverless da CLI mais robusta para criar, implantar e mantar as funções.

Crie um serviço

As funções são criadas e contidas nos Services:

  1. Faça login no Console da Twilio e navegue até a guia Functions.

  2. Crie um serviço clicando no botão Criar serviço e adicionando um nome como encrypted-gather-sample.

Adicionar dependência

Nesta solução, a biblioteca axios é usada para fazer uma solicitação ao seu serviço de back-end “fingir” (a função decrypt-gather) para processamento.

Adicione axios como uma dependência ao seu serviço.

Crie uma variável de ambiente

Esta solução requer uma chave secreta, que será usada para criptografar e descriptografar os dados confidenciais.

Sua string de chave secreta deve ter pelo menos 32 bytes de comprimento. Mantenha este segredo privado.

Para criar um segredo aleatório, a seguinte linha de comando pode ser usada com Mac/Linux:

xxd -l32 -p /dev/urandom

Alternativamente, este segredo pode ser gerado pelo Node.js:

crypto.randomBytes(32).toString('hex')

 

Adicione uma variável de ambiente no seu serviço que armazena a chave.

Para fins de teste, a seguinte chave secreta de 32 bytes pode ser usada.

a154eb4c759711bc2538a7cc021e9e9f17dd8aa63151c62ca28a82a4a404203d

Criar a  função de criptografia AES

Primeiro, crie uma função para lidar com criptografia e descriptografia de dados usando criptografia de chave simétrica.

Crypto do Node.js

O Node.js oferece um módulo de criptografia integrado chamado Crypto. O Crypto fornece vários métodos úteis, como createCipheriv() e createDecipheriv(), que nos permitem especificar que tipo de algoritmo de codificação de bloco empregar.

Codificação de bloco GCM

Advanced Encryption Standard, conhecido como AES, é uma técnica para proteger dados usando algoritmos de criptografia. O AES pode ser alcançado por meio de uma variedade de modos de operações.

Nesta solução, você usará GCM, Galois/Counter Mode, uma cifra de bloco criptográfico de chave simétrica que é preferida por sua velocidade e força.

Código

Crie uma nova função chamada AES com o código a seguir.

const crypto = require("crypto")

const ALGORITHM = {
    BLOCK_CIPHER: "aes-256-gcm",
    AUTH_TAG_BYTE_SIZE: 16, 
    IV_BYTE_SIZE: 12,  
}

exports.encrypt = (plainText, key) => {
    const nonce = crypto.randomBytes(ALGORITHM.IV_BYTE_SIZE)
    const cipher = crypto.createCipheriv(
        ALGORITHM.BLOCK_CIPHER, 
        Buffer.from(key, 'hex'), 
        nonce, 
        {
            authTagLength: ALGORITHM.AUTH_TAG_BYTE_SIZE
        }
    )

    const cipherText = Buffer.concat([
        nonce,
        cipher.update(plainText),
        cipher.final(),
        cipher.getAuthTag()
    ])

    return cipherText.toString('hex')
}

exports.decrypt = (cipherText, key) => {
    cipherText = Buffer.from(cipherText, 'hex')

    const authTag = cipherText.slice(-16)
    const nonce = cipherText.slice(0, 12)
    const encryptedMessage = cipherText.slice(12, -16)

    const decipher = crypto.createDecipheriv(
        ALGORITHM.BLOCK_CIPHER, 
        Buffer.from(key), 
        nonce, 
        {
            authTagLength: ALGORITHM.AUTH_TAG_BYTE_SIZE
        }
    )

    decipher.setAuthTag(authTag)
    const decrypted = decipher.update(encryptedMessage, '', 'utf8') + decipher.final('utf8')      
    return decrypted 
}

Esta função deve ser definida para uma visibilidade "Privada", uma vez que só será utilizada a partir de outra função no mesmo Serviço.

Criar função encrypted-gather

Em seguida, crie a função que executará as operações de <Gather> confidenciais. Esta função será configurada como o webhook de voz de número de telefone recebido em uma etapa posterior.

A partir desta função, os dígitos inseridos pelo chamador serão criptografados assim que recebidos e enviados em seu estado criptografado para a função final, "destino".

Código

Crie uma nova função chamada encrypted-gather com o código a seguir:

const axios = require('axios')
const AES = require(Runtime.getFunctions()['AES'].path)

exports.handler = async function (context, event, callback) {
    const twiml = new Twilio.twiml.VoiceResponse()

    const secret_key = context.AES_SECRET

    const functionUrl = `https://${context.DOMAIN_NAME}/encrypted-gather`
    const dummyApi = `https://${context.DOMAIN_NAME}/decrypt-gather`

    const step = event.step || "getLast4CC"

    switch (step) {
        case ("getLast4CC"):
            gatherLast4Card(twiml, functionUrl);
            break
        case ("getPin"):
            let encryptedCardDigits = AES.encrypt(event.Digits, secret_key)
            gatherPin(twiml, encryptedCardDigits, functionUrl)
            break
        case ("processData"):
            let encryptedPinDigits = AES.encrypt(event.Digits, secret_key)
            await processGatheredData(twiml, event.encryptedCardDigits, encryptedPinDigits, dummyApi)
            break
    }

    return callback(null, twiml)
}

const gatherLast4Card = (twiml, functionUrl) => {
    const gather = twiml.gather({
        action: `${functionUrl}?step=getPin`,
        method: 'POST',
        input: 'dtmf',
        timeout: 10,
        numDigits: 4,
    });
    gather.say('Please enter last 4 digits of your payment card number.');

    return gather
}

const gatherPin = (twiml, encryptedCardDigits, functionUrl) => {
    const gather = twiml.gather({
        action: `${functionUrl}?step=processData&encryptedCardDigits=${encryptedCardDigits}`,
        method: 'POST',
        input: 'dtmf',
        timeout: 10,
        numDigits: 4,
    });
    gather.say('Please enter your unique 4 digit identification number');

    return gather
}

const processGatheredData = async (twiml, encryptedCardDigits, encryptedPinDigits, dummy_url) => {
    // make request to "dummy" api endpoint - example decrypt function
    try {
        const apiResponse = await axios({
            method: 'post',
            url: dummy_url,
            data: {
                encryptedCardDigits, encryptedPinDigits
            }
        })

        twiml.say(`Thank you. Your account number is ${apiResponse.data.account} and your balance is ${apiResponse.data.balance}`)
    }
    catch (e) {
        twiml.say(`We were not able to locate you in our system. Goodbye.`)
    }

    return twiml
}

Esta função deve ser definida como "Protegida", pois será chamada de dentro do Twilio e pode ser protegida com o cabeçalho X-Twilio-Signature header.

 

Ao implementar esta solução em produção, você precisará alterar a variável de descriptografia "dummyApi" para o URL do seu serviço de back-end.

const dummyApi = `https://${context.DOMAIN_NAME}/decrypt-gather`

 

Como a criptografia está sendo feita?

Na parte superior, você importa as funções criadas na etapa anterior com a seguinte linha:

const AES = require(Runtime.getFunctions()['AES'].path)

Em seguida, você define seu segredo obtendo-o da variável de ambiente:

const secret_key = context.AES_SECRET

E, o mais importante, qualquer informação confidencial é envolvida com a função criptografia. (Neste caso, as informações de <Gather> são passadas como o parâmetro Digit e podem ser acessadas a prtir do objeto de evento.)

let encryptedCardDigits = AES.encrypt(event.Digits, secret_key)

Isso lida com a criptografia das informações coletadas.

Criar a função decrypt-gather

Por fim, vamos criar uma função para demonstrar como descriptografar os dados confidenciais.

Em um ambiente de produção, isso provavelmente seria uma solicitação para o serviço de back-end que processa as informações do autor da chamada com base nas necessidades da sua empresa.

Nesta solução, uma terceira função atuará como o "serviço de back-end" que processa esses dados. Esta função receberá os dígitos criptografados e os descriptografará para processamento posterior.

Código

Crie uma nova função chamada decrypt-gather com o seguinte código:

 

const AES = require(Runtime.getFunctions()['AES'].path) 

exports.handler = function(context, event, callback) { 
const response = new Twilio.Response() 
const secret_key = context.AES_SECRET 

const last4card = AES.decrypt(event.encryptedCardDigits, secret_key) 
const pin = AES.decrypt(event.encryptedPinDigits, secret_key) 

//hard-coded values used for testing purposes 
if (last4card === "1234" && pin === "4321") { 
response.setBody(JSON.stringify({ 
account: "AC12345678", 
balance: "12.55"
 })) 
} else { 
response.setStatusCode(404) 
response.setBody("No data found") 
} 

return callback(null, response) 
}

A visibilidade desta função será "Pública", pois está fingindo ser um serviço externo.

Como a descriptografia está sendo feita?

Na parte superior, você importa funções AES novamente e define secret_key como uma variável.

Em seguida, você chama decrypt nas informações que foram criptografadas anteriormente:

 

const last4card = AES.decrypt(event.encryptedCardDigits, secret_key)

Configuração adicional

Webhook de número de telefone

Para simplificar, conecte esta função diretamente a um número de telefone.

Para configurar o número de telefone:

  1. No Console da Twilio Console, navegue até a seção Números de telefone

  2. Selecione seu número de telefone e vá até a seção Voice & Fax

  3. Defina a função encrypted-gather à medida que uma chamada é recebida no webhook em Configuração do Voice

  4. Salvar as alterações

Se você espera acionar isso no Twilio Studio, confira esta postagem do blog para saber mais sobre como incorporar essa solução de forma segura ao Studio.

Ativar o modo PCI

Quase pronto! Você protegeu as funções, mas ainda há mais uma área em que o Twilio retém dígitos reunidos em texto simples, Registros de chamadas de voz.

Abaixo está uma captura de tela do Console da Twilio para uma chamada de entrada com a solução criptografada <Gather> implementada. Embora o Functions tenha protegido os dados, o Voice não o fez.

 

Só há uma maneira de evitar que esses dados sejam exibidos no registro de chamadas: o Modo PCI. Ativar o Modo PCI na sua conta irá editar todos os dados capturados de qualquer operação <Gather>.

 

Ativar o Modo PCI em uma conta é uma via de mão única. Uma vez ligado, você não será capaz de desligá-lo. A supressão pode tornar a solução de problemas de voz mais desafiadora.

Se você realmente quer capturar informações confidenciais com segurança...

  1. Navegue até Configurações do Twilio Voice no Console da Twilio. (No painel de navegação esquerdo, clique em Voice > Configurações > Geral.)

  2. Clique no botão Enable PCI Mode (Ativar Modo PCI).

  3. Salve as alterações.

Faça uma chamada

Agora é a hora da verdade: é hora de fazer uma chamada de teste para o número de telefone.

A partir daqui, há dois caminhos a percorrer.

Se você inserir 1234 como os últimos 4 dígitos do seu "cartão de crédito" e 4321 como o PIN exclusivo, você ouvirá algumas informações de conta "fictícias" retornadas na chamada. Este é um exemplo de uma resposta de API bem-sucedida.

Se você inserir outros dígitos, ele se comportará como se você não fosse um usuário conhecido e retornará uma resposta 404. Este é um exemplo de uma solicitação malsucedida, que registrará um erro no Twilio Debugger.

Como sei que funcionou?

Siga o caminho sem sucesso e dê uma olhada no seu registro de erro no Console da Twilio.

Para a resposta de erro 404, você encontrará um erro 82005 do Functions com os seguintes detalhes:

 

Isso é bom. Sem a criptografia, uma resposta malsucedida teria registrado essas variáveis em texto simples. Mas agora os dados registrarão sua forma mais segura e criptografada.

Você também pode verificar seu registro de chamadas para confirmar os dígitos mostrados *EDITADO* lá também.

Isso é seguro?

Seguir este tutorial (incluindo as etapas opcionais do Modo PCI) impediria que os dados fossem registrados em texto simples em qualquer lugar do ecossistema da Twilio e impediria que qualquer pessoa na Twilio pudesse descriptografar seus dados confidenciais, tornando isso uma melhoria em relação ao padrão.

No entanto, a chave secreta usada para criptografia e descriptografia é armazenada como uma variável de ambiente no serviço, o que significa que os usuários aos quais você concede acesso ao Twilio Functions poderão extrair a chave e potencialmente realizar o esforço para descriptografar os valores.

Recomendação final

Se você estiver fazendo modificações no código de exemplo fornecido, lembre-se de que oFunctions retêm avisos e erros do console nos sistemas internos da Twilio e no Twilio Debugger por algum tempo.

Não use nenhum dos seguintes métodos de registro do console com dados confidenciais e não criptografados:

 

console.log() 
console.warn() 
console.error()

Conclusão

Nesta lição, você aprendeu como proteger os dados coletados do <Gather> TwiML com criptografia por meio de uma função sem servidor e redação por meio do modo Voice PCI.

Se você deseja receber pagamentos de seus chamadores, considere o recurso Twilio <Pay>.

Para saber mais sobre conformidade com PCI na Twilio, confira a documentação e matriz de responsabilidades.

Os usuários confiam em você para manter informações confidenciais privadas. Certifique-se de respeitar e reter essa confiança fazendo tudo o que puder para proteger os dados que você processa.

Bry Schinina é uma desenvolvedora e educadora que aprecia profundamente quando as empresas não expõem informações privadas. Ela trabalha como líder técnica e gerente técnica sênior de contas na Twilio, resolvendo problemas complexos e ajudando organizações a ter sucesso com as plataformas de engajamento digital. Você pode entrar em contato com ela pelo e-mail bschinina [arroba] twilio.com.