https://d226lax1qjow5r.cloudfront.net/blog/blogposts/build-a-google-authenticator-from-a-landline-with-vonage/Social_Google-Authenticator_1200x627.png

Crea un "Autenticador de Google" desde una línea fija con Vonage

Publicado el May 7, 2021

Tiempo de lectura: 22 minutos

Nos encanta la autenticación de dos factores (2FA): es mucho más seguro iniciar sesión con una contraseña memorizada y una contraseña de un solo uso generada por el teléfono móvil. Sin embargo, el problema de una buena seguridad es que hace que sea muy fácil bloquearse.

Imagínese que está fuera de casa, de vacaciones o en viaje de negocios a una ciudad lejana. Para eso está diseñado el sistema 2FA: para que pueda utilizar el ordenador compartido del vestíbulo del hotel sin temer a los keyloggers. Puede que los piratas informáticos consigan tu contraseña, pero no podrán acceder a tus cosas sin el teléfono en la mano¡!

An example image of the authenticator

Pero, ¿qué ocurre si no tiene su móvil? ¿Si lo pierdes, se estropea o te lo roban? Bueno, para empezar tendrás que llamar a tu aseguradora... pero los datos de contacto están en tu teléfono y en el correo electrónico como copia de seguridad... ¡y no puedes acceder a tu correo electrónico sin tu teléfono ya que estás usando 2FA! 😱

¿Y si pudieras acceder a tu contraseña 2FA como un servicio remoto? Podrías marcar un número, introducir tu PIN y que te leyera los dígitos.

Antecedentes tecnológicos

Aunque las primeras implementaciones eran propietarias, existe un estándar abierto de la Initiative for Open Authentication (OATH) para contraseñas de un solo uso basadas en tiempo (TOTP) especificado en RFC 6238. Toma un secreto precompartido y el número de ticks de 30 segundos desde 1970-01-01 00:00:00 UTC, calcula un hash criptográfico y lo transforma en un número de seis dígitos que no puede predecirse sin conocer el secreto.

Sin embargo, lo que hace que esto sea práctico de utilizar es un esquema URI definido por Google. Estos se parecen a: otpauth://totp/Example:totp@example.com?secret=KVKFKRCPNZQUYMLXOVYDSQKJKZDTSRLD&issuer=Example

Lo más habitual es que estos URI se codifiquen como un código QR, como éste:

QR Code example for authentication.

Requisitos previos

Para este ejemplo necesitarás algunas cosas:

  • Autenticador de Google (Android/iOSo cualquier aplicación similar de OATH TOTP)

  • Un servicio compatible con 2FA (como una cuenta de Google)

  • Un ngrok Account

  • A Heroku Account

  • ngrok instalado localmente (he utilizado 2.3.35)

  • node.js instalado localmente (he utilizado la versión 12.6.1)

Vonage API Account

To complete this tutorial, you will need a Vonage API account. If you don’t have one already, you can sign up today and start building with free credit. Once you have an account, you can find your API Key and API Secret at the top of the Vonage API Dashboard.

This tutorial also uses a virtual phone number. To purchase one, go to Numbers > Buy Numbers and search for one that meets your needs.

Aplicaciones

Vamos a usar node.js para este ejemplo porque hace realmente fácil ejecutar un servicio web simple. También hay una implementación bastante buena de la lógica de contraseña de un solo uso que necesitamos: otplib.

En primer lugar, tenemos que instalar las librerías necesarias, lo que hacemos con npm.

npm install express body-parser otplib

Ahora ponga el siguiente código en un archivo llamado totp.js. Empezaremos con el PIN de acceso y el secreto compartido, que es el mismo que el del código QR anterior, e introduciremos otplib.

const totp = require('otplib');

// Hardcoded secrets
const secret = 'KVKFKRCPNZQUYMLXOVYDSQKJKZDTSRLD';
const pin = '1234';

Construiremos un sencillo node.js Express que utiliza la Voice API de Vonage para responder la llamada y devolver un objeto de control de llamadas Nexmo (NCCO) que le indica a la API que utilice texto a voz para pronunciar una frase y luego espere la entrada del usuario a través del teclado del teléfono. Este es un sistema de Respuesta de Voz Interactiva (IVR) muy simple, pero no vamos a entrar en cómo funciona la Voice API aquí. Si desea entender lo que este código está haciendo en mayor profundidad, eche un vistazo a este tutorial.

const app = require('express')()
const bodyParser = require('body-parser')

app.use(bodyParser.json())

const onInboundCall = (request, response) => {
  const ncco = [
    {
      action: 'talk',
      text: 'Please enter your PIN, followed by a hash.'
    },
    {
      action: 'input',
      eventUrl: [`${request.protocol}://${request.get('host')}/webhooks/passcode`]
    }
  ]

  response.json(ncco)
}

Ahora viene la parte crítica: comprobar el PIN introducido por el usuario. Si es correcto, obtendremos el código de acceso actual y se lo leeremos.

const onInput = (request, response) => {
  const dtmf = request.body.dtmf;
  if (dtmf === pin) {
    const token = totp.authenticator.generate(secret);
    const slow = token.split('').join('. ');

    const ncco = [{
      action: 'talk',
      text: `Passcode is ${slow}. Repeat. ${slow}.`
    }]

    response.json(ncco)
  } else {
    const ncco = [{
      action: 'talk',
      text: `Incorrect PIN. Goodbye.`
    }]

    response.json(ncco)
  }
}

Por último, conectamos las funciones que acabamos de definir a métodos/rutas HTTP e iniciamos la aplicación escuchando en port 3000.

const PORT = 3000

app
  .get('/webhooks/answer', onInboundCall)
  .post('/webhooks/passcode', onInput)

app.listen(PORT)

Ya está. Ahora puedes ejecutar esto con node totp.js y probarlo yendo a http://localhost:3000/webhooks/answer en tu navegador-¡devolverá el objeto JSON de la función onInboundCall que acabamos de definir.

Implantación local con ngrok

Bueno, está bien que algo funcione, pero no sirve de mucho si sólo podemos acceder a ello en nuestra máquina local.

En ngrok proporciona una forma maravillosa de configurar un reenviador dinámico para que nuestro pequeño servicio esté disponible en Internet. Registre en el sitio web (sólo necesita una cuenta gratuita) y ejecútelo:

ngrok authtoken

Eso guardará las credenciales de tu Account localmente, y luego puedes configurar el reenvío de HTTP al puerto 3000 (el que usamos arriba) ejecutando:

ngrok http 3000

An image example of Ngrok running

Tome nota de los URI que muestra, ya que serán específicos de su servicio y cambiarán cada vez que se ejecute ngrok.

De nuevo, podemos probar esto con nuestro navegador yendo a: https://bd934ed5.ngrok.io/webhooks/answer

Nota: Tendrá que utilizar el URI que proporciona ngrok.

ngrok también nos ofrece HTTPS gratuito, por lo que nuestra conexión será segura.

Configuración de API de Vonage

Sigue sin hacer nada, sin embargo. Esta parte es donde se pone bueno.

En primer lugar, tenemos que crear una nueva aplicación Voice API. Ve a la pestaña "Tus aplicaciones" del panel de API de Vonage y haz clic en el botón grande "Crear una nueva aplicación":

An example of the Vonage Dashboard

Introduzca un nombre como "TOTP Passcode Example", y genere un par de claves para que pueda tener acceso a la API:

An example of generating a keypair to have API access

A continuación, active la función "Voice" y establezca los URI de evento (https://bd934ed5.ngrok.io/webhooks/passcode) y respuesta (https://bd934ed5.ngrok.io/webhooks/answer) como los de ngrok que ha anotado más arriba. Asegúrate de utilizar tus propios URIs de ngrok. Ten en cuenta que el URI de evento utiliza HTTP POST, por lo que tendrás que seleccionarlo en el menú desplegable:

Choosing the capabilities and setting webhoosk for your Vonage Application

Alternativamente, si prefieres usar una CLI, puedes crear una aplicación como esta.

Instala la CLI de Vonage globalmente con este comando:

npm install @vonage/cli -g

Luego, configura la CLI con tu clave y secreto de API de Vonage. Puedes encontrar esta información en el Panel del desarrollador.

vonage config:set --apiKey=VONAGE_API_KEY --apiSecret=VONAGE_API_SECRET

Cree un nuevo directorio para su proyecto y CD en él:

mkdir my_project
CD my_project

Ahora, utiliza la CLI para crear una aplicación de Vonage.

vonage apps:create ✔ Application Name … theoretical_felidae ✔ Select App Capabilities › Voice ✔ Create voice webhooks? … yes ✔ Answer Webhook - URL … https://bd934ed5.ngrok.io/webhooks/answer ✔ Answer Webhook - Method › GET ✔ Event Webhook - URL … https://bd934ed5.ngrok.io/webhooks/passcode ✔ Event Webhook - Method › POST ✔ Allow use of data for AI training? Read data collection disclosure - https://help.nexmo.com/hc/en-us/articles/4401914566036 … yes

Para poder marcar este servicio, necesitamos un número externo. Crea uno utilizando "Numbers" -> "Buy Numbers" desde el panel de control:

Screenshot showing example of buying a number in Vonage Dashboard

De nuevo, para los aficionados a la CLI, existe la opción de buscar primero números con capacidad de voz (por ejemplo, en EE.UU.):

vonage numbers:search US

Entonces compra uno de la lista:

vonage numbers:buy [NUMBER] [COUNTRYCODE]

Por último, tenemos que vincular este número a la aplicación. Si vas a "Mis solicitudes" en el panel de control y haces clic en tu solicitud TOTP, deberías ver una línea en la parte inferior para tu número y un botón "Vincular" debajo de "Gestionar":

Screenshot showing how to link a number to your application

Haga clic en "Enlazar" y confirme si es necesario:

Screenshot showing confirmation of linking number to application

No temas, ejército CLI, también hay un comando para esto:

vonage apps:link APPLICATION_ID --number=YOUR_VONAGE_NUMBER

También hay comandos útiles si no puede recordar el número de teléfono (vonage numbers) o el UUID de la aplicación (vonage apps).

Ahora tenemos nuestro servicio asociado a un número de teléfono. Si llamamos a ese número, se activará el endpoint /webhooks/answer endpoint y el NCCO devuelto activará la conversión de texto a voz para pronunciar la frase "Por favor, introduzca su PIN, seguido de una almohadilla" y luego esperará a que se introduzca desde el teclado del teléfono. Introduzca "1234#" en el teclado y se enviará al punto final. /webhooks/passcode que leerá en voz alta el código de seis dígitos.

Escanea el código QR que aparece en la parte superior de esta entrada del blog en Google Authenticator y verás que los códigos coinciden a la perfección.

Despliegue global con Heroku

Está bien que lo hayamos puesto en marcha, pero no es muy cómodo que funcione en tu máquina: se detendrá si la apagas y, si estás de viaje, es poco probable que te dejes un ordenador encendido en casa. Así que será mejor tenerlo funcionando en algún lugar de Internet.

Heroku es bueno para esto, ya que permite desplegar y alojar servicios web de forma sencilla. Una Account gratuita es suficiente, así que ve y regístrate y configúrala (con Git y la CLI de Heroku). Esperaré.

Cambios en el código

Heroku asignará un puerto aleatorio para que la aplicación escuche, así que no podemos usar 3000. Tenemos que hacer este cambio de una línea a totp.js para recogerlo del entorno:

const PORT = process.env.PORT || 3000

Es necesario realizar otro cambio en el archivo package.json para que el comando npm start ya que, de lo contrario, Heroku no sabrá cómo iniciar nuestra aplicación. Simplemente añade el comando node totp.js que usamos localmente al elemento scripts. Todo el archivo package.json debería verse así:

{
  "name": "totp",
  "version": "1.0.0",
  "description": "One Time Password by phone",
  "main": "totp.js",
  "dependencies": {
    "body-parser": "^1.19.0",
    "express": "^4.17.1",
    "nexmo": "^2.6.0",
    "otplib": "^12.0.1"
  },
  "devDependencies": {},
  "scripts": {
    "start": "node totp.js",
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "Russ Williams",
  "license": "ISC"
}

Configuración de Git Source Control

Necesitamos usar Git para desplegar en Heroku, además es obviamente una buena práctica tener un control de código fuente para cualquier desarrollo. Sólo nos preocuparemos de un repositorio local por ahora, pero GitHub es gratuito y mantendrá tu código seguro mientras lo pone a tu disposición desde cualquier parte del mundo.

En primer lugar, cree un repositorio:

git init

A continuación, añade los archivos fuente que necesitaremos:

git add totp.js package.json private.key

Por último, tenemos que confirmar estos cambios:

git commit -m"Initial release"

Despliegue en Heroku

En primer lugar, tenemos que crear una nueva aplicación en Heroku:

heroku create

Esto dará a su nueva aplicación un nombre generado aleatoriamente, algo como lit-spire-15244. El comando heroku create crea un repositorio remoto (por ejemplo https://git.heroku.com/lit-spire-15244.git) al que podemos enviar código.

Para desplegar la aplicación, tenemos que utilizar Git de nuevo para empujar a ese repositorio remoto:

git push heroku master

Eso empujará el código a Heroku, y desencadenará un despliegue. Se ejecutará internamente npm start e informará de un estado como https://lit-spire-15244.herokuapp.com/ deployed to Heroku. Si utiliza ese URI, https://lit-spire-15244.herokuapp.com/webhooks/answeren mi caso, deberías ver que el servicio se está ejecutando. De nuevo, tenemos HTTPS gratis, lo cual es agradable.

Si tienes problemas, o simplemente quieres ver qué está pasando, puedes ver los registros del servicio con el comando:

heroku logs --tail

Actualizar la aplicación de Vonage

Por último, debemos actualizar el Panel de API de Vonage. Ve a la pestaña "Tus aplicaciones", elige la aplicación TOTP, haz clic en "Editar" y cambia las URI:

Screenshot showing update webhooks

Próximos pasos

¿Cómo obtengo el secreto de mis Account?

Desgraciadamente, Google Authenticator y la mayoría de aplicaciones similares no lo permiten. Una vez que han escaneado un código QR, no proporcionan una forma de recuperar el secreto.

En el caso de las cuentas de G-Suite/Google (por ejemplo, Gmail), ofrecen la opción "Cambiar teléfono" en la página de configuración de 2FA de página de configuración de 2FAque te permite obtener un nuevo código QR. Para la mayoría de los demás servicios, tendrás que desactivar y volver a activar 2FA, o pedir al administrador del sistema que lo haga por ti, básicamente el mismo proceso que cambiar tu teléfono móvil.

Hacer una captura de pantalla, así como escanear el código QR en la aplicación, te permitirá extraer el secreto y utilizarlo con esta aplicación.

¿Cómo obtengo el secreto de un código QR?

Utiliza una aplicación genérica de escáner de códigos QR para obtener el URI: hay muchas disponibles para todos los teléfonos, e incluso puede que haya una integrada en la aplicación de la cámara de tu teléfono. También puedes utilizar un sitio web como https://webqr.com/ para escanear una foto.

El secreto que necesita es la secuencia de letras mayúsculas entre "secret=" y el "&" del URI: otpauth://totp/Example:totp@example.com?secret=KVKFKRCPNZQUYMLXOVYDSQKJKZDTSRLD&issuer=Example

¿Adónde podemos llevar esto?

Hay muchas formas de desarrollar esta aplicación de ejemplo:

  • Requerir secretos codificados no es óptimo y sería mucho mejor recuperarlos de una base de datos del servidor.

  • No hay protección contra el forzamiento de su PIN si alguien está dispuesto a hacer 10.000 llamadas a esta aplicación, por lo que algún tipo de limitación de la tasa sería útil para implementar

  • Es un desperdicio tener un número de teléfono para un solo secreto, así que podríamos crear un sistema de menús IVR ("Pulse 1 para el código de acceso de Gmail, Pulse 2 para...").

  • Los códigos de acceso son sensibles al tiempo, así que si sólo faltan un par de segundos para que cambie el código, probablemente deberíamos utilizar la función de texto a voz para decirlo y esperar al siguiente, en lugar de leer en voz alta un código obsoleto. Es mejor prolongar la llamada unos segundos que tener que volver a marcar e intentarlo de nuevo.

  • Extraer manualmente los secretos de los códigos QR es un asco y existen módulos node.js (p.ej. qrcode-reader) que pueden hacerlo por nosotros. Si estuviéramos almacenando los secretos de la aplicación en una base de datos, podríamos añadir un simple front-end web para inscribirse todo el camino desde una captura de pantalla (por ejemplo esta guía de Google)

  • ... o desde el acceso a la cámara del teléfono y un poco de pegamento HTML5 para capturar una imagen (p.ej. getUserMedia) o incluso analizar el código QR (por ejemplo este tutorial o html5-qrcode) y llamar al servicio web de inscripción

Espero que esto te haya dado algunas ideas sobre cómo puedes usar las API de Vonage con la autenticación de dos factores.

Compartir:

https://a.storyblok.com/f/270183/200x267/a8435a2b98/russ-williams.png
Russ Williams

Russ has been arguing with computers since 1985, and now mostly wins the fights. He has worked in video games, defence and investment banking, and joined the Vonage team four days before the Coronavirus lockdown. He enjoys travel, poker, games of all sorts, science fiction, and is easily dist... ooh, squirrel!