Información blog

Linux, tutoriales, noticias, sistemas, redes y seguridad informática, entre otras cosas.

martes, 15 de noviembre de 2016

Cómo crear nuestro propio segundo factor de autenticación Linux

Anteriormente ya he hablado con respecto al concepto del segundo factor de autenticación, en concreto con la aplicada en las conexiones SSH.  Esta medida de seguridad ha ido ganando mucha relevancia durante los últimos años, y con razón pues añade una importante capa de seguridad extra a nuestros entornos... La cuestión está en que usando únicamente un segundo factor de autenticación en las conexiones SSH, hacemos que cualquiera que se loguee a nivel local no tenga restricciones para acceder al equipo, lo que puede resultar cómodo por un lado, pero inseguro por otro... Es por eso que hoy vamos a preparar un segundo factor de autenticación para cualquier login que se realice en Linux, ya sea local o remotamente. En concreto realizaremos un segundo factor de autenticación casero programado en Bash que, en mi opinión, le puede resultar útil a más de uno.

2fa_portada

Para ello lo primero que tenemos que tener en cuenta es donde podemos "interponer" nuestro segundo factor de autenticación... Es decir en qué parte del sistema podemos aplicar nuestro programa para que tanto los logins locales como remotos se vean afectados... El lugar más apropiado sería el fichero .bashrc alojado en la carpeta "home" de cada usuario, pues sabemos con toda seguridad que cualquier inicio de sesión ejecutará dicho programa y que dicho fichero es fácilmente editable... En mi caso en particular he aplicado el segundo factor de autenticación en el fichero /home/ivan/.bashrc; fichero al que le habría añadido está línea al final:

/usr/bin/2fa.sh

Esta línea simplemente significaría que, tras realizar todas las tareas correspondientes, ejecute el script 2fa.sh, script que de momento no existe (o que no debería existir) ya que dicho fichero va a ser fabricación nuestra. Empecemos a construir nuestro script paso a paso... Lo primero que tenemos que hacer es generar un código aleatorio que podamos usar en nuestra "nueva" autenticación... Con un código numérico tendría que ser suficiente, así que vamos a comenzar nuestro script con una pequeña función que genere un número aleatorio de 6 dígitos, si bien podemos hacer que dicho número sea mayor en caso de verlo necesario. Podemos escribir nuestro script con el editor que más cómodo nos sea, vi, nano, emacs... Lo importante sería que dicho script se llamase 2fa.sh y se alojase en /usr/bin. Además dicho script tendría que ser creado como root. Nuestro comienzo sería el siguiente:

  1. #!/bin/bash
  2. #VARIABLES
  3. CLAVE=""
  4. #GENERADOR NUMERO ALEATORIO
  5. function generador_clave()
  6. {
  7.         for i in `seq 1 6`;
  8.         do
  9.                 rand=$(( $RANDOM % 10 ))
  10.                 CLAVE="${CLAVE}${rand}"
  11.         done
  12. }
  13. generador_clave

Este comienzo como podéis ver es tan simple como crear una variable "vacía" llamada CLAVE. Luego crearíamos una función que asignaría 6 dígitos aleatorios a dicha variable y por último llamaríamos a la función. Algo muy sencillo y que de momento no entraña ninguna dificultad...

Esta clave sería la que luego tendríamos que usarla para autenticarnos con lo que es importante hacérnosla llegar... Aquí podemos pensar en diferentes métodos; algunos más laboriosos que otros, tales como el uso de un router GSM que nos envíe mensajes a nuestro móvil... Pero en este caso lo que queremos es una opción sencilla y que no requiera ningún tipo de inversión o de aparato "especial", con lo que en mi caso en particular me he decantado por enviar la clave por correo electrónico. Para ello habría que usar la aplicación sendemail; aplicación que no viene instalada por defecto en el sistema con lo que en caso de no tenerla instalada habría que instalarla vía consola mediante el comando:

apt-get install sendemail

Con esta aplicación instalada, antes de usarla en nuestro script es importante entender su funcionamiento... Esta aplicación lo que hace es que nos conectemos a una cuenta de correo mediante el protocolo SMTP, para después desde dicha cuenta de correo enviar una mail a un destinatario; acción para lo cual se usaría la siguiente sintaxis:

sendemail -f mail_origen -t mail_destino -xu "mail_origen" -xp "contraseña" -m "Contenido_mensaje" -s servidor_smtp:puerto -o tls=yes -u "Título_mail" 

Aquí lo único "especial" sería el servidor SMTP y el puerto a usar, ya que ambos datos variarían dependiendo de si el servidor es de un dominio u otro. Los correos más populares serían gmail y hotmail así que he aquí a continuación la lista de servidores SMTP de dichos servicios de correo.

Hotmail

Servidor: smtp.live.com
Puerto: 25

Gmail

Servidor: smtp.gmail.com
Puerto: 465

A sabiendas del comando y de la información de los servidores SMTP, podemos ahora darle más forma a nuestro script y darle la funcionalidad de enviarnos por correo la clave previamente generada, haciendo que tome el siguiente aspecto:

  1. #!/bin/bash
  2. #VARIABLES
  3. CLAVE=""
  4. #ACTIVACION TRAP
  5. trap '' 2 3 9 15
  6. #GENERADOR NUMERO ALEATORIO
  7. function generador_clave()
  8. {
  9.         for i in `seq 1 6`;
  10.         do
  11.                 rand=$(( $RANDOM % 10 ))
  12.                 CLAVE="${CLAVE}${rand}"
  13.         done
  14. }
  15. #ENVIADOR CORREO
  16. function enviador_mail()
  17. {
  18.     sendemail -f usuario@gmail.com -t destino@hotmail.com -xu "usuario@gmail.com" -xp "Contraseña" -m "${CLAVE}" -s smtp.gmail.com:465 -o tls=yes -u "2FA" > /dev/null
  19. }
  20. #LLAMADA FUNCIONES
  21. generador_clave
  22. enviador_mail

Con lo que tenemos ahora ya estaríamos mandando una clave aleatoria de 6 dígitos desde un correo de origen a un destino... Lo cual esta muy bien y se va acercando a lo que buscamos, pero que todavía no se trata de lo que buscamos, pues no se hace nada con dicha clave...

Ahora llegaría el turno de hacer que el usuario pueda introducir una clave manualmente y que ésta sea comparada con la clave que hemos enviado por correo. Aquí podemos optar por diferentes métodos de introducción de contraseña dependiendo de cómo nos logueemos; no es lo mismo loguearnos por consola que vía entorno gráfico, con lo que tendremos que hacer un script que se pueda adaptar a la situación dependiendo de nuestras necesidades. Para este script en concreto haremos que se pueda introducir la clave tanto por consola, como en entornos gráficos Gnome y KDE. Eso significaría que usaríamos 3 métodos de introducción de datos distintos:

  • Usaríamos el comando read para leer el dato introducido desde la consola. El comando sería read PASS (PASS el nombre de la variable que usaríamos para almacenar la contraseña).
  • Para la introducción de la contraseña mediante una interfaz gráfica, primero habría que ver si el entorno gráfico es gnome o KDE. En caso de ser KDE habría que usar la herramienta kdialog que usaría la sintaxis:
PASS=$(kdialog --title "PASSWORD" --inputbox "Introduce la contraseña")
  • Mientras que en Gnome la herramienta sería zenity, y la sintaxis sería esta:
PASS=$(zenity --entry --title "PASSWORD" --text "Introduce la contraseña")

Con estas introducciones haríamos que la variable PASS tuviese el valor introducido; variable que compararíamos con la clave. Obviamente haríamos que estas introducciones apareciesen constantemente hasta que la clave introducida coincida con la clave.

Con lo que recién acabamos de comentar, el script tomaría el siguiente aspecto, el cual como podéis ver, es mucho más completo. Un script que con lo que aparece a continuación ya sería funcional:

  1. #!/bin/bash
  2. #VARIABLES
  3. CLAVE=""
  4. PASS=""
  5. #GENERADOR NUMERO ALEATORIO
  6. function generador_clave()
  7. {
  8.         for i in `seq 1 6`;
  9.         do
  10.                 rand=$(( $RANDOM % 10 ))
  11.                 CLAVE="${CLAVE}${rand}"
  12.         done
  13. }
  14. #ENVIADOR CORREO
  15. function enviador_mail()
  16. {
  17.     sendemail -f usuario@gmail.com -t destino@hotmail.com -xu "usuario@gmail.com" -xp "Contraseña" -m "${CLAVE}" -s smtp.gmail.com:465 -o tls=yes -u "2FA" > /dev/null
  18. }
  19. #LLAMADA FUNCIONES
  20. generador_clave
  21. enviador_mail
  22. #DIALOGS PARA ENTORNO GRAFICO
  23. if [[ "$(env |grep DISPLAY)" = "DISPLAY=:0" ]]then
  24.     while [ "${CLAVE}" !"${PASS}" ]do
  25.         if [[ "$(env |grep XDG_CURRENT_DESKTOP)" = "KDE" ]]then #DIALOG PARA KDE
  26.             PASS=$(kdialog --title "PASSWORD" --inputbox "Introduce la contraseña")
  27.         else #DIALOG PARA GNOME
  28.             PASS=$(zenity --entry --title="PASSWORD" --text="Introduce la contraseña")
  29.         fi
  30.     done
  31. #TEXTO PLANO PARA CONSOLA
  32. else
  33.     while [ "${CLAVE}" !"${PASS}" ]do
  34.         echo 'Introduce la contraseña'
  35.         read PASS
  36.     done
  37. fi

Gracias a este script compararíamos la contraseña introducida (ya sea por consola o por interfaz gráfica) y haríamos que no dejase de pedir la contraseña hasta que sea la correcta... Es decir que el script en sí cumpliría su función, si bien todavía habría que mejorarlo un poco más, pues tiene pequeñas carencias...

La primera de ellas sería que cada vez que un usuario se loguee para lo que sea, pedirán el segundo factor de autenticación, aún cuando sea el mismo y simplemente haya abierto otra consola... Lo suyo sería que durante el tiempo que el equipo estuviese encendido, no volviese a pedir el segundo factor de autenticación, pues sino a la mínima tendríamos al sistema pidiéndonos una nueva clave de forma innecesaria, pues se da por hecho que si has pasado exitosamente la primera vez por dicho método, no hay necesidad de repetir dicha autenticación. Para ello simplemente habría que crear un fichero en el directorio temporal /tmp (que sabemos que vacía su contenido al reiniciar el equipo) cuya existencia se comprobaría dentro del fichero .bashrc para así saber si ejecutar el script o no. En mi caso he denominado a estos ficheros temporales token_${UID}, donde $UID sería el identificador del usuario. Por defecto dicho fichero no existiría, con lo que la primera vez siempre habría que crear dicho fichero mediante el script. Es por eso que en el fichero .bashrc haríamos que el script se ejecutase si el fichero NO existiese, lo cual ser haría modificando la última línea del dicho fichero para que tenga este aspecto:

  1. if [ ! -f /tmp/token_${UID}] ; then
  2.     /usr/bin/2fa.sh
  3. fi

Además, en el estado actual el script es bastante vulnerable; un simple ctrl + c en un login por consola lo detendría y haría que se saltasen este paso sin problemas... Es por ello que además del cambio atrás mencionado vamos a "atrapar" algunas señales para evitar esta situación mediante el comando trap; en concreto las señales 2 3 9 y 15. Obviamente, al finalizar el script deshabilitaríamos dicho "atrape".

Ambas modificaciones, tanto la evasión de la repetición, como la captura de ciertas señales harían que el script 2fa.sh tomase este aspecto:

  1. #!/bin/bash
  2. #VARIABLES
  3. CLAVE=""
  4. PASS=""
  5. #ACTIVACION TRAP
  6. trap '' 2 3 9 15
  7. #GENERADOR NUMERO ALEATORIO
  8. function generador_clave()
  9. {
  10.         for i in `seq 1 6`;
  11.         do
  12.                 rand=$(( $RANDOM % 10 ))
  13.                 CLAVE="${CLAVE}${rand}"
  14.         done
  15. }
  16. #ENVIADOR CORREO
  17. function enviador_mail()
  18. {
  19.     sendemail -f usuario@gmail.com -t destino@hotmail.com -xu "usuario@gmail.com" -xp "Contraseña" -m "${CLAVE}" -s smtp.gmail.com:465 -o tls=yes -u "2FA" > /dev/null
  20. }
  21. #LLAMADA FUNCIONES
  22. generador_clave
  23. enviador_mail
  24. #DIALOGS PARA ENTORNO GRAFICO
  25. if [[ "$(env |grep DISPLAY)" = "DISPLAY=:0" ]]then
  26.     while [ "${CLAVE}" !"${PASS}" ]do
  27.         if [[ "$(env |grep XDG_CURRENT_DESKTOP)" = "KDE" ]]then #DIALOG PARA KDE
  28.             PASS=$(kdialog --title "PASSWORD" --inputbox "Introduce la contraseña")
  29.         else #DIALOG PARA GNOME
  30.             PASS=$(zenity --entry --title="PASSWORD" --text="Introduce la contraseña")
  31.         fi
  32.     done
  33. #TEXTO PLANO PARA CONSOLA
  34. else
  35.     while [ "${CLAVE}" !"${PASS}" ]do
  36.         echo 'Introduce la contraseña'
  37.         read PASS
  38.     done
  39. fi
  40. #EVITAR REPETICION 2FA PARA UN MISMO USUARIO HASTA REINICIO
  41. if [ "${CLAVE}" = "${PASS}" ]then
  42.     touch /tmp/token_${UID}
  43.     chmod 400 /tmp/token_${UID}
  44. fi
  45. #DESACTIVACION TRAP
  46. trap - 2 3 9 15

Ahora sí que tendríamos un script que no solo es funcional, sino que también puede usarse en situaciones reales... Un script que añadiría una capa de seguridad adicional a nuestro sistema y que únicamente requiere añadir la llamada al script /usr/bin/2fa.sh en el fichero .bashrc de cada usuario que queremos que tenga doble seguridad. Solo quedaría por hacer una cosa más en nuestro script, que sería opcional y dependería de si tenemos varios usuarios usando el mismo equipo (con diferentes cuentas de usuario, se entiende). Este script requiere especificar a mano todos los datos de mail, el origen, el destino, usuario, contraseña... Al ser un binario que va a ser usado por todos los usuarios, tendríamos que ver si nos merece la pena o no poner o no diferentes configuraciones de correo dependiendo del usuario...

En caso de querer tener flexibilidad con dicho dato, el cual es bastante personal y que no queremos que el resto de usuarios vea... Podemos hacer que cada usuario tenga una configuración especial y que en caso de que no haya configuración especial alguna para dicho usuario, se use un correo genérico. Esto se podría lograr gracias a la privacidad que nos ofrece la carpeta home. Cada usuario tiene una carpeta /home/nombre_usuario a la que solamente pueden acceder ellos y root con lo que podemos crear un fichero con un nombre y una estructura determinada que sirva para extraer los datos de allí y usarlos. Por ejemplo podríamos crear un fichero llamado llamado User2fa dentro de nuestro directorio home (por ejemplo dentro de /home/ivan en mi caso) que tuviese la estructura:

  1. DE: usuario@gmail.com
  2. A: destino@hotmail.com
  3. USUARIO: "usuario@gmail.com"
  4. PWD: "Contraseña"
  5. SERVIDOR: smtp.gmail.com
  6. PUERTO: 465

Con estos datos, simplemente habría que recorrer el fichero y recoger los valores para luego ponerlos en la posición indicada del script... Algo tan sencillo como editar la función enviador_mail y hacer que el aspecto final sea este:

  1. #!/bin/bash
  2. #VARIABLES
  3. CLAVE=""
  4. PASS=""
  5. #ACTIVACION TRAP
  6. trap '' 2 3 9 15
  7. #GENERADOR NUMERO ALEATORIO
  8. function generador_clave()
  9. {
  10.         for i in `seq 1 6`;
  11.         do
  12.                 rand=$(( $RANDOM % 10 ))
  13.                 CLAVE="${CLAVE}${rand}"
  14.         done
  15. }
  16. #ENVIADOR CORREO
  17. function enviador_mail()
  18. {
  19.     # SI EXISTE EL fichero User2fa, usamos los datos de este
  20.     if [ -f ~/User2fa ]then
  21.         i=1
  22.         while [ ${i} -le 6 ]do
  23.             VAR[${i}]="$(head -n ${i} ~/User2fa |tail -n -1 |awk '{print $2}')"
  24.             i=$(($i + 1))
  25.         done
  26.         sendemail -f ${VAR[1]} -t ${VAR[2]} -xu "${VAR[3]}" -xp "${VAR[4]}" -m "${CLAVE}" -s ${VAR[5]}:${VAR[6]} -o tls=yes -u "2FA" >/dev/null
  27.     else # EN CASO CONTRARIO, USAMOS UNOS DATOS GENERICOS
  28.         sendemail -f usuario@gmail.com -t destino@hotmail.com -xu "usuario@gmail.com" -xp "Contraseña" -m "${CLAVE}" -s smtp.gmail.com:465 -otls=yes -u "2FA" > /dev/null
  29.     fi
  30. }
  31. #LLAMADA FUNCIONES
  32. generador_clave
  33. enviador_mail
  34. #DIALOGS PARA ENTORNO GRAFICO
  35. if [[ "$(env |grep DISPLAY)" = "DISPLAY=:0" ]]then
  36.     while [ "${CLAVE}" !"${PASS}" ]do
  37.         if [[ "$(env |grep XDG_CURRENT_DESKTOP)" = "KDE" ]]then #DIALOG PARA KDE
  38.             PASS=$(kdialog --title "PASSWORD" --inputbox "Introduce la contraseña")
  39.         else #DIALOG PARA GNOME
  40.             PASS=$(zenity --entry --title="PASSWORD" --text="Introduce la contraseña")
  41.         fi
  42.     done
  43. #TEXTO PLANO PARA CONSOLA
  44. else
  45.     while [ "${CLAVE}" !"${PASS}" ]do
  46.         echo 'Introduce la contraseña'
  47.         read PASS
  48.     done
  49. fi
  50. #EVITAR REPETICION 2FA PARA UN MISMO USUARIO HASTA REINICIO
  51. if [ "${CLAVE}" = "${PASS}" ]then
  52.     touch /tmp/token_${UID}
  53.     chmod 400 /tmp/token_${UID}
  54. fi
  55. #DESACTIVACION TRAP
  56. trap - 2 3 9 15

Gracias a estos cambios finales realizados no solo tendríamos un segundo factor de autenticación sino que además tendríamos unos datos de mail adaptados a las necesidades de cada usuario, ofreciendo la flexibilidad de que cada usuario que use el segundo factor de autenticación pueda escoger el mail de origen y de destino a su completo gusto.

Espero que os haya resultado útil.

Saludos.

No hay comentarios :

Publicar un comentario