Un sistema de verificación en dos pasos, (Two Factor Authentication o 2FA) consiste en que la autenticación a un servicio se realiza mediante dos piezas de información, una que conocemos y otra que no. La pieza que conocemos es nuestra contraseña, que es susceptible de ser sustraída, mientras que la información que no conocemos es un número de identificación (PIN) aleatorio que cambia cada 30 segundos y que está vinculado con un dispositivo hardware. Esto es lo que se conoce como una contraseña de un solo uso (One Time Password u OTP). De esta forma, aunque alguien nos robe o averigüe nuestra contraseña, a no ser que también tenga acceso al dispositivo que crea los PINs, no podrá acceder al servicio con nuestra cuenta.

Hay que tener en cuenta que, dado que el PIN es de 6 dígitos, si alguien pudiera probar 1000000 (un millón) de contraseñas en 30 segundos encontraría la clave, por lo que es necesario añadir algún mecanismo extra que impida el acceso por fuerza bruta al sistema.

Cuando Google introdujo 2FA en sus aplicaciones, también creó un módulo PAM para GNU/Linux y una aplicación para el móvil que convierte nuestro móvil en el dispositivo generador de PINs.


Actualizado el 18 de marzo de 2015

Como alternativa a la aplicación móvil de Google (código disponible en github), podemos usar FreeOTP, una aplicación también libre para Android e iOS, compatible con HOTP y TOTP, que servirá perfectamente para nuestro propósito.


Google Authenticator Android

Fuente: google.com

Antes de comenzar, es necesario remarcar que este sistema es incompatible con el uso exclusivo de claves para conectarnos por SSH; se debe poder acceder introduciendo usuario y contraseña.

Requisitos previos

Para habilitar 2FA en nuestro servidor, necesitamos descargar, compilar e instalar el módulo PAM en nuestra máquina.

Instalación

Instalamos, previamente, los paquetes necesarios:

$ sudo aptitude install libpam0g-dev libpam-devperm mercurial

Descargamos el módulo PAM:

$ hg clone https://code.google.com/p/google-authenticator/

Realizando la instalación en una Ubuntu Lucid Lynx (10.04) me aparecía el siguiente error:

abort: repository [svn]https://zxing.googlecode.com/svn/trunk/ not found!

Parece ser que es porque tiene una versión de mercurial un poco vieja.

$ hg --version
Mercurial Distributed SCM (version 1.4.3)

Deberemos instalar una versión de mercurial más nueva de la que está en los repositorios de Lucid, o descargar el código en otro sitio y copiarlo. Con la versión de mercurial en Ubuntu Natty Narwhal no tuve problemas:

$ hg --version
Mercurial Distributed SCM (version 1.7.5)

Si lo hemos bajado en otra máquina, no hace falta que copiemos el repositorio entero, únicamente el directorio libpam:

user@otherhost:~$ scp -r google-authentication/libpam/ user@host:~

Una vez hecho este paso, lo compilamos en la máquina que lo queremos instalar, y lo instalamos:

$ cd libpam/
$ make

Como inciso, comentar que, ya que estaba, también lo iba a instalar en una Ubuntu Natty Narwhal, y me dio el siguiente error:

google-authenticator.o: In function `displayQRCode':
/home/karpoke/hg-read-only/google-authenticator/libpam/google-authenticator.c:154: undefined reference to `dlopen'
/home/karpoke/hg-read-only/google-authenticator/libpam/google-authenticator.c:166: undefined reference to `dlsym'
/home/karpoke/hg-read-only/google-authenticator/libpam/google-authenticator.c:168: undefined reference to `dlsym'
/home/karpoke/hg-read-only/google-authenticator/libpam/google-authenticator.c:253: undefined reference to `dlclose'
/home/karpoke/hg-read-only/google-authenticator/libpam/google-authenticator.c:156: undefined reference to `dlopen'

El problema parece ser que el Makefile no encuentra la librería libdl—la busca en /usr/lib/libdl.so—. La solución pasa por buscarla nosotros mismos y modificar dicho fichero:

$ find /usr/lib -name libdl.so
/usr/lib/i386-linux-gnu/libdl.so
$ sed -i 's|/usr/lib/libdl.so|/usr/lib/i386-linux-gnu/libdl.so|g' Makefile
$ make

Lo instalamos, por fin:

$ sudo make install
$ cp pam_google_authenticator.so /lib/security
$ cp google-authenticator /usr/local/bin

Configurar el servicio de SSH

Ahora debemos añadir el módulo recién instalado al final del fichero /etc/pam.d/sshd ^1^:

# Google 2FA
auth required pam_google_authenticator.so

Ejecutamos el siguiente comando con cada usuario con el que queremos utilizar el 2FA, lo cual nos creará una clave secreta en el directorio de usuario:

$ google-authenticator
https://www.google.com/chart?chs=200x200&chld=M|0&cht=qr&chl=otpauth://totp/user@server%3Fsecret%3DSAEP64T5VZAVWAFB
Your new secret key is: SAEP64T5VZAVWAFB
Your verification code is 376046
Your emergency scratch codes are:
  67868696
  26247332
  54815527
  54336661
  71083816

Do you want me to update your "~/.google_authenticator" file (y/n) y

Do you want to disallow multiple uses of the same authentication
token? This restricts you to one login about every 30s, but it increases
your chances to notice or even prevent man-in-the-middle attacks (y/n) y

By default, tokens are good for 30 seconds and in order to compensate for
possible time-skew between the client and the server, we allow an extra
token before and after the current time. If you experience problems with poor
time synchronization, you can increase the window from its default
size of 1:30min to about 4min. Do you want to do so (y/n) n

If the computer that you are logging into isn't hardened against brute-force
login attempts, you can enable rate-limiting for the authentication module.
By default, this limits attackers to no more than 3 login attempts every 30s.
Do you want to enable rate-limiting (y/n) y

Deberemos guardar esos códigos celosamente, ya que si perdemos el móvil, esa será la única manera de poder iniciar sesión de forma remota.

Lo siguiente es abrir en un navegador la URL que nos aparece al principio y nos aparecerá un QRCode. Utilizamos la aplicación Google Authenticator para nuestro móvil y lo escaneamos. Si ya teníamos otro generador, ahora tendremos los dos y los podremos distinguir por el nombre.

También podemos leer el código desde el terminal para ver lo que contiene:

$ wget -O qrcode.png 'https://www.google.com/chart?chs=200x200&chld=M|0&cht=qr&chl=otpauth://totp/user@server%3Fsecret%3DSAEP64T5VZAVWAFB'
$ qrdecode qrcode.png
otpauth://totp/user@server?secret=SAEP64T5VZAVWAFB

Es importante que el servidor tenga instalado un servicio de NTP para actualizar la hora de forma precisa. Si tenemos problemas con esto, deberíamos permitir un tamaño de ventana más abierto, tal como sugería google-authenticator.

También necesitaremos editar el fichero de configuración de ssh, /etc/ssh/sshd_config, para que contenga:

ChallengeResponseAuthentication yes
UsePAM yes

Si usamos las directivas AllowUsers o AllowGroups debemos acordarnos de incluir a cada usuario.

Reiniciamos el servicio de SSH pero no cerramos la conexión que tenemos abierta, sino que intentaremos conectarnos iniciando otra sesión nueva. El motivo es que si tuviéramos algún problema, nos quedaríamos sin poder acceder a la máquina. Si tenemos las conexiones por SSH compartidas, no bastará con abrir un nuevo terminal, deberemos conectarnos desde otro usuario o en otra máquina.

$ ssh user@host
Password:
Verification code:

La contraseña es la misma que teníamos y el código de verificación es el que nos aparezca en el móvil.

Listos. Configurar un sistema de verificación en dos pasos para SSH es así de sencillo. Por un lado, gracias a Google y por otro, gracias a tutoriales como el de TechRepublic.

Omitiendo 2FA

Podemos tener diferentes razones para no querer utilizar este tipo de autenticación en algunos casos. Por ejemplo, permitir la conexión desde la propia red o permitir que algunos usuarios no utilicen este sistema.

Omitiendo 2FA para accesos desde la red interna

Si sólo queremos 2FA para accesos remotos, desde fuera de la red local, y preferimos omitirlo en conexiones desde la misma LAN, deberemos hacer lo siguiente. Creamos el fichero /etc/security/access-local.conf y ponemos:

+ : ALL : 192.168.50.0/24
+ : ALL : LOCAL
- : ALL : ALL

Modificamos el fichero /etc/pam.d/sshd para añadir la siguiente línea justo antes de la que ya habíamos añadido, quedando así:

auth [success=1 default=ignore] pam_access.so accessfile=/etc/security/access-local.conf
auth required                   pam_google_authenticator.so

Ahora ya podemos reiniciar el servicio SSH, y para las conexiones locales o desde la red 192.168.50.0/24 bastará con proporcionar la contraseña.

Omitiendo 2FA para un usuario concreto

Para evitar que determinados usuarios usen este sistema de autenticación, podemos incluir una línea como la siguiente, al principio del fichero /etc/security/access-local.conf, quedando así:

+ : username : ALL
+ : ALL : 192.168.50.0/24
+ : ALL : LOCAL
- : ALL : ALL

Deberemos reiniciar el servicio SSH para que los cambios tengan efecto.

Omitiendo 2FA para un grupo concreto

En lugar de ir añadiendo usuarios al fichero /etc/security/access-local.conf, podemos incluir una nueva regla al fichero /etc/pam.d/sshd, justo antes de los cambios que habíamos acabado de añadir en este fichero, quedando así:

auth sufficient                 pam_succeed_if.so user ingroup nonotp
auth [success=1 default=ignore] pam_access.so accessfile=/etc/security/access-local.conf
auth required                   pam_google_authenticator.so

Los usuarios que pertenezcan al grupo nonotp no utilizarán este tipo de autenticación.

Deberemos reiniciar el servicio SSH para que los cambios tengan efecto.

pam_succeed_if.so

pam_succeed_if.so se emplea para provocar que la autenticación falle o sea exitosa en función de algunas características del usuario que está siendo autenticado. Se pueden realizar comprobaciones con los campos: user, uid, gid, shell, home y service. También se puede comprobar la pertenencia, o no, de un usuario a un grupo.


Actualizado el 7 de abril de 2012

La clave de un solo uso que se genera en un momento dado depende únicamente de la clave secreta inicial del usuario y del instante en que se genera dicha clave de un sólo uso. Google Authenticator se basa en el RFC 4226 (Time based One Time Password) para generar una semilla inicial de 16 dígitos en base 32 (RFC 4648). En la página anterior se enlaza a una clase en PHP creada por el autor que muestra como se puede crear la clave para un momento dado y comprobar si una clave dada es correcta permitiendo un pequeño desfase entre los relojes de referencia.

En este enlace se muestra una implementación en Python, cuyo código se puede descargar de GitHub.

El proceso de obtención de una clave es el siguiente:

  1. convertir la cadena con la clave a binario
  2. convertir la cadena con el time stamp asociado al intervalo a binario
  3. calcular el hash HMAC del time stamp (la parte entera tras divirlo entre el tiempo del intervalo, 30 segundos) usando la clave. Nos devolverá un hash SHA1 de 20 bytes
  4. en el último byte del hash devuelto se especifica el offset a partir del cual se encuentra la contraseña OTP
  5. obtenemos los bytes donde se encuentra la contraseña y los convertimos a un entero módulo 1000000

En Python:

def get_hotp_token(secret, intervals_no):
    key = base64.b32decode(secret)
    msg = struct.pack(">Q", intervals_no)
    h = hmac.new(key, msg, hashlib.sha1).digest()
    o = ord(h[19]) & 15
    h = (struct.unpack(">I", h[o:o+4])[0] & 0x7fffffff) % 1000000492246
    return h

def get_totp_token(secret):
    return get_hotp_token(secret, intervals_no=int(time.time())//30)
print get_totp_token('SAEP64T5VZAVWAFB')
492246

Un ejemplo de uso del módulo:

$ git clone https://github.com/tadeck/onetimepass
$ ipython
In [1]: import onetimepass as otp
In [2]: my_secret = 'SAEP64T5VZAVWAFB'
In [3]: otp.get_totp(my_secret)
Out[3]: 453001
In [4]: otp.valid_totp(453001, my_secret)
Out[4]: True

Actualizado el 1 de marzo de 2015

Desde Ubuntu Precise Pangolin 12.04, el paquete libpam-google-authenticator ya se encuentra en los repositorios.


1 Incluso podríamos hacer lo mismo para /etc/pam.d/gdm y utilizar 2FA para iniciar sesión en Gnome.


Entradas relacionadas


Published

Category

admin

Tags

Contacto