La Two Factor Auth (2FA) es una implementaci贸n espec铆fica en dos pasos de la Multiple Factor Auth (MFA). Para ello implementa una doble capa de autenticaci贸n que previene el acceso no autorizado a un servicio.

Niveles de seguridad

Si usamos como referencia las gu铆as de dise帽o de la NIST estadounidense (National Institute of Standards and Technology) podemos valorar el nivel de seguridad de un sistema de autenticaci贸n en tres niveles (Authenticator Assurance Level 1-3), de menor a mayor seguridad.

AAL 1

Bastar铆a con un 煤nico factor de autenticaci贸n, como un memorized secret (una contrase帽a), para tener este nivel de seguridad. Las sesiones pueden durar 30 d铆as como m谩ximo, independientemente de la actividad del usuario.

AAL 2

Para alcanzar este nivel es necesario usar dos factores de autenticaci贸n distintos, bien usando un dispositivo multi-factor o bien combinando dos factores sencillos, el caso m谩s normal. En este 煤ltimo caso, la prerrogativa es usar algo que sabe junto con algo que se tiene. En primer caso un secreto memorizado/contrase帽a y en el segundo un secreto look-up (por ejemplo una lista secuencial de contrase帽as de un s贸lo uso escritas en un papel), un dispositivo out-of-band (un dispositivo que usa un canal secundario, como podr铆a ser una app en un smartphone) o un dispositivo criptogr谩fico de un 艣olo factor (por ejemplo un USB YubiKey). Las sesiones pueden durar 12 horas como m谩ximo, independientemente de la actividad del usuario y 30 minutos si el mismo est谩 inactivo. Tras el cierre de la sesi贸n esta se puede volver a abrir con un 煤nico factor.

AAL 3

Parecido al anterior pero en este nivel s贸lo se puede emplear autenticadores basados en hardware. Las sesiones se reducen nuevamente a 12 horas/15 minutos y s贸lo se pueden reanudar usando los dos factores.

En un proceso de doble autenticaci贸n est谩ndar estar铆amos en el nivel dos, lo que nos da una primera aproximaci贸n de lo que se ha de implementar:

  • Un factor de autenticaci贸n simple o contrase帽a. Seg煤n las recomendaciones de la NIST, 8 caracteres como m铆nimo si es escogida por el propio usuario, reduci茅ndose a 6 si lo hace el sistema. Se recomienda permitir un m谩ximo m铆nimo de 64 caracteres. Se deber铆an permitir todos los caracteres ASCII, incluyendo el espacio, as铆 como copia/pegar la contrase帽a. No se deber铆a permitir el almacenamiento de pistas de autenticaci贸n (芦驴C贸mo se llama tu mascota?禄, p. ej.) que puedan orientar a un usuario no autenticado. Se pide la implementaci贸n de una blacklist de contrase帽as comunes que impida la elecci贸n de una clave comprometida. Se aconseja la implementaci贸n de un medidor de fuerza de la contrase帽a.

  • Sesiones con duraciones limitadas (12 horas / 30 minutos).

  • Otro factor de autenticaci贸n simple. 脡ste suele ser de la elecci贸n del usuario y ha ser algo que se tiene. Este dispositivo permitir谩 el empleo de una clave de un s贸lo uso (por lo tanto algo que no se sabe) que ser谩 el segundo factor. 脡stas se denominan OTP (one-time-password).

One-time-password

La generaci贸n de tokens se hace usando el algoritmo hom贸nimo, que deriva de una clave secreta constante y un n煤mero entero variable, un token de un n煤mero arbitrario de d铆gitos enteros, normalmente 6. Para ello primero hace falta generar y almacenar dicha clave secreta, que servir谩 como semilla criptogr谩fica. Tras ella tenemos que determinar c贸mo generar la variable. El elemento que cambia, haciendo renovar el resultado ser谩 un 铆ndice o el tiempo unix. En el primer caso hablar铆amos de un hash-based OTP (HOTP; documentado en https://tools.ietf.org/html/rfc4226), y en el segundo de un time-based OTP (TOTP; documentado en https://tools.ietf.org/html/rfc4226).

La implementaci贸n est谩ndar actualmente es el TOTP. Una funci贸n TOTP, calcula la variable que identifica la autenticaci贸n, tras lo que llama a una funci贸n HOTP. Para calcular la misma lo que hace es, calcular la diferencia entre un tiempo inicial y el tiempo actual y dividir entre un paso medido en segundos (steps en el c贸digo inferior, normalmente 30). Esto quiere decir que las claves TOTP tienen una vida est谩ndar de 30 segundos, tiempo en el cual dejan de ser v谩lidas y han de ser renovadas por el dispositivo que las genera. Cu谩nto mayor este valor, m谩s c贸modo para el usuario (que tiene menos exigencia de agilidad) pero menor seguridad, ya que amplia el tiempo posible para un ataque por fuerza bruta.

import hashlib
import hmac
import struct
import time
from django.conf import settings
from urllib.parse import quote, urlencode

def hotp(key, counter, digits=6):
    msg = struct.pack(b'>Q', counter)
    hs = hmac.new(key, msg, hashlib.sha1).digest()
    hs = list(iter(hs))

    # Truncate
    offset = hs[19] & 0x0f
    bin_code = (
        (hs[offset] & 0x7f) << 24
        | hs[offset + 1] << 16
        | hs[offset + 2] << 8
        | hs[offset + 3]
    )
    return bin_code % pow(10, digits)

def totp(key, t=None, t0=0, steps=30, digits=6):
    t = int(t or time.time())
    counter = (t - int(t0)) // int(steps)
    return hotp(key, counter, digits)

Aplicaciones m贸viles para la generaci贸n de tokens

La aplicaci贸n est谩ndar para la generaci贸n de tokens OTP es Google Authenticator aunque hay muchas distintas, varias de ellas libres, como freeOTP o andOTP.

Estas aplicaciones funcionan guardando en las mismas la clave secreta generada en el servicio a ser autenticado. Para ello estas se comparten mediante un c贸digo QR a trav茅s de la c谩mara del m贸vil, de manera que no pueden ser interceptadas ni alteradas. Una vez con el c贸digo QR generan continuamente tokens, el usuario s贸lo tiene que abrir la aplicaci贸n y copiar el 煤ltimo token generado.

El 煤nico aspecto a tener en cuenta para su implementaci贸n es la generaci贸n del c贸digo QR. 脡ste ha de representar un enlace de la forma:

otpauth://totp/Example:user@example.com?secret=JBSWY3DPEHPK3PXP&issuer=Example&algorithm=SHA1&digits=6&period=30

o

otpauth://hotp/Example:user@example.com?secret=JBSWY3DPEHPK3PXP&issuer=Example&algorithm=SHA1&digits=6&counter=1

Hay varios elementos de identificaci贸n: issuer (Maadix), accountname (user@example.com) y el secret (JBSWY3DPEHPK3PXP). El issuer sirve para desambiguar credenciales. En este caso se introduce como prefijo del accountname y como par谩metro del request. El segundo es opcional pero es recomendable para que el enlace funcione con versiones antiguas de Google Authenticator. El secret tiene que ir encodeado en Base32.

Otros par谩metros son relativos a la implementaci贸n del algoritmo (en este caso se indica que se quiere un algoritmo TOTP/HOTP, que use SHA-1 [1] para generar tokens de 6 digitos [2]). El par谩metro period es opcional e indica el step en segundos del TOTP, mientras que counter es requerido en caso de HOTP y es el 铆ndice variable que emplea el algoritmo OTP. El orden de los par谩metros GET es importante porque si el secret no es el primero la aplicaci贸n de Microsoft Authenticator podr铆a no funcionar.

[1] Google Authenticator ignora este par谩metro y usa SHA-1 por defecto.

[2] En Android y Blackberry Google Authenticator tambi茅n ignora este par谩metro y usa 6 por defecto.

Un ejempo de implementaci贸n simplificada en Python ser铆a:

from urllib.parse import quote, urlencode

def get_otpauth_url(accountname, secret, issuer=None, digits=None):
    # quote and urlencode work best with bytes, not unicode strings.
    accountname = accountname.encode('utf8')
    issuer = issuer.encode('utf8') if issuer else None

    label = quote(b': '.join([issuer, accountname]) if issuer else accountname)

    # Ensure that the secret parameter is the FIRST parameter of the URI, this
    # allows Microsoft Authenticator to work.
    query = [
        ('secret', secret),
        ('digits', digits or totp_digits())
    ]

    if issuer:
        query.append(('issuer', issuer))

    return 'otpauth://totp/%s?%s' % (label, urlencode(query))

Review de apps OTP: https://www.androidauthority.com/best-two-factor-authenticator-apps-904743/

Detalles de implementaci贸n: https://github.com/google/google-authenticator/wiki/Key-Uri-Format