One time passwords con OPIE
Continuando con mis posts sobre seguridad, tema que me apasiona, hoy voy a explicar como implantar un sistema de One-Time-Password en nuestros servidores Linux.
Como se puede sobreentender de su nombre, los sistemas de One-Time-Password son sistemas de autenticación en los que cada contraseña se utiliza una única vez. Esto nos puede resultar útil en el caso de que debamos hacer login en situaciones comprometidas: usando un protocolo con el que las credenciales viajan en claro (sin cifrar) por la red, desde un equipo del que no podemos asegurar su integridad (que no tenga keyloggers instalados, por ejemplo), etc.
Aunque existen tres formas de implementar sistemas OTP, desde un punto de vista práctico existen dos tipos:
- Basados en secuencia: cada vez que usamos una contraseña esta deja de ser válida y el sistema nos pedirá la siguiente en el proximo login. Mientras no hagamos login, el sistema nos seguirá pidiendo la misma.
- Basados en relojes sincronizados: la contraseña va cambiando con el tiempo, se use o no. El principal requisito de estos sistemas es que el servidor y el generador de la contraseña deben estar sincronizados en el tiempo.
El sistema que os presento es OPIE, que está basado en secuencia, es una implementación de S/Key. Si elegí este fué por sencillez de implementación ya que todo el software necesario esta disponible desde los repositorios oficiales de Debian. Pero le he encontrado dos “problemas”: la página del proyecto no funciona (al menos mientras estoy escribiendo este post) y que no hay desarrollos nuevos desde 1998.
Las contraseñas de OPIE son independientes de las del sistema operativo, cosa necesaria ya que sino de poco nos serviría si a partir de la contraseña de OPIE se pudiera averiguar la del sistema operativo. Para funcionar, OPIE se basa en tres datos: un número de secuencia, una semilla y una passphrase (donde reside la fortaleza del sistema frente a ataques de fuerza bruta).
La forma que tiene de generar la contraseña de un sólo uso es concatenando la semilla y la passphrase, y aplicando la función de hash MD5 tantas veces como le indique el número de secuencia. El resultado se convierte en seis palabras inglesas. Todo este cálculo se realiza desde el cliente. La passphrase nunca se almacena en el servidor.
Para poder validar la contraseña, el servidor necesita almacenar la última contraseña que se usó y el número de secuencia y semilla para podérselo indicar al usuario al hacer login (para los curiosos, esta información se almacena en /etc/opiekeys
, que es un fichero que sólo puede leer el usuario root). El sistema dará por buena la contraseña si el MD5 de la contraseña proporcionada por el usuario coincide con la contraseña que tenía almacenada. Una vez se ha verificado la contraseña el número de secuencia se decrementa y cuando este llega a 1 es necesario reinicializar el contador con una nueva semilla (si no, las contraseñas se repetirían) Como el MD5 es una función unidireccional, es imposible encontrar la nueva contraseña a partir de la anterior.
Instalación y configuración
Cómo siempre, empezaremos instalando el software necesario. En este caso deberemos instalar paquetes tanto en el servidor como en un equipo de confianza que nos hará de cliente. Para distinguir los comandos que debemos ejecutar en uno u otro equipo, usare los prompts server:~#
y client:~#
para el servidor y el cliente respectivamente.
server:~# aptitude install libpam-opie opie-server opie-client client:~# aptitude install opie-client
Una vez instalado el software, cada usuario que quiera usar este sistema de autenticación debe inicializarlo. Hay dos formas de hacerlo, en una se supone que el acceso que tenemos al servidor es seguro y en la otra esta suposición no es necesaria. Si estamos implementando este sistema significa que la seguridad nos preocupa (algunos dirían que somos paranoicos ) y por eso sólo explicaré el segundo método, que no es mucho más complicado y si es más seguro. En este proceso hay que ejecutar comandos tanto en el servidor como en el equipo de confianza (cliente). Supongamos que el usuario username quisiera inicializarlo:
- Debería empezar por usar
opiepasswd
en el servidor.username@server:~$ opiepasswd Adding username: You need the response from an OTP generator. New secret pass phrase: otp-md5 499 se4569 Response:
- En este momento vamos a un terminal del cliente (máquina de confianza) y ejecutamos
opiekey
pasándole como parámetros los valores del número de secuencia y semilla que aparecen al lado de otp-md5. Cuando nos lo pida, le introduciremos la passphrase que queremos configurar. Recordad que cuanto más complicada sea la passphrase, mejor.client:~$ opiekey 499 se4569 Using the MD5 algorithm to compute response. Reminder: Don't use opiekey from telnet or dial-in sessions. Enter secret pass phrase: <introducimos la passphrase> WOOL ARTY FUM SNUG WARN WICK
- Volvemos a la consola que tenemos en el servidor y pegamos la última línea que obtuvimos con
opiekey
en el cliente. Lo remarcado en cursiva es lo que ya teníamos en pantalla.username@server:~$ opiepasswd Adding username: You need the response from an OTP generator. New secret pass phrase: otp-md5 499 se4569 Response: WOOL ARTY FUM SNUG WARN WICK ID username OTP key is 499 se4569 WOOL ARTY FUM SNUG WARN WICK
Con esto, el usuario username dispone de 499 contraseñas de un sólo uso.
Para hacer que el sistema empiece a usar OPIE como sistema de autenticación debemos indicárselo al PAM. La forma más sencilla es sobreescribir el fichero /etc/pam.d/common-auth
con el que podemos encontrar en /usr/share/doc/libpam-opie/examples/pam.d/common-auth
(recomiendo hacer una copia de seguridad del antiguo) en el que se indica que el primer método de autenticación seguirá siendo la contraseña del sistema operativo pero si este fallá se usará OPIE:
server:~$ cat /etc/pam.d/common-auth # # /etc/pam.d/common-auth - authentication settings common to all services # # This file is included from other service-specific PAM config files, # and should contain a list of the authentication modules that define # the central authentication scheme for use on the system # (e.g., /etc/shadow, LDAP, Kerberos, etc.). The default is to use the # traditional Unix authentication mechanisms. # auth sufficient pam_unix.so auth sufficient pam_opie.so auth required pam_deny.so
Si lo hacemos de esta forma, cualquier servicio que autentique por PAM se podrá beneficiar del sistema de One Time Password: ssh, su, etc. De nada serviría poder usar OTP para hacer login desde un equipo del que no nos fiamos y luego tecleamos la clave de root en la misma máquina. En el caso de usarlo también para obtener permisos de administrador, aconsejo usar sudo
antes que su
por la sencilla razón que para usar el primero se pide la contraseña del usuario que pide los privilegios (que ya está usando OTP) mientras que el segundo pide la contraseña de root.
En el caso de SSH debemos realizar una pequeña comprobación en la configuración del servidor de sshd (fichero /etc/ssh/sshd_config
): el parámetro ChallengeResponseAuthentication
debe estar a yes. Si no estuviera así, el sistema no le indicaría al usuario el número de secuencia y semilla al intentar hacer login.
Ejemplo de uso
Ya lo tenemos todo listo. Veamos un ejemplo de conexión SSH usando OPIE:
- Desde la máquina de dudosa seguridad nos conectamos como normalmente:
unsecure:~$ ssh username@example.com Password:
- El sistema nos pide la contraseña como siempre, ya que así lo hemos configurado en las PAM. Pero como no nos fiamos de la seguridad de esa máquina no introducimos la contraseña:
unsecure:~$ ssh username@example.com Password: <enter> otp-md5 498 se4569 ext, Response:
- Como la autenticación por contraseña a fallado, ha entrado en acción OPIE que nos indica el siguiente número de secuencia (498) y la semilla (se4569). Desde un equipo de confianza usamos
opiekey
para obtener el OTP, pasándole el secuencial y la semilla:secure:~$ opiekey 498 se4569 Using the MD5 algorithm to compute response. Reminder: Don't use opiekey from telnet or dial-in sessions. Enter secret pass phrase: <introducimos la passphrase que usamos al configurarlo> GWYN OAF HIRE ALOE CASK CERN
- Una vez tenemos la contraseña, se la indicamos al servidor y si todo ha ido bien, ya estamos dentro (lo remarcado en cursiva es lo que ya teníamos en pantalla):
unsecure:~$ ssh username@example.com Password: otp-md5 498 se4569 ext, Response: <introducimos la contraseña generada (no se ve)> Last login: Mon Apr 9 10:01:18 2008 from 192.168.0.10 username@server:~$
- Si nos intentamos conectar de nuevo, veremos como el número de secuencia se ha decrementado:
unsecure:~$ ssh username@example.com Password: <enter> otp-md5 497 se4569 ext, Response:
Generadores de claves
Me imagino que os deberéis estar preguntando que ventaja tiene este sistema si de todas formas necesitas un equipo de confianza para generar la contraseña. Es decir, ¿si tengo un portátil para ejecutar opiekey
, por que no lo uso para conectarme directamente desde él? Entre otros motivos se me ocurre que ese portátil no pueda tener conexión a Internet porque, por política de seguridad, la red en la que estás no permita que equipos externos tengan acceso al exterior. Pero yo nunca he dicho que necesitemos un PC para generar la contraseña. Existen, como mínimo, dos alternativas:
Podemos imprimir una lista de contraseñas y llevarlas bien cerca de nosotros. Para hacerlo, en el cliente de confianza ejecutaremos:
client:~$ opiekey -n 5 499 se4569 Using the MD5 algorithm to compute response. Reminder: Don't use opiekey from telnet or dial-in sessions. Enter secret pass phrase: <introducimos la passphrase que usamos al configurarlo> 495: CRAB YEAR MAO JAG BEAD KUDO 496: CLOD REED BOND COMA DELL HEFT 497: ERIC WEE WISE TOLD NINA DIVE 498: GWYN OAF HIRE ALOE CASK CERN 499: WOOL ARTY FUM SNUG WARN WICK
Donde en la opción -n se le indica el número de contraseñas que queremos que nos imprima y el 499 es el número de secuencia más alto que queremos que aparezca en la lista.
Y si no queremos llevar las contraseñas por escrito, podemos usar nuestro móvil o PDA para calcular la contraseña instalando el equivalente al opiekey
. Para hacerlo necesitaremos que el dispositivo pueda ejecutar código Java. Yo he encontrado tres aplicaciones GPL: j2me-otp, otp-j2me (sí, no son muy originales con los nombres) y otpgen. No voy a explicar como se instalan, ya que depende mucho del móvil que tengáis.
De los tres, el que más me gusta es el tercero pues es más cómodo: te permite definir cuentas en las que mantiene el seed y el número de secuencia, que se va decrementando a medida que obtienes contraseñas. Además estoy seguro de que no hace cosas raras ya que lo he auditado (lo que os dará igual si no os fiáis de mí ) y al que he hecho dos pequeñas aportaciones: el número de secuencia sólo se decrementa cuando aceptamos la contraseña que nos ha proporcionado y he añadido una opción de configuración para que se muestren o no asteriscos al escribir la passphrase.
A pesar de haber mandado el parche al desarrollador, la verdad es que me animé tanto con esta aplicación que he terminado reescribiéndola casi por completo y podéis ver el resultado en la página del proyecto.
Mantenimiento
Como a medida que vamos usando contraseñas el número de secuencia va decrementando, debemos reinicializarlo cuando este se acerque a uno. Para hacerlo usaremos las mismas herramientas que usamos para inicializarlo (opiekey y opiepasswd) y el proceso es casi el mismo. La única diferencia es que en esta ocasión opiepasswd nos solicitará la actual contraseña antes de solicitarnos la nueva:
username@server:~$ opiepasswd Updating username: You need the response from an OTP generator. Old secret pass phrase: otp-md5 495 se4569 ext Response: CRAB YEAR MAO JAG BEAD KUDO <contraseña actual obtenida con opiekey> New secret pass phrase: otp-md5 499 se1984 Response: NINA TOLD ALIA LAKE LARK THIS <contraseña nueva obtenida con opiekey> ID username OTP key is 499 se1984 NINA TOLD ALIA LAKE LARK THIS
Es muy importante no repetir nunca la misma semilla, si mantenemos la passphrase, ya que las contraseñas se calculan a partir de estos dos datos y si en alguna ocasión repitiéramos, las contraseñas que se generarían serían repetidas y ya no serían de un sólo uso.
Seguridad del sistema
Las contraseñas de este sistema son generadas mediante algoritmos de hashing, por lo que empezaremos estudiando la seguridad o robustez del sistema a partir de estos algoritmos y en concreto de la dificultad de averiguar el texto a partir del resultado de la función. Por la forma en que se generan las contraseñas, significa revisar lo dificultad de averiguar la siguiente contraseña a partir de la actual. Aunque en 1996 se descubrió una vulnerabilidad en el algoritmo MD5, que es el que usa OPIE, esta vulnerabilidad no afecta al aspecto que nos preocupa. (para los curiosos, dicha vulnerabilidad es un problema de colisiones: resulta más o menos fácil encontrar dos textos que den el mismo resultado, pero esto no significa que, dado un resultado, se pueda calcular un texto que lo genere). Por lo tanto, no debemos preocuparnos en este aspecto. La robustez del sistema dependerá de la robustez de las contraseñas generadas, que es el siguiente punto a considerar.
OPIE genera contraseñas de 64 bits de longitud a partir de los 128 bits resultantes del MD5 haciendo una XOR entre los 64 primeros bits y los 64 últimos. Consideraremos que los 64 bits son aleatorios ya que se calculan mediante funciones de hashing. Eso significa que estas contraseñas tienen una robustez de 64 bits. Para poder comparar, según la Wikipedia, una contraseña de la misma longitud (ocho carácteres) generada por un usuario tiene la robustez equivalente de entre 18 y 30 bits. Por lo tanto, en este aspecto es mejor que las contraseñas que usamos habitualmente: distributed.net necesitó 4 años, 9 meses y 23 días para romper una clave de 64 bits mientras que, también según la Wikipedia, sólo se necesitarían 16 minutos para averiguar una contraseña creara por un usuario.
Por lo tanto, parece que el sistema es seguro. Si véis algun error en mi razonamiento, por favor, indicádmelo mediante los comentarios.
Por otro lado, ya que estamos hablando de seguridad y si habéis llegado hasta aquí posiblemente signifique que os interesa poderos conectar por SSH a un servidor vuestro desde una red potencialmente insegura, dejadme que os dé un consejo: llevad con vosotros, y a buen recaudo, el figerprint de la clave de vuestro servidor y verificad que coincida con el que se os indica al iniciar la conexión por primera vez. De esta manera os aseguraréis que no estáis siendo sujetos de un ataque man-in-the-middle. Puede que muchos de vosotros ya lo estéis haciendo, pero seguro que hay otros muchos que no.
Conclusiones
Aunque el post es bastante largo (a veces pienso que me enrollo demasiado) este sistema es bastante sencillo de implementar, la seguridad que ofrece es adecuada y, si lo configuráis como os he indicado, no tiene impacto en el día a día ya que podéis usar vuestras contraseñas de siempre cuando trabajáis desde entornos seguros, pero dispondréis de esta alternativa en casos donde la seguridad no esté tan clara. Además, ¿sabéis la cara que pone la gente cuando usas el móvil para calcular una contraseña al intentar hacer login en un sistema?
Si tenéis algo que decir sobre el post o si encontráis alguna burrada (cosa posible ya que dada la longitud del post lo he escrito en varias tandas), no dudéis en usar los comentarios.